如何使用 java 生成的 public 在 nodejs 中加密字符串

How do I encrypt string in nodejs with public generated by java

我想在将密码发送到 Springboot API 之前在 angular 端加密我的密码。我的想法是:-

  1. 在java
  2. 中生成public密钥和私钥
  3. 对 public 密钥和私钥进行 base64 编码
  4. 在节点 (angular) 端使用编码的 public 密钥,这将用于加密
  5. 在springboot rest中使用编码的私钥api,这将用于解密

下面是我的java代码,它生成、加密和解密密码

RSAKeyPairGenerator.java

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

public class RSAKeyPairGenerator {
    private static String privateKeyString = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALyR1WRqVBuvdmXmgrcZ4VMY4hIFTYt63NGEN9atlgnngNliMasvzBofnjRtk5mukezUKujtON6mPnAjKJoEDdiaw3Rk0hKvgW5MszyUBE5bLThexVLbi+nQYSf2GSvd0fjdZn6KjhgVOdbYe+JduU/iPD3/Ti5p+w3MewgZpy2RAgMBAAECgYAqWdqCXfsb6LF/u2C6PN7Faf5EK9q5q9NyXu6nkX70JIFk0U/0cZy2dUlz3vRafMGbXh9xBu5R2yaEyvCwfp6ZF26A8MbS/IaI53LMKmyclhES2f3tZLKUPkg6fntz+e6e/6oSDdQPHP3J2QrpuwzyW+UZJQmmYYj7f9sq/+dawQJBAOnkHWkLuIZNIgLzV0TuHA6fcOXrqrSlS1/Q0qLyaQEZOObocXDvWaCSP+8RXiqZSwAnMJbrGHQA5ULXLWI4GjkCQQDOZP6bfn2VV3m8JSlYL9Uz4TrRYb8Qe3/mohhMsyDB7Ua/6d9BV7JZgbYiXP0utobKVWLxD49OFMkgEs20qY4ZAkB4dJsQ9pBZ2m+hxWE0hsy8WzDxuKV504c2GX3hnaamgi7j/OIvn5UxNSDoJrGwjrIpqgVENF+rnqpz+g3Nf8dBAkBV/op+6yMUGFBmbe1eCwAAD7XcC6f6DBrsU1lgi7n4Uw6JY75bkViEJqFmi+wJjI94uj7xRZRl6g8qx+rhfUvxAkEArD1we6ZLTWz73D8z+4Boj7Su7b5LhfgvXVL5V4Zdog3X58pHK+dYppapDh5KjZZdor+y/6uz1PqIs2dmFy/xtQ==";
    private static String publicKeyString = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kdVkalQbr3Zl5oK3GeFTGOISBU2LetzRhDfWrZYJ54DZYjGrL8waH540bZOZrpHs1Cro7Tjepj5wIyiaBA3YmsN0ZNISr4FuTLM8lAROWy04XsVS24vp0GEn9hkr3dH43WZ+io4YFTnW2HviXblP4jw9/04uafsNzHsIGactkQIDAQAB";
    private static PrivateKey privateKey;
    private static PublicKey publicKey;
    
    public void generateKeys() throws NoSuchAlgorithmException {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        keyGen.initialize(1024);
        KeyPair pair = keyGen.generateKeyPair();
        RSAKeyPairGenerator.privateKey = pair.getPrivate();
        RSAKeyPairGenerator.publicKey = pair.getPublic();
    }
    
    public void readKeys() throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyFactory kf = KeyFactory.getInstance("RSA");

        byte [] encoded = Base64.getDecoder().decode(privateKeyString);
        PKCS8EncodedKeySpec keySpec1 = new PKCS8EncodedKeySpec(encoded);
        RSAKeyPairGenerator.privateKey = kf.generatePrivate(keySpec1);
        
        encoded = Base64.getDecoder().decode(publicKeyString);
        X509EncodedKeySpec keySpec2 = new X509EncodedKeySpec(encoded);
        RSAKeyPairGenerator.publicKey = kf.generatePublic(keySpec2);
    }

    public static void main(String[] args) throws NoSuchAlgorithmException, IOException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {
        RSAKeyPairGenerator keyPairGenerator = new RSAKeyPairGenerator();
        // Next line is for testing with encrypted text generated by nodejs code with genrated keys
        // keyPairGenerator.generateKeys();
        keyPairGenerator.readKeys();
        
        System.out.println("-----Private----------");
        System.out.println(Base64.getEncoder().encodeToString(privateKey.getEncoded()));
        System.out.println("Encoding : "+ privateKey.getFormat());
        System.out.println("----------------------");
        
        System.out.println("-----Public----------");
        System.out.println(Base64.getEncoder().encodeToString(publicKey.getEncoded()));
        System.out.println("Encoding : "+ publicKey.getFormat());
        System.out.println("----------------------");
        
        String secretMessage = "Test";
        System.out.println(secretMessage);
        
        Cipher encryptCipher = Cipher.getInstance("RSA");
        encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
        
        byte[] secretMessageBytes = secretMessage.getBytes(StandardCharsets.UTF_8);
        byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
        // Next lines are for testing decryption with encrypted text generated by nodejs code with generated keys
        // byte[] encryptedMessageBytes = "B��u���<⌂׷�lǚm0�W������1�%EN�7��‼��l��l�<�����k;�������GĦ9D8��Z��I�Oɺ♫��→P'�§�{k�Ɋ+-}?��rZ".getBytes();
        // byte[] encryptedMessageBytes =  Base64.getDecoder().decode("woboAUytDXJLlKm7zbqNdxVORG+kio9kZxvMPOHruQfxwNEx7SVFTsw3oeETqbRs4NZskjzO2Nzjyms73vv758Dcy0fEpjlEOKmrWuG62knOT8m6DqGDGlAn1hXpe2u6yYorLX0/6fhyWg1C/JR1sbaKPH/Xt+Fsx5ptMONX0uw=");
        System.out.println(new String(encryptedMessageBytes));
                
        Cipher decryptCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
        decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
        
        byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes);
        String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8);
        System.out.println(decryptedMessage);
    }
}

