【问题标题】:Java Decrypt with NodeJS Encrypt <RSA_PKCS1_OAEP_PADDING> padding and <sha256> oaepHashJava Decrypt with NodeJS Encrypt <RSA_PKCS1_OAEP_PADDING> padding 和 <sha256> oaepHash
【发布时间】:2021-05-04 17:13:13
【问题描述】:

我需要解密来自使用 NodeJS 构建的外部服务的一些信息。 此服务要求提供 base64 中 pem 格式的 RSA (2048) 公钥,以加密信息。

我正在用 Java 创建密钥对:

    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    kpg.initialize(2048);
    KeyPair kp = kpg.generateKeyPair();
    PublicKey pubkey = kp.getPublic();
    PrivateKey privkey = kp.getPrivate();

    String pemPublicString = "-----BEGIN PUBLIC KEY-----\n";
    pemPublicString = pemPublicString+Base64.getEncoder().encodeToString(pubkey.getEncoded())+"\n";
    pemPublicString = pemPublicString+"-----END PUBLIC KEY-----\n";
    
    String pemPrivateString = "-----BEGIN RSA PRIVATE KEY-----\n";
    pemPrivateString = pemPrivateString+Base64.getEncoder().encodeToString(privkey.getEncoded())+"\n";
    pemPrivateString = pemPrivateString+"-----END RSA PRIVATE KEY-----\n";
    
    //Send to node js service
    String base64publickey = Base64.getEncoder().encodeToString(pemPublicString.getBytes());

    //Store for decrypting
    String base64privatekey = Base64.getEncoder().encodeToString(pemPrivateString.getBytes());

外部服务正在对信息进行如下加密并返回字节:

  crypto.publicEncrypt(
  {
    key: publicKey,
    padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
    oaepHash: "sha256",
  },
    dataToEncrypt
  );

我正在尝试将Java中的信息解密如下:

    public String decrypt(String payload, String privateKey){
      byte [] ciphertext = payload.getBytes(StandardCharsets.UTF_8);
      Cipher oaepFromInit = Cipher.getInstance("RSA/ECB/OAEPPadding");
      OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new 
      MGF1ParameterSpec("SHA-1"), PSource.PSpecified.DEFAULT);
      oaepFromInit.init(Cipher.DECRYPT_MODE, getRSAPrivateKeyFrom(privateKey), oaepParams);
      byte[] pt = oaepFromInit.doFinal(ciphertext);
      return new String(pt, StandardCharsets.UTF_8);
    }

    private PrivateKey getRSAPrivateKeyFrom(String content) {
      byte[] decodedBytes = Base64.getDecoder().decode(content);
      String decodedString = new String(decodedBytes);
      Security.addProvider(new BouncyCastleProvider());
      PEMParser pemParser = new PEMParser(new StringReader(decodedString));
      JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
      Object object = pemParser.readObject();
      PrivateKey k = converter.getPrivateKey((PrivateKeyInfo) object);
      return k;
   }

现在我得到一个 BadPadding 异常,知道可能是什么问题吗? 提前致谢。

【问题讨论】:

  • 你能试试MGF1ParameterSpec("SHA-256")吗? Java 有点特殊,因为它不为 MGF 和标签使用相同的散列。通常,如果在库中只给出一个哈希,则它用于两者。如果不起作用,请尝试将"SHA-1" 作为第一个参数...
  • 您的 RSA 私钥未正确编码,Java 使用 PKCS#8 作为默认编码,在 PEM 中应该只是 PRIVATE KEY,而不是 RSA PRIVATE KEY

标签: javascript java node.js rsa encryption-asymmetric


【解决方案1】:

发布的代码没有显示 NodeJS 代码如何导出密文。 Java 代码decrypt() 中的以下行:

 byte[] ciphertext = payload.getBytes(StandardCharsets.UTF_8);

表示您使用了 Utf-8 编码。这是破坏密文的常见错误(请参阅here)。而不是 Utf-8,应用 binary-to-text enncoding,例如 Base64。

密文的导出将在 NodeJS 中完成:

var chiphertextBase64 = ciphertext.toString('base64');

以及在 Java 中的导入:

import java.util.Base64;
...
byte[] ciphertext = Base64.getDecoder().decode(payload);  

在 NodeJS 代码中,OAEP (RSAES-OAEP) 被指定为填充。 crypto.publicEncrypt() 与参数 oaepHash 一起适用于 OAEP 和 MGF1 摘要的 same 摘要。 oaepHash: "sha256" 因此为 both 摘要指定了 SHA256。
相反,Java 允许单独(和不同)规范 OAEP 和 MGF1 摘要。虽然 SHA256 用于 decrypt() 中的 OAEP 摘要,但 SHA1 用于 MGF1 摘要。后者与NodeJS代码不一致,需要改成SHA256:

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

Maarten Bodewes 在第 1 条评论中已怀疑此错误。


在发布的代码中,PEM 编码的pemPublicStringpemPrivateString 键分别被Base64 编码为base64publickeybase64privatekey。这不是必需的,因为 PEM 编码密钥的主体已经经过 Base64 编码,并且页眉和页脚由 ASCII 字符组成。因此,第二次 Base64 编码没有带来任何好处,但缺点是密钥数据被不必要地放大(Base64 开销:33%,请参阅here)。另一方面,如果服务需要双 Base64 编码的公钥,则必须遵守。

生成密钥时,发布的 Java 代码隐式使用 PKCS#8 格式的私钥。您可以使用privkey.getFormat() 验证这一点,它将输出PKCS#8。 PKCS#8 的相关标头是-----BEGIN PRIVATE KEY----- 和页脚-----END PRIVATE KEY-----。您当前使用的页眉和页脚都属于 PEM 编码的 PKCS#1 私钥,因此与密钥数据不一致。 Maarten Bodewes 在第二条评论中已经解决了这个问题。

顺便说一句,无需 BouncyCastle 使用 PKCS8EncodedKeySpec 即可轻松导入 PKCS#8 密钥。为此,只需删除页眉、页脚和换行符,其余部分必须进行 Base64 解码(DER 编码),s。例如here.

【讨论】:

    猜你喜欢
    • 2022-12-02
    • 2022-12-02
    • 2022-01-25
    • 1970-01-01
    • 2017-11-28
    • 2018-12-11
    • 2012-11-24
    • 1970-01-01
    • 2012-04-30
    相关资源
    最近更新 更多