美文网首页
nodejs和java通过RSA进行签名和验签的两种方式

nodejs和java通过RSA进行签名和验签的两种方式

作者: 繁华落尽ing | 来源:发表于2019-06-14 17:40 被阅读0次

    前言

    1. 通常我们使用的是标准的PEM证书,PEM证书是OpenSSL的标准格式,详细信息可以百度。
    2. PEM格式信息最大的特点是会带头信息和尾信息。公钥: -----BEGIN PUBLIC KEY----------END PUBLIC KEY-----。私钥 -----BEGIN PRIVATE KEY----------END PRIVATE KEY-----"
    3. 简述下利用openssl生成pem的过程:
    • 1.生成RSA私钥 通常大于512,选择1024或者2048
      openssl genrsa -out rsa_private_key.pem 1024
    • 2.需要转换成RSA私钥转换成PKCS8格式
      openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem-outform PEM -nocrypt
    • 3.生成RSA公钥
      openssl rsa -in rsa_private_key.pem-pubout -out rsa_public_key.pem

    通过PEM进行加密、解密、签名、验签

    java端核心操作流程

    1.Java通过RSA对数据进行加密、解密、签名、验签的流程。

    1)私钥签名

    a)获取私钥
    //获取KeyFactory,指定RSA算法

    KeyFactorykeyFactory = KeyFactory.getInstance("RSA");
    
    //将BASE64编码的私钥字符串进行解码
    
    BASE64Decoderdecoder = newBASE64Decoder();
    
    byte[] encodeByte = decoder.decodeBuffer(priKey);
    
    //将BASE64解码后的字节数组,构造成PKCS8EncodedKeySpec对象,生成私钥对象
    
    PrivateKeyprivatekey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodeByte));
    

    b)使用私钥,对数据进行签名

    //获取Signature实例,指定签名算法(本例使用SHA1WithRSA)
    
    Signaturesignature = Signature.getInstance("SHA1WithRSA");
    
    //加载私钥
    
    signature.initSign(privatekey);
    
    //更新待签名的数据
    
    signature.update(plain.getBytes("UTF-8"));
    
    //进行签名
    
    byte[] signed = signature.sign();
    
    //将加密后的字节数组,转换成BASE64编码的字符串,作为最终的签名数据
    
    BASE64Encoderencoder = newBASE64Encoder();
    
    return encoder.encode(signed);
    

    2)公钥验签

    a)获取公钥

    //获取KeyFactory,指定RSA算法
    
    KeyFactorykeyFactory = KeyFactory.getInstance("RSA");
    
    //将BASE64编码的公钥字符串进行解码
    
    BASE64Decoderdecoder = newBASE64Decoder();
    
    byte[] encodeByte = decoder.decodeBuffer(pubKey);
    
    //将BASE64解码后的字节数组,构造成X509EncodedKeySpec对象,生成公钥对象
    
    PublicKeypublicKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodeByte));
    

    b)使用公钥,进行验签

    //获取Signature实例,指定签名算法(与之前一致)
    
    Signaturesignature = Signature.getInstance("SHA1WithRSA");
    
    //加载公钥
    
    signature.initVerify(publicKey);
    
    //更新原数据
    
    signature.update(plain.getBytes("UTF-8"));
    
    //公钥验签(true-验签通过;false-验签失败)
    
    BASE64Decoderdecoder = newBASE64Decoder();
    
    return signature.verify(decoder.decodeBuffer(sign));
    

    Nodejs通过RSA进行签名和验签

    1.按照相关包

    npm install node-rsa --save

    2.首先生成key

    使用的私钥是由java端生成的

    require node-rsa包
    const NodeRSA = require('node-rsa');

    const key = new NodeRSA({
     b: 1024
    });
    key.importKey(privateKey, 'pkcs8'); // privateKey 带头尾的格式化的字符串
    key.setOptions({ signingScheme: 'sha256' });//指定加密格式
    

    a)nodejs进行签名

    key.sign(buffer, [encoding], [source_encoding]);
    

    b)nodejs进行验签

    key.verify(buffer, signature, [source_encoding], [signature_encoding]) //Return result of check, true or false.
    

    划重点了


    现实情况下,Java有自己的KeyPairGenerator类可以自己生成公钥和私钥,并不用借助openssl。导致其他语言比较nodejs和python统一不了私钥和公钥的格式。最大的区别是KeyPairGenerator生成的是字符串形式的KEY而不是PEM格式的。

    Java生成公钥和私钥

        // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
        KeyPairGenerator keyPairGen = null;
        try {
            keyPairGen = KeyPairGenerator.getInstance("RSA");
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // 初始化密钥对生成器,密钥大小为96-1024位
        keyPairGen.initialize(2048, new SecureRandom());
        // 生成一个密钥对,保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        // 得到私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        // 得到公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        // 得到公钥字符串
        String publicKeyString = Base64.encode(publicKey.getEncoded());
        // 得到私钥字符串
        String privateKeyString = Base64.encode(privateKey.getEncoded());
    

    Java签名 同上

     * RSA签名
     * 
     * @param content    待签名数据
     * @param privateKey 商户私钥
     * @param encode     字符集编码
     * @return 签名值
     */
    public static String sign(String content, String privateKey, String encode) {
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
    
            KeyFactory keyf = KeyFactory.getInstance("RSA");
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);
    
            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
    
            signature.initSign(priKey);
            signature.update(content.getBytes(encode));
    
            byte[] signed = signature.sign();
    
            return Base64.encode(signed);
        } catch (Exception e) {
            e.printStackTrace();
        }
    
        return null;
    }
    

    Java 验签

    /**
     * RSA验签名检查
     * 
     * @param content   待签名数据
     * @param sign      签名值
     * @param publicKey 分配给开发商公钥
     * @param encode    字符集编码
     * @return 布尔值
     */
    public static boolean doCheck(String content, String sign, String publicKey, String encode) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = Base64.decode(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
    
            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
    
            signature.initVerify(pubKey);
            signature.update(content.getBytes(encode));
    
            boolean bverify = signature.verify(Base64.decode(sign));
            return bverify;
    
        } catch (Exception e) {
            e.printStackTrace();
        }
    
        return false;
    }
    

    node 的签名和验签

    辅助工作

    1. 需要借助转换函数转换成PEM格式
    function insertStr(str, insertStr, sn) {
        var newstr = '';
        for (var i = 0; i < str.length; i += sn) {
        var tmp = str.substring(i, i + sn);
        newstr += tmp + insertStr;
        }
    return newstr;
    }
    
    const getPrivateKey = function(key) {
        const result = insertStr(key, '\n', 64);
        return '-----BEGIN PRIVATE KEY-----\n' + result + '-----END PRIVATE KEY-----';
    };
    
    const getPublicKey = function(key) {
        const result = insertStr(key, '\n', 64);
        return '-----BEGIN PUBLIC KEY-----\n' + result + '-----END PUBLIC KEY-----';
    };
    

    2.签名

    const key = new NodeRSA({
     b: 2048  //可以知道位数
    });
    key.importKey(privateKey, 'pkcs8'); //根据java端模式
    key.setOptions({ signingScheme: 'sha256' });//指定加密格式
    
    
    // API key.sign(buffer, [encoding], [source_encoding]);
    let sign = key.sign(noSign, 'base64', 'utf8');
    

    特别注意http请求的时候会把RSA签名后的字符串中的+号进行转义。需要调用下 encodeURIComponent方法。

    相关文章

      网友评论

          本文标题:nodejs和java通过RSA进行签名和验签的两种方式

          本文链接:https://www.haomeiwen.com/subject/lenpfctx.html