Java 和使用网络加密 api 的浏览器之间的 RSA 解密失败
RSA decrypt failing between Java and browser using web crypto api
我正在尝试使用 RSA public 密钥从服务器获取加密响应。密码在服务器端生成,但在客户端解码失败。 Web 加密 API 抛出 DOM 异常。
Java 服务器:
byte[] exponentBytes = Base64.getUrlDecoder().decode(body.exponent);
byte[] modulusBytes = Base64.getUrlDecoder().decode(body.modulus);
BigInteger exponent = new BigInteger(1, exponentBytes);
BigInteger modulus = new BigInteger(1, modulusBytes);
RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey publicKey = factory.generatePublic(spec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherBytes = cipher.doFinal("hello".getBytes())
return Base64.getEncoder().encodeToString(cipherBytes);
浏览器:
const key = await window.crypto.subtle.generateKey(
{
name: 'RSA-OAEP',
modulusLength: 512,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256',
},
true,
['encrypt', 'decrypt'],
)
const jwk = await window.subtle.exportKey('jwk', key.publicKey);
const response = await fetch('/foo/bar', { method: 'post', body: { exponent: jwk.e, modulus: jwk.n } });
const body = await response.text();
const binary = window.atob(body);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
await window.crypto.subtle.decrypt(
{ name: 'RSA-OAEP' },
key.privateKey,
bytes.buffer,
); // returns undefined throws the error
编辑:经过进一步研究,我发现:
- (很明显)web crypto生成的密码api解码没有问题
- 从服务器端返回字节数组本身没有帮助。
您正在使用的 Webcrypto 端
name: 'RSA-OAEP',
hash: 'SHA-256',
...
实例化算法。在 Java 方面,您“只需”用
实例化密码
Cipher cipher = Cipher.getInstance("RSA");
但那是
的同义词
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
您需要使用这些行来实例化 Webcrypto 算法:
Cipher encryptCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
OAEPParameterSpec oaepParameterSpecJCE = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParameterSpecJCE);
ciphertextByte = encryptCipher.doFinal(plaintextByte);
安全说明:512 的密钥长度是 不安全,请使用至少 2048 位的密钥长度。
我正在尝试使用 RSA public 密钥从服务器获取加密响应。密码在服务器端生成,但在客户端解码失败。 Web 加密 API 抛出 DOM 异常。
Java 服务器:
byte[] exponentBytes = Base64.getUrlDecoder().decode(body.exponent);
byte[] modulusBytes = Base64.getUrlDecoder().decode(body.modulus);
BigInteger exponent = new BigInteger(1, exponentBytes);
BigInteger modulus = new BigInteger(1, modulusBytes);
RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey publicKey = factory.generatePublic(spec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherBytes = cipher.doFinal("hello".getBytes())
return Base64.getEncoder().encodeToString(cipherBytes);
浏览器:
const key = await window.crypto.subtle.generateKey(
{
name: 'RSA-OAEP',
modulusLength: 512,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256',
},
true,
['encrypt', 'decrypt'],
)
const jwk = await window.subtle.exportKey('jwk', key.publicKey);
const response = await fetch('/foo/bar', { method: 'post', body: { exponent: jwk.e, modulus: jwk.n } });
const body = await response.text();
const binary = window.atob(body);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
await window.crypto.subtle.decrypt(
{ name: 'RSA-OAEP' },
key.privateKey,
bytes.buffer,
); // returns undefined throws the error
编辑:经过进一步研究,我发现:
- (很明显)web crypto生成的密码api解码没有问题
- 从服务器端返回字节数组本身没有帮助。
您正在使用的 Webcrypto 端
name: 'RSA-OAEP',
hash: 'SHA-256',
...
实例化算法。在 Java 方面,您“只需”用
实例化密码Cipher cipher = Cipher.getInstance("RSA");
但那是
的同义词Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
您需要使用这些行来实例化 Webcrypto 算法:
Cipher encryptCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
OAEPParameterSpec oaepParameterSpecJCE = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParameterSpecJCE);
ciphertextByte = encryptCipher.doFinal(plaintextByte);
安全说明:512 的密钥长度是 不安全,请使用至少 2048 位的密钥长度。