Перенос метода шифрования с Java на Python

Я не могу получить такой же вывод из своего кода Python, в чем моя ошибка?

я не уверен, но я делаю ошибку в процессе кодирования и декодирования

from Crypto.Cipher import AES
from Crypto import Random
import base64
from hashlib import pbkdf2_hmac
import binascii
import os
import datetime, time

def pad(byte_array):
    BLOCK_SIZE = 16
    pad_len = BLOCK_SIZE - len(byte_array) % BLOCK_SIZE
    return byte_array + (bytes([pad_len]) * pad_len)

key = pbkdf2_hmac(
hash_name = 'SHA1', 
password = b"75820705-2b7a-46dc-b811-0f6ad4ff33af", 
salt = os.urandom(8), 
iterations = 100, 
dklen = 384
)

auth_key = "d4eee068-272a-4aec-9681-5e16dcef6fbd";
timedate = x = datetime.datetime.fromtimestamp(time.time()-1000*10*60)
paylaod = auth_key+"|"+x.strftime('%Y-%m-%dT%H:%M:%S.0000%f')[:-3]+"Z"


cipher = AES.new(key[:32], AES.MODE_CBC, key[32:48])
plain = pad(paylaod.encode("UTF-8"))
encrypted_text = cipher.encrypt( plain )

print (base64.b64encode(encrypted_text).decode("UTF-8"))

это рабочий метод на java

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.security.SecureRandom;
import java.util.Arrays;
import java.lang.StringBuilder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Calendar;
import java.util.*;

byte[] bArr = new byte[8];
new SecureRandom().nextBytes(bArr);
byte[] encoded = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(new PBEKeySpec(str2.toCharArray(), bArr, 100, 384)).getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(Arrays.copyOfRange(encoded, 0, 32), "AES");

Cipher instance = Cipher.getInstance("AES/CBC/PKCS5Padding");
instance.init(instance.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(Arrays.copyOfRange(encoded, 32, 48)));
byte[] doFinal = instance.doFinal(str.getBytes("UTF-8"));

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byteArrayOutputStream.write(doFinal);
byteArrayOutputStream.write(bArr);
            return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());

и это основная часть java:

public static void main(String[] args)
{       
    String auth_key = "d4eee068-272a-4aec-9681-5e16dcef6fbd";
    SimpleDateFormat var0 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'", Locale.US);
    var0.setTimeZone(TimeZone.getTimeZone("UTC"));
    String payload = auth_key+"|"+var0.format(new Date(Long.valueOf((new Date()).getTime()-1000*10*60))); //random key from /keys endpoint
    String outputVal = a(payload, "bd1676b5-5ce3-4351-a39b-36a7b7219c11"); //x-vmob-uid
    System.out.println(outputVal.replace("\n", "").replace(" ", ""));
}

Правильный вывод таков:

Wgxc7xuqdKd2CqyT2KLE6ihankSTbTS/grIj+uyGG4IgpXWFxJ+KE4En/lQnL2vEu67w0sHeT6Tu1ibV0zahqpCKjw4pGPhhuCErS/8pojzg2TSMfFh7fw==

но я получаю это:

8/VHDoMCOOI4Aaxus2nxridBPfm4Gvy2g8yRgK3VJUr3eSa3UucsAdzRMapuQj6pN3el12tqaAKYeNpFZCv5SuVosd4AYXwvmf/3uy5yr2U=

надеюсь, кто-нибудь предложит мне, где проверить или дать мне ошибку

Вы также можете добавить часть import в Java? чтобы мы могли видеть, какие библиотеки используются.

XoXo 17.05.2019 18:26

также, можете ли вы сделать pip freeze и опубликовать вывод в вопросе? чтобы мы могли видеть, какие пакеты в Python используются.

XoXo 17.05.2019 18:30

