在Java和JavaScript之间使用OAEP的RSA加密

4

我正试图在JavaScript中加密一个短字符串,并在Java中解密它。解密失败了,我认为这是由于两个平台之间的块模式和/或填充差异引起的。我尝试使用Java和JavaScript分别加密相同的字符串,得到不同的结果,这表明确实存在差异。下面是创建密钥的Java代码:

KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair keyPair = kpg.generateKeyPair();

这是我用来测试加密的Java代码:

Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] bytes = cipher.doFinal("asdf".getBytes());

我将公钥发送给JavaScript进程,并将其转换为一个名为publicKey的ArrayBuffer。通过使用crypto.subtle.exportKey导出并检查字节,我已经验证了JavaScript端上的密钥与Java端上的密钥匹配。以下是我用于测试加密的JavaScript代码:

crypto.subtle.importKey('spki', publicKey,
                        {hash: 'SHA-256', name: 'RSA-OAEP'}, true,
                        ['encrypt'])
      .then((key) => {
        crypto.subtle.encrypt({name: 'RSA-OAEP'}, key,
                              new TextEncoder().encode('asdf'))
              .then((buffer) => {

              });
      });

在Java中,字节数组的内容和JavaScript中的数组缓冲区并不相同。我不确定的设置是Java端的参数以及JavaScript端的和参数。是否有任何设置可以使用内置类在Java和JavaScript之间工作?或者我应该看第三方库(例如Bouncy Castle)?

你看到了这个问题吗? - kelalaka
@kelalaka,我没有看到那个问题,谢谢!不幸的是,那个问题中唯一可行的建议似乎是使用node-forge包,如果可以避免,我真的不想使用第三方包。 - Andy King
2
@AndyKing 谢谢你的问题。我之前也遇到了同样的问题,但是没有使用第三方库找到了解决方案。如果有点晚抱歉 :)。 - Chip
3个回答

16

这篇文章有些老,但如果您想在JavaScript中使用subtle crypto并且可以控制Java解密,这里有一个备选方案。

假设您使用了问题中的原始JS代码进行加密,以下是如何在Java中解密的方法:

Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
cipher.init(Cipher.DECRYPT_MODE, privKey, oaepParams);
byte[] decrypted = cipher.doFinal(encryptedBytes)

使用Cipher RSA/ECB/OAEPWithSHA-256AndMGF1Padding 的问题在于它默认使用 SHA-1 作为 MGF1 Padding,而 Javascript 使用 SHA-256,导致不匹配。通过指定 MGF1ParamterSpec,我们可以强制 Java 使用与 Javascript 默认相同的哈希算法。


4

除了@Chip的答案 - 这真的很有帮助 - 我想添加以下情况:

假设您想在Javascript(webcrypto)中用以下方式进行解密:

window.crypto.subtle.decrypt(
        {
            name: "RSA-OAEP",
            hash: { name: "SHA-512" }
            //label: Uint8Array([...]) //optional
        },
        privateRsaKey, //CryptoKey object containing private RSA key
        encdata //ArrayBuffer containing to be decrypted data
    )
    .catch(function(err){
        console.error(err);
    })

接下来,您需要在Java中使用以下OAEPParameterSpec进行加密(可能反之亦然,但我没有尝试过):

OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-512", "MGF1", 
        new MGF1ParameterSpec("SHA-512"), PSource.PSpecified.DEFAULT);

由于 @Chip 只提到了 MGF1 填充,我假设你需要使用

OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-512", "MGF1", 
        new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);

但显然,必须像我第一个OAEPParameterSpec代码块中所示那样将两个哈希函数都更改为SHA-512。


0

看起来 JavaScript 和 Java 中内置的加密/解密功能对于 RSA 加密没有兼容的设置。一个可行的解决方案似乎是从 Github 上获取 forge 库(forge on github)。该库的关键设置在 Github 页面上描述如下(RSA examples):

// encrypt data with a public key using RSAES-OAEP/SHA-256/MGF1-SHA-1
// compatible with Java's RSA/ECB/OAEPWithSHA-256AndMGF1Padding
var encrypted = publicKey.encrypt(bytes, 'RSA-OAEP', {
  md: forge.md.sha256.create(),
  mgf1: {
    md: forge.md.sha1.create()
  }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接