Java byterray to string 必须等于 python bytearray string when generate secret with SecretKeyFactory
Java byterray to string must be equal python bytearray string when generate secret with SecretKeyFactory
我的任务是将一些 python 加密代码重写为 java。我是 python 的新人。 Python代码:
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
backend = default_backend()
PASSWORD = bytes((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16))
key = PBKDF2HMAC(hashes.SHA256(), 32, salt, iterations, backend).derive(PASSWORD)
我的 java 实现:
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
byte[] PASSWORD = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
SecretKey tmp = factory.generateSecret(new PBEKeySpec(new String(PASSWORD).toCharArray(), salt, iterations, 256));
byte[] key = tmp.getEncoded();
如您所见,PASSWORD 是一个字节数组,它是我从十六进制字符串中获取的,即 010203....0F10,我无法更改它(即无法将其指定为 [=40 中的字符串=] 实现,据我所知,服务器也将 PASSWORD 转换为字节数组)。使用这个虚拟密码一切正常,即由 python 和 java 代码生成的密钥是相等的。但是当密码更改为任意密码时,我遇到了一个问题,例如 AFFFFFFFFDBGEHTH....
据我了解 java 字节数组表示为有符号整数的问题。 IE。例如,当我将十六进制“FFFAAABBBCCCDDDDFFAAAAAAAAAAAABB”转换为字节数组时,它将是字节数组 [-1, -6, -86, -69, -68, -52, -35, -35, -1, -86, -86 , -86, -86, -86, -86, -69],但在 python 中它将 [255, 250, 170, 187, 188, 204, 221, 221, 255, 170, 170, 170 , 170, 170, 170, 187]。然后,当我将 java 字节数组转换为 PBEKeySpec 构造函数的 charArray - new PBEKeySpec(new String(new byte[]{-1, -6, -86, -69, -68, -52, -35, - 35, -1, -86, -86, -86, -86, -86, -86, -69}).toCharArray()... 效果出乎意料。
如何更改我的 java 代码以接收与 python 中相同的密钥?据我了解,我必须将 java 字节数组字符串编码为与 python .derive(...) 方法中相同的值。
提前致谢。
更新:
salt = b'salt'
PASSWORD = = bytes((255, 250, 170, 187, 188, 204, 221, 221, 255, 170, 170, 170, 170, 170, 170, 187))
key = PBKDF2HMAC(hashes.SHA256(), 32, salt, 512, backend).derive(PASSWORD)
和
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
password = new String(new byte[]{-1, -6, -86, -69, -68, -52, -35, -35, -1, -86, -86, -86, -86, -86, -86, -69});
var key = secretKeyFactory
.generateSecret(new PBEKeySpec(password.toCharArray(),
"salt".getBytes(), 512, 256))
.getEncoded();
应该会给出相同的结果。它适用于新的 byte[]{1,2,3,4,....16} 密码。
更新 2:我将密码更改为 unsigned int[] 但它仍然不起作用:
char[] password = new char[PASSWORD.length];
for (int i = 0; i<PASSWORD.length; password[i] = (char)(PASSWORD[i++] & 0xFF));
var key = secretKeyFactory
.generateSecret(new PBEKeySpec(password, "salt".getBytes(), 512, 256))
.getEncoded();
您在 Java 代码中使用 SHA1
而在 Python 代码中使用 SHA256
,这就是您得到不同结果的原因。使用 PBKDF2WithHmacSHA256
.
除了不同的摘要(s. 1st ), the problem is that the key derived with PBKDF2WithHmacSHA256
is an instance of PBKDF2KeyImpl
, which requires a string as password. This string is UTF8 encoded in PBKDF2KeyImpl
(see documentation of the class PBKDF2KeyImpl
)。但是这里的密码是一个(任意的)字节序列,一般不兼容UTF8,所以在UTF8解码的时候数据会被破坏。一种可能的解决方案是将 PBEKeySpec
替换为 BouncyCastle 的 PKCS5S2ParametersGenerator
,它期望密码为字节数组(在 init
中):
import java.nio.charset.StandardCharsets;
import org.bouncycastle.crypto.PBEParametersGenerator;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
...
byte[] salt = "salt".getBytes(StandardCharsets.UTF_8);
int iterations = 512;
byte[] PASSWORD = new byte[] { (byte)255, (byte)250, (byte)170, (byte)187, (byte)188, (byte)204, (byte)221, (byte)221, (byte)255, (byte)170, (byte)170, (byte)170, (byte)170, (byte)170, (byte)170, (byte)187 };
PBEParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA256Digest());
generator.init(PASSWORD, salt, iterations);
byte[] keyBytes = ((KeyParameter)generator.generateDerivedParameters(256)).getKey();
// with bytesToHex from
System.out.println(bytesToHex(keyBytes).toLowerCase()); // d8aa4772e9648572611fe6dca7f653353de934cdb3b29fab94eb13ba2b198b9f
结果现在与 Python 代码的结果匹配:
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
salt = b'salt'
iterations = 512
PASSWORD = bytes((255, 250, 170, 187, 188, 204, 221, 221, 255, 170, 170, 170, 170, 170, 170, 187))
key = PBKDF2HMAC(hashes.SHA256(), 32, salt, iterations).derive(PASSWORD)
print(key.hex()) # d8aa4772e9648572611fe6dca7f653353de934cdb3b29fab94eb13ba2b198b9f
我的任务是将一些 python 加密代码重写为 java。我是 python 的新人。 Python代码:
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
backend = default_backend()
PASSWORD = bytes((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16))
key = PBKDF2HMAC(hashes.SHA256(), 32, salt, iterations, backend).derive(PASSWORD)
我的 java 实现:
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
byte[] PASSWORD = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
SecretKey tmp = factory.generateSecret(new PBEKeySpec(new String(PASSWORD).toCharArray(), salt, iterations, 256));
byte[] key = tmp.getEncoded();
如您所见,PASSWORD 是一个字节数组,它是我从十六进制字符串中获取的,即 010203....0F10,我无法更改它(即无法将其指定为 [=40 中的字符串=] 实现,据我所知,服务器也将 PASSWORD 转换为字节数组)。使用这个虚拟密码一切正常,即由 python 和 java 代码生成的密钥是相等的。但是当密码更改为任意密码时,我遇到了一个问题,例如 AFFFFFFFFDBGEHTH.... 据我了解 java 字节数组表示为有符号整数的问题。 IE。例如,当我将十六进制“FFFAAABBBCCCDDDDFFAAAAAAAAAAAABB”转换为字节数组时,它将是字节数组 [-1, -6, -86, -69, -68, -52, -35, -35, -1, -86, -86 , -86, -86, -86, -86, -69],但在 python 中它将 [255, 250, 170, 187, 188, 204, 221, 221, 255, 170, 170, 170 , 170, 170, 170, 187]。然后,当我将 java 字节数组转换为 PBEKeySpec 构造函数的 charArray - new PBEKeySpec(new String(new byte[]{-1, -6, -86, -69, -68, -52, -35, - 35, -1, -86, -86, -86, -86, -86, -86, -69}).toCharArray()... 效果出乎意料。
如何更改我的 java 代码以接收与 python 中相同的密钥?据我了解,我必须将 java 字节数组字符串编码为与 python .derive(...) 方法中相同的值。 提前致谢。
更新:
salt = b'salt'
PASSWORD = = bytes((255, 250, 170, 187, 188, 204, 221, 221, 255, 170, 170, 170, 170, 170, 170, 187))
key = PBKDF2HMAC(hashes.SHA256(), 32, salt, 512, backend).derive(PASSWORD)
和
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
password = new String(new byte[]{-1, -6, -86, -69, -68, -52, -35, -35, -1, -86, -86, -86, -86, -86, -86, -69});
var key = secretKeyFactory
.generateSecret(new PBEKeySpec(password.toCharArray(),
"salt".getBytes(), 512, 256))
.getEncoded();
应该会给出相同的结果。它适用于新的 byte[]{1,2,3,4,....16} 密码。
更新 2:我将密码更改为 unsigned int[] 但它仍然不起作用:
char[] password = new char[PASSWORD.length];
for (int i = 0; i<PASSWORD.length; password[i] = (char)(PASSWORD[i++] & 0xFF));
var key = secretKeyFactory
.generateSecret(new PBEKeySpec(password, "salt".getBytes(), 512, 256))
.getEncoded();
您在 Java 代码中使用 SHA1
而在 Python 代码中使用 SHA256
,这就是您得到不同结果的原因。使用 PBKDF2WithHmacSHA256
.
除了不同的摘要(s. 1st PBKDF2WithHmacSHA256
is an instance of PBKDF2KeyImpl
, which requires a string as password. This string is UTF8 encoded in PBKDF2KeyImpl
(see documentation of the class PBKDF2KeyImpl
)。但是这里的密码是一个(任意的)字节序列,一般不兼容UTF8,所以在UTF8解码的时候数据会被破坏。一种可能的解决方案是将 PBEKeySpec
替换为 BouncyCastle 的 PKCS5S2ParametersGenerator
,它期望密码为字节数组(在 init
中):
import java.nio.charset.StandardCharsets;
import org.bouncycastle.crypto.PBEParametersGenerator;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
...
byte[] salt = "salt".getBytes(StandardCharsets.UTF_8);
int iterations = 512;
byte[] PASSWORD = new byte[] { (byte)255, (byte)250, (byte)170, (byte)187, (byte)188, (byte)204, (byte)221, (byte)221, (byte)255, (byte)170, (byte)170, (byte)170, (byte)170, (byte)170, (byte)170, (byte)187 };
PBEParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA256Digest());
generator.init(PASSWORD, salt, iterations);
byte[] keyBytes = ((KeyParameter)generator.generateDerivedParameters(256)).getKey();
// with bytesToHex from
System.out.println(bytesToHex(keyBytes).toLowerCase()); // d8aa4772e9648572611fe6dca7f653353de934cdb3b29fab94eb13ba2b198b9f
结果现在与 Python 代码的结果匹配:
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
salt = b'salt'
iterations = 512
PASSWORD = bytes((255, 250, 170, 187, 188, 204, 221, 221, 255, 170, 170, 170, 170, 170, 170, 187))
key = PBKDF2HMAC(hashes.SHA256(), 32, salt, iterations).derive(PASSWORD)
print(key.hex()) # d8aa4772e9648572611fe6dca7f653353de934cdb3b29fab94eb13ba2b198b9f