将加密方法从 Java 移植到 Python
Porting encryption method from Java to 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=
希望有人建议我在哪里检查或告诉我错误
TL;DR
您看到 Java 与 Python 结果不同的原因是:
- Java 与 Python
pbkdf2_hmac
使用了不同的值
- 当前时间用作输入的一部分,在运行之间会发生变化
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=
比较结果
JavaoutputVal.length()
是120,Pythonlen(result)
是108。
一起来看看吧:
Java:
Z6iTzNaJcDVdL5Rv8psb1D+xakq4By4KUxipmVv0ASjZUfIZO3nu+an5p27BxQ+x1+qoMLgD4vEub5PWcs69FDFy4y2etgiBCiCVnOM6RFlYWFhYWFhY==][=336][=116]
Python:
Z6iTzNaJcDVdL5Rv8psb1D+xakq4By4KUxipmVv0ASjZUfIZO3nu+an5p27BxQ+x1+qoMLgD4vEub5PWcs69FDFy4y2etgiBCiCVnOM6RFk=
试错
此时,我注意到在 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")
一项实验表明,删除 Java 中的 byteArrayOutputStream.write(bArr);
会生成与 Python 完全相同的字符串。
思前想后
在比较结果之前确保输入相同
仔细检查您使用的字符串
try-and-error在某些情况下确实可以工作
我的 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=
希望有人建议我在哪里检查或告诉我错误
TL;DR
您看到 Java 与 Python 结果不同的原因是:
- Java 与 Python
pbkdf2_hmac
使用了不同的值
- 当前时间用作输入的一部分,在运行之间会发生变化
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=
比较结果
JavaoutputVal.length()
是120,Pythonlen(result)
是108。
一起来看看吧:
Java:
Z6iTzNaJcDVdL5Rv8psb1D+xakq4By4KUxipmVv0ASjZUfIZO3nu+an5p27BxQ+x1+qoMLgD4vEub5PWcs69FDFy4y2etgiBCiCVnOM6RFlYWFhYWFhY==][=336][=116]
Python:
Z6iTzNaJcDVdL5Rv8psb1D+xakq4By4KUxipmVv0ASjZUfIZO3nu+an5p27BxQ+x1+qoMLgD4vEub5PWcs69FDFy4y2etgiBCiCVnOM6RFk= 此时,我注意到在 Java 中,您有 而在 Python 中,您只使用 一项实验表明,删除 Java 中的 在比较结果之前确保输入相同 仔细检查您使用的字符串 try-and-error在某些情况下确实可以工作试错
doFinal
和 bArr
、byteArrayOutputStream.write(doFinal);
byteArrayOutputStream.write(bArr);
plain
plain = pad(payload.encode("UTF-8"))
encrypted_text = cipher.encrypt(plain)
result = base64.b64encode(encrypted_text).decode("UTF-8")
byteArrayOutputStream.write(bArr);
会生成与 Python 完全相同的字符串。思前想后