这给了我以下输出

-----Private----------
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMv+t+D6/7ZY6m+CjD97NB989P2YjmGcE2yHUokyjmxVi/SxIu9MboiVNewR+mFjTFYPGgMWHz1/XeVGlinjN/5+0fdiO/JEvEUilFmkkIF3My11I09b9NZ9RJmpCNCbqOg7NSt6VD8IC/w19N25xwcofc3qbgfEjKSs0CgxfahrAgMBAAECgYEAiyLlEBKiryDeZchJGFNULdXw07dmBbWKmg+CgAl3kvSWTQM0rLsY+Rese6OXfy1XN6t9NnW0QSHKTUNj0JYl7btKEblVHERbjwavUe83tcNIc3o0xrGoBAzma14ZicPYm70JWixTO778lm5AXKl504+oOQyVYmfgXcevPXwdIkECQQDvO3DBuu6wp5nbwR4t0736HOH9jVIjJOS8oABUARDhNt1fkb8uEptDr5EomVRMqarJMAjSMpYxrUCpvgtf4nZ5AkEA2ksDr8pbbFvZPaAe0F43LmJtFM3PvCz87S93p20YjwVMmGV17sJ5t7a26HtubgRLEWq4z6rxq2oXPv4y1lstAwJAbj9cVUtKWIrEcutqdwAPmsXYt7p60ctcxjiOLihXmRJprnNCQX89olG0eZs/qBzAofrK9eNuJ/KJzC/SmhuJMQJAAz9gc6oQCCGprrgGHVV5frAqLUgOkh8dOC4fmpcN6XrLs+y2f3HXO7t1JypG704TC9RJoZVKeSFf7Sj8+qFqnwJBALRiQc9SG5Ht5TLo12W4l3bRaxpbo597BgKRjz3hqiySdqjZA9Qe84qmEKveZ9eWehbfqwzKmCbW43I1ZEFZ95o=
Encoding : PKCS#8
----------------------
-----Public----------
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDL/rfg+v+2WOpvgow/ezQffPT9mI5hnBNsh1KJMo5sVYv0sSLvTG6IlTXsEfphY0xWDxoDFh89f13lRpYp4zf+ftH3YjvyRLxFIpRZpJCBdzMtdSNPW/TWfUSZqQjQm6joOzUrelQ/CAv8NfTduccHKH3N6m4HxIykrNAoMX2oawIDAQAB
Encoding : X.509
----------------------
Test
�N::�`��\A���ƈ�~��5s���
�0�I�C�uƹx2�Z&Kں�"ьC�$
q��K���h�^<�5�E�Ɨ0B�͒Z�{��EDbDH�.��#:�e��h~j���������q�c��R�
Test

在 Nodejs 方面,我使用上面的 public 来加密我的文本。

index.js

const crypto = require("crypto");

var pubkey = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDL/rfg+v+2WOpvgow/ezQffPT9mI5hnBNsh1KJMo5sVYv0sSLvTG6IlTXsEfphY0xWDxoDFh89f13lRpYp4zf+ftH3YjvyRLxFIpRZpJCBdzMtdSNPW/TWfUSZqQjQm6joOzUrelQ/CAv8NfTduccHKH3N6m4HxIykrNAoMX2oawIDAQAB\n-----END PUBLIC KEY-----";

var plaintext = "Test"
var buffer = new Buffer.from(plaintext, "utf8");
var enc = crypto.publicEncrypt(pubkey, buffer);
var bs64 = enc.toString('base64');

console.log(enc.toString());
console.log(enc.toString('base64'));

这给了我以下输出

B��u���<⌂׷�lǚm0�W������1�%EN�7��‼��l��l�<�����k;�������GĦ9D8��Z��I�Oɺ♫��→P'�§�{k�Ɋ+-}?��rZ
woboAUytDXJLlKm7zbqNdxVORG+kio9kZxvMPOHruQfxwNEx7SVFTsw3oeETqbRs4NZskjzO2Nzjyms73vv758Dcy0fEpjlEOKmrWuG62knOT8m6DqGDGlAn1hXpe2u6yYorLX0/6fhyWg1C/JR1sbaKPH/Xt+Fsx5ptMONX0uw=

现在我尝试使用 java 端的两个字符串进行解密。

当我尝试解码字符串时,出现以下错误

Exception in thread "main" javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes
    at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:346)
    at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:391)
    at javax.crypto.Cipher.doFinal(Cipher.java:2168)
    at com.mindtree.starr.wsr.RSAKeyPairGenerator.main(RSAKeyPairGenerator.java:90)

而且,当我尝试解码 base64 字符串时,出现以下错误

Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
    at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:379)
    at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:290)
    at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:365)
    at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:391)
    at javax.crypto.Cipher.doFinal(Cipher.java:2168)
    at com.mindtree.starr.wsr.RSAKeyPairGenerator.main(RSAKeyPairGenerator.java:90)

我做错了什么?

问题在第一条评论中描述得很清楚,解决方案在第二条评论中。

对我有用的 Cipher 实例的正确转换字符串是 RSA/ECB/OAEPWithSHA-1AndMGF1Padding

所以下面的解密 Cipher 实例化工作得很好。

Cipher decryptCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");