在 Java 中解密 JavaScript Web Crypto API (RSA-OAEP)

问题描述 投票:0回答:1

我尝试在 WebCrypto API(客户端)中使用 RSA-OAEP-SHA-512 加密字符串,然后我想使用 Java(服务器端)再次解密该字符串。但是,服务器端解密失败并出现错误:解密中填充错误。

我正在使用 JavaScript 生成 RSA 密钥:

async function generateRSAKeyPair() {
    const rsaKeyPair = await window.crypto.subtle.generateKey(
      {
        name: 'RSA-OAEP',
        modulusLength: 4096,
        publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537
        hash: { name: 'SHA-512' },
      },
      true,
      ['encrypt', 'decrypt']
    );
    return rsaKeyPair;
}

我正在使用以下方法加密 JSON 字符串:

sync function encryptWithRSA(jsonString, jsonRSAPublicKey) {
    // Parse JSON keys
    const rsaPublicKey = JSON.parse(jsonRSAPublicKey);

    // Import RSA public key
    const importedRSAPublicKey = await window.crypto.subtle.importKey(
      'jwk',
      rsaPublicKey,
      { name: 'RSA-OAEP', hash: { name: 'SHA-512' } },
      true,
      ['encrypt']
    );

    // Convert JSON String to ArrayBuffer
    const aesKeyBuffer = new TextEncoder().encode(jsonString);

    // Encrypt AES key using RSA public key
    const encryptedAesKeyBuffer = await window.crypto.subtle.encrypt(
      { name: 'RSA-OAEP' },
      importedRSAPublicKey,
      aesKeyBuffer
    );

    // Convert encrypted AES key to base64
      const encryptedAesKeyBase64 = window.btoa(String.fromCharCode(...new Uint8Array(encryptedAesKeyBuffer)));
    return encryptedAesKeyBase64;
}

我正在尝试使用 Java 来解密它:

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.math.BigInteger;
import java.util.Base64;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;

public void decrypt() {
    try {
        PrivateKey privateKey = loadPrivateKey();

        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-512AndMGF1Padding");

        cipher.init(Cipher.DECRYPT_MODE, privateKey);

        byte[] encryptedBytes = Base64.getDecoder().decode(cipherText);
        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);

        this.clearText = new String(decryptedBytes);
        System.out.println("Decrypted clear text: " + clearText);
    } catch (Exception e) {
        e.printStackTrace();
        System.err.println("Decryption failed: " + e.getMessage());
    }
}

    private PrivateKey loadPrivateKey() throws Exception {
        // Parse the JWK JSON string
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> jwk = mapper.readValue(rsaKey, Map.class);
        System.out.println("JWK parsed: " + jwk);

        // Extract the key components from the JWK
        String nBase64 = (String) jwk.get("n");
        String dBase64 = (String) jwk.get("d");
        String pBase64 = (String) jwk.get("p");
        String qBase64 = (String) jwk.get("q");
        String dpBase64 = (String) jwk.get("dp");
        String dqBase64 = (String) jwk.get("dq");
        String qiBase64 = (String) jwk.get("qi");
        String eBase64 = (String) jwk.get("e");

        // Decode the Base64 URL-encoded components
        BigInteger modulus = new BigInteger(1, Base64.getUrlDecoder().decode(nBase64));
        BigInteger privateExponent = new BigInteger(1, Base64.getUrlDecoder().decode(dBase64));
        BigInteger publicExponent = new BigInteger(1, Base64.getUrlDecoder().decode(eBase64));
        BigInteger primeP = new BigInteger(1, Base64.getUrlDecoder().decode(pBase64));
        BigInteger primeQ = new BigInteger(1, Base64.getUrlDecoder().decode(qBase64));
        BigInteger primeExponentP = new BigInteger(1, Base64.getUrlDecoder().decode(dpBase64));
        BigInteger primeExponentQ = new BigInteger(1, Base64.getUrlDecoder().decode(dqBase64));
        BigInteger crtCoefficient = new BigInteger(1, Base64.getUrlDecoder().decode(qiBase64));

        // Create the RSA private key specification
        RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent, primeP, primeQ, primeExponentP, primeExponentQ, crtCoefficient);

        // Generate the private key
        KeyFactory keyFactory = KeyFactory.getInstance(AppConfig.CRYPTO_RSA);
        return keyFactory.generatePrivate(keySpec);
    }


这给出了以下输出:

javax.crypto.BadPaddingException: Padding error in decryption
    at java.base/com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:383)
    at java.base/com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:419)
    at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2244)
    at zkgitclientcli.crypto.RsaEncryptionHandler2.decrypt(RsaEncryptionHandler2.java:48)
    at zkgitclientcli.commands.login.DecryptAesKey.execute(DecryptAesKey.java:23)
    at zkgitclientcli.commands.CommandManager.executeCommand(CommandManager.java:44)
    at zkgitclientcli.ZkGit.main(ZkGit.java:12)
    at org.codehaus.mojo.exec.ExecJavaMojo.doMain(ExecJavaMojo.java:385)
    at org.codehaus.mojo.exec.ExecJavaMojo.doExec(ExecJavaMojo.java:374)
    at org.codehaus.mojo.exec.ExecJavaMojo.lambda$execute$0(ExecJavaMojo.java:296)
    at java.base/java.lang.Thread.run(Thread.java:1583)
Decryption failed: Padding error in decryption

私钥导入很可能有效,因为字符串使用相应的公钥在 Java 中加密,并使用此方法成功解密。仅当使用 WebCrypto 加密字符串作为输入时才会失败。

另外,我可以编写一个JavaScript方法来成功解密加密字符串,因此base64编码的加密字符串是可以解密的,但在Java中则不行...

我尝试了以下设置,但没有成功: SHA-1:RSA/ECB/OAEPWithSHA-1AndMGF1Padding SHA-224:RSA/ECB/OAEP 具有 SHA-224 和 MGF1 填充 SHA-256:RSA/ECB/OAEP 具有 SHA-256 和 MGF1 填充 SHA-384:带有 SHA-384 和 MGF1 填充的 RSA/ECB/OAEP

javascript java encryption webcrypto-api
1个回答
0
投票

这是基于上面提供的评论的工作代码:

public void decrypt() {
    try {
       PrivateKey privateKey = loadPrivateKey();

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

  Cipher cipher = Cipher.getInstance(AppConfig.CRYPTO_RSA_OAEP);
         cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams);
         byte[] encryptedBytes = Base64.getDecoder().decode(cipherText);
       byte[] decryptedBytes = cipher.doFinal(encryptedBytes);

       clearText = new String(decryptedBytes);

     } catch (Exception e) {
       clearText = e.getMessage();
     }
}
© www.soinside.com 2019 - 2024. All rights reserved.