Я вижу на стороне Python, когда вы создаете ключ key = pbkdf2_hmac(..., аргумент соли является случайным salt = os.urandom(8). Затем ключ используется шифром. Итак, если вы используете разные соли, как результат может быть таким же? Кроме того, полезная нагрузка отличается, потому что она рассчитывается из time.time(), которая будет отличаться при каждом запуске (и в Java должны соблюдаться те же рассуждения). Обратите внимание, что я не знаю шифрования, но это то, что бросилось мне в глаза.

Steven Rumbalski 17.05.2019 18:33

Ваш код Java ссылается на переменные str2 и str, но ваш фрагмент кода не показывает, как они инициализируются

Palamino 17.05.2019 18:33

@StevenRumbalski в java это: byte[] bArr = new byte[8]; новый SecureRandom().nextBytes(bArr); значит генератор случайных чисел

D3vr00T 17.05.2019 18:46

@Palamino, вы можете просто выполнить код Java, скопировав код фрагмента

D3vr00T 17.05.2019 18:46

@FreeWar, когда я запускаю ваш Java-код локально, он дает разные результаты для каждого запуска. например VUnWoEYyKDphtUB7U2yS7hRLoou8Atu9f4CerOFkyI+EjpfUQ6MCqpb6NUzy‌​vMoXap6o3E6GMTv1JCSF‌​rgsfdngSM4sM9A1H8IFJ‌​e8ATTJcjf5mtCaV8mg==

XoXo 17.05.2019 18:48

Дело не в том, что вы не используете рандомизацию в обоих случаях. Дело в том, что рандомизация означает, что ваши результаты будут разными — не только между двумя программами, но и от запуска к запуску.

Steven Rumbalski 17.05.2019 18:49

да, я знаю это, но вывод отличается длиной и последними двумя символами! Когда я вызываю вывод из java, я получаю длину 120 с ==, но в python у меня есть слово длины 108 и только 1 '=' @StevenRumbalski

D3vr00T 17.05.2019 19:07

@JeffXiao вывод каждый раз разный, но это правильный вывод «длина и последние 2 символа»

D3vr00T 17.05.2019 19:08

Добавлен ответ @FreeWar

XoXo 17.05.2019 21:54
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
11
529
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

TL;DR

Причины, по которым вы видите различия в результатах Java и Python:

  1. разные значения используются в Java и Python для pbkdf2_hmac
  2. текущее время используется как часть ввода, которое меняется между запусками
  3. byteArrayOutputStream.write(bArr); вводит немного более длинную строку в вывод Java.

Я предполагаю, что 3 - это то, что вы ищете, но позвольте мне дать длинный ответ мыслительного процесса для вышеуказанных выводов.

Длинный ответ

Воспроизвести проблему

Полный исходный код Java, который работает:

package answer;

import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.Arrays;

import java.text.SimpleDateFormat;

import java.io.ByteArrayOutputStream;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.IvParameterSpec;

import java.util.Base64;

public class SO56189889 {
    public static void main(String[] args) {
        String auth_key = "d4eee068-272a-4aec-9681-5e16dcef6fbd";
        SimpleDateFormat var0 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'", Locale.US);
        var0.setTimeZone(TimeZone.getTimeZone("UTC"));

        // random key from /keys endpoint
        String formatted = var0.format(getSeedDate());

        String payload = auth_key + "|" + formatted;
        System.out.println(payload);
        String outputVal = magic(payload, "bd1676b5-5ce3-4351-a39b-36a7b7219c11"); // x-vmob-uid
        System.out.println(outputVal.length());
        System.out.println(outputVal);
    }

    public static Date getSeedDate() {
        Date now = new Date(Long.valueOf((new Date()).getTime() - 1000 * 10 * 60));
        return now;
    }

    public static String magic(String str, String str2) {
        try {
            byte[] bArr = new byte[8];
            // new SecureRandom().nextBytes(bArr);
            for (int i = 0; i < 8; i++) {
                bArr[i] = 'X';
            }
            String temp = new String(bArr);
            System.out.println(temp);

            byte[] encoded = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
                    .generateSecret(new PBEKeySpec(str2.toCharArray(), bArr, 100, 384)).getEncoded();
            SecretKeySpec secretKeySpec = new SecretKeySpec(Arrays.copyOfRange(encoded, 0, 32), "AES");

            Cipher instance = Cipher.getInstance("AES/CBC/PKCS5Padding");
            // XXX: use Cipher.ENCRYPT_MODE (was: instance.ENCRYPT_MODE)
            instance.init(Cipher.ENCRYPT_MODE, secretKeySpec,
                    new IvParameterSpec(Arrays.copyOfRange(encoded, 32, 48)));
            byte[] doFinal = instance.doFinal(str.getBytes("UTF-8"));

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byteArrayOutputStream.write(doFinal);
            byteArrayOutputStream.write(bArr);

            System.out.println("no exception, everything OK");

            return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
        } catch (Exception e) {
            System.out.println(e.toString());
            return "NOT WORKING";
        }
    }
}

Полный код Python, который работает:

from Crypto.Cipher import AES
from Crypto import Random
import base64
from hashlib import pbkdf2_hmac
import binascii
import os
import datetime, time

def pad(byte_array):
    BLOCK_SIZE = 16
    pad_len = BLOCK_SIZE - len(byte_array) % BLOCK_SIZE
    return byte_array + (bytes([pad_len]) * pad_len)

# salt = os.urandom(8)
salt = b'XXXXXXXX'
print(salt)
print('---------------')

key = pbkdf2_hmac(
hash_name = 'SHA1', 
# password = b"75820705-2b7a-46dc-b811-0f6ad4ff33af", 
password = b"bd1676b5-5ce3-4351-a39b-36a7b7219c11",
# salt = os.urandom(8), 
salt = salt,
iterations = 100, 
dklen = 384
)

auth_key = "d4eee068-272a-4aec-9681-5e16dcef6fbd"
x = datetime.datetime.fromtimestamp(time.time()-1000*10*60)
timedate = x
# payload = auth_key+"|" + x.strftime('%Y-%m-%dT%H:%M:%S.0000%f')[:-3]+"Z"
payload = "d4eee068-272a-4aec-9681-5e16dcef6fbd|1970-01-01T00:00:00.0000000Z"
print(payload)
print('-----------------')

cipher = AES.new(key[:32], AES.MODE_CBC, key[32:48])
plain = pad(payload.encode("UTF-8"))
encrypted_text = cipher.encrypt(plain)

result = base64.b64encode(encrypted_text).decode("UTF-8")
print(len(result))
print(result)

Убедитесь, что ввод Java-программы не изменился

В приведенном выше Java-коде изменение исходит из:

  • getSeedDate()

  • new SecureRandom().nextBytes(bArr); в magic()

Давайте изменим их:

public static Date getSeedDate() {
    Date seed = new Date(0L);    // 0L: the milliseconds since January 1, 1970, 00:00:00 GMT.
    return seed;
}
// new SecureRandom().nextBytes(bArr);
for (int i = 0; i < 8; i++) {
    bArr[i] = 'X';
}

Теперь вывод Java всегда одинаков: Z6iTzNaJcDVdL5Rv8psb1D+xakq4By4KUxipmVv0ASjZUfIZO3nu+an5p27BxQ+x1+qoMLgD4vEub5PWcs69FDFy4y2etgiBCiCVnOM6RFlYWFhYWFhYWA==

Убедитесь, что ввод программы Python не меняется

Измените следующее, чтобы программа Python использовала те же значения, что и программа Java.

# payload = auth_key+"|" + x.strftime('%Y-%m-%dT%H:%M:%S.0000%f')[:-3]+"Z"
payload = "d4eee068-272a-4aec-9681-5e16dcef6fbd|1970-01-01T00:00:00.0000000Z"
# salt = os.urandom(8)
salt = b'XXXXXXXX'
key = pbkdf2_hmac(
hash_name = 'SHA1', 
# password = b"75820705-2b7a-46dc-b811-0f6ad4ff33af", 
password = b"bd1676b5-5ce3-4351-a39b-36a7b7219c11",
salt = salt,
iterations = 100, 
dklen = 384
)

Обратите внимание, что password должен быть идентичен тому, что есть в Java.

Теперь программа Python всегда выводит: Z6iTzNaJcDVdL5Rv8psb1D+xakq4By4KUxipmVv0ASjZUfIZO3nu+an5p27BxQ+x1+qoMLgD4vEub5PWcs69FDFy4y2etgiBCiCVnOM6RFk=

Сравните результаты

Java outputVal.length() — 120, а Python len(result) — 108.

Давайте посмотрим их вместе:

Джава: Z6iTzNaJcDVdL5Rv8psb1D+xakq4By4KUxipmVv0ASjZUfIZO3nu+an5p27BxQ+x1+qoMLgD4vEub5PWcs69FDFy4y2etgiBCiCVnOM6RFлЫВФхЙВФхЙВА==

Питон: Z6iTzNaJcDVdL5Rv8psb1D+xakq4By4KUxipmVv0ASjZUfIZO3nu+an5p27BxQ+x1+qoMLgD4vEub5PWcs69FDFy4y2etgiBCiCVnOM6RFк=

Пробовать и ошибаться

В этот момент я заметил, что в Java у вас есть doFinal и bArr,

byteArrayOutputStream.write(doFinal);
byteArrayOutputStream.write(bArr);

В то время как в Python вы используете только plain

plain = pad(payload.encode("UTF-8"))
encrypted_text = cipher.encrypt(plain)

result = base64.b64encode(encrypted_text).decode("UTF-8")

Эксперимент показывает, что удаление byteArrayOutputStream.write(bArr); в Java генерирует точную строку, что и Python.

После мыслей

  • убедитесь, что входные данные одинаковы, прежде чем сравнивать результаты

  • дважды проверьте строки, которые вы используете

  • в некоторых случаях метод проб и ошибок может сработать

я действительно глуп, хахахаха, я не заметил "byteArrayOutputStream.write(bArr);" Просто добавил его в свой массив байтов, и все работает хорошо! Спасибо, бро

D3vr00T 17.05.2019 22:44

Другие вопросы по теме