为了账号安全,请及时绑定邮箱和手机立即绑定

使用 SecretKeyFactory 生成机密时,Java 字节数组到字符串必须等于 python

使用 SecretKeyFactory 生成机密时,Java 字节数组到字符串必须等于 python

慕桂英4014372 2023-08-08 16:39:28
我有一个任务将一些 python 加密代码重写为 java。我是Python新手。Python代码:from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMACfrom cryptography.hazmat.backends import default_backendbackend = 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,我无法更改它(即无法将其指定为 python 实现中的字符串,因为我了解服务器转换密码也转换为字节数组)。使用这个虚拟密码一切正常,即由 python 和 java 代码生成的密钥是相同的。但是当密码更改为任意密码时,我遇到了一个问题,即例如 AFFFFFFFFDBGEHTH .... 据我了解 java 字节数组表示为有符号整数的问题。即,当我将十六进制“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代码才能接收与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 回答

?
幕布斯6054654

TA贡献1876条经验 获得超7个赞

这里的密码是一个(任意)字节序列,通常与 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 https://stackoverflow.com/a/9855338

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


查看完整回答
反对 回复 2023-08-08
?
慕慕森

TA贡献1856条经验 获得超17个赞

SHA1您在 Java 代码和Python 代码中使用SHA256,这就是您得到不同结果的原因。使用PBKDF2WithHmacSHA256.



查看完整回答
反对 回复 2023-08-08
  • 2 回答
  • 0 关注
  • 137 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信