简体   繁体   English

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

[英]RSA Encryption with OAEP between Java and JavaScript

I am trying to encrypt a short string in JavaScript and decrypt it in Java.我试图用 JavaScript 加密一个短字符串并用 Java 解密它。 The decryption fails, and I think it's because of a difference in the block mode and/or padding between the two platforms.解密失败,我认为这是因为两个平台之间的块模式和/或填充不同。 I tried encrypting the same string in both Java and JavaScript, and got different results, which indicates that there is indeed a difference.我尝试在Java和JavaScript中加密相同的字符串,得到不同的结果,这表明确实存在差异。 Here is the Java code that creates the key:这是创建密钥的 Java 代码:

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

And here is the Java code that I used to test the encryption:这是我用来测试加密的 Java 代码:

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

I send the public key across to the JavaScript process, and convert it to an ArrayBuffer, with the variable name publicKey .我将公钥发送到 JavaScript 进程,并将其转换为变量名publicKey的 ArrayBuffer。 I have verified that the key on the JavaScript side matches the key on the Java side (by exporting it with crypto.subtle.exportKey and examining the bytes).我已经验证了 JavaScript 端的密钥与 Java 端的密钥匹配(通过使用crypto.subtle.exportKey导出它并检查字节)。 Here is the JavaScript code that I used to test the encryption:这是我用来测试加密的 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) => {

              });
      });

The contents of the byte array in Java and the array buffer in JavaScript are not the same. Java 中的字节数组和 JavaScript 中的数组缓冲区的内容是不一样的。 The settings that I am unsure of are the parameter to Cipher#getInstance on the Java side and the parameters to importKey and encrypt on the JavaScript side.我不确定的设置是 Java 端的Cipher#getInstance参数和 JavaScript 端的importKeyencrypt参数。 Are there any settings that will work between Java and JavaScript using the built-in classes?是否有任何设置可以使用内置类在 Java 和 JavaScript 之间工作? Or should I look at third-party libraries (eg, Bouncy Castle)?或者我应该查看第三方库(例如,Bouncy Castle)?

This is old, but here's an alternate solution if you want to use subtle crypto in javascript, and have control over the java decryption.这是旧的,但是如果您想在 javascript 中使用微妙的加密并控制 java 解密,这里有一个替代解决方案。

Here's how you decrypt in Java assuming you used the original JS code in the question to encrypt:假设您使用问题中的原始 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)

The issue with the Cipher RSA/ECB/OAEPWithSHA-256AndMGF1Padding is that it uses SHA-1 for the MGF1 Padding by default.密码RSA/ECB/OAEPWithSHA-256AndMGF1Padding的问题在于它默认使用 SHA-1 作为 MGF1 填充。 Javascript uses SHA-256, which causes the mismatch. Javascript 使用 SHA-256,这会导致不匹配。 By specifying the MGF1ParamterSpec, we can force Java to use the same hashing algorithm as Javascript default.通过指定 MGF1ParamterSpec,我们可以强制 Java 使用与 Javascript 默认相同的哈希算法。

In addition to @Chip's answer - which was really helpful - I would like to add the following case:除了@Chip 的回答 - 这真的很有帮助 - 我想添加以下案例:

Assume you want to use the following for decryption in Javascript (webcrypto):假设您想在 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);
    })

Then you have to use the following OAEPParameterSpec for encryption in Java (and likely vice versa but I did not try that):然后你必须使用以下 OAEPParameterSpec 在 Java 中进行加密(反之亦然,但我没有尝试过):

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

Since @Chip referred to the MGF1 Padding I only, I assumed one would have to use由于@Chip 只提到了 MGF1 Padding I,我认为必须使用

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

but apparently one has to change both hash functions to SHA-512 as shown in my first OAEPParameterSpec code block.但显然必须将两个哈希函数更改为 SHA-512,如我的第一个 OAEPParameterSpec 代码块所示。

It looks as though the built-in encryption/decryption in JavaScript and Java do not have compatible settings for RSA encryption.看起来 JavaScript 和 Java 中的内置加密/解密没有兼容的 RSA 加密设置。 A viable solution appears to be the forge library from github ( forge on github ).一个可行的解决方案似乎是来自 github 的 forge 库( forge on github )。 The key settings are described on the github page as follows ( RSA examples ): github页面上的密钥设置描述如下( RSA示例):

// 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()
  }
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM