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

编辑:经过进一步研究,我发现:

  1. (很明显)web crypto生成的密码api解码没有问题
  2. 从服务器端返回字节数组本身没有帮助。

您正在使用的 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 位的密钥长度。