Портирование метода шифрования с Java на Python - PullRequest
0 голосов
/ 17 мая 2019

Я не могу получить тот же вывод из моего кода 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=

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

1 Ответ

2 голосов
/ 17 мая 2019

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.

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

Java: Z6iTzNaJcDVdL5Rv8psb1D + xakq4By4KUxipmVv0ASjZUfIZO3nu + an5p27BxQ + x1 + qoMLgD4vEub5PWcs69FDFy4y2etgiBCiCVnOM6RF lYWFhYWFhYWA ==

Python: Z6iTzNaJcDVdL5Rv8psb1D + xakq4By4KUxipmVv0ASjZUfIZO3nu + an5p27BxQ + x1 + qoMLgD4vEub5PWcs69FDFy4y2etgiBCiCVnOM6RF * +1079 * к = * * тысяча восемьдесят два Попытка и ошибка На данный момент я заметил, что в 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.

После мыслей

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

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

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

...