zoukankan      html  css  js  c++  java
  • Node.js 基于 ursa 模块的 RSA 加密解密(已与IOS,Android实现加密通信)

    前几天调试一个RSA加密,遇到一些问题,在网上找了好久好久,与Node.js相关的资源少得非常可怜,最后还是靠自己一步一步解决了,今天把代码和一些心得拿出来分享一下:
    cnode链接地址:https://cnodejs.org/topic/54d2de4cfbf1e531447acc95

    一. 相关知识
    这里推介几篇博客,在做加密解密之前先了解一下,我以前没做过,所以上网找了一些,提供大家参考。
    算法原理1
    算法原理2
    关于密钥长度
    关于填充
    ursa模块
    关于RSA的原理什么的,上面的文章有详细的介绍,这里不再赘述,下面开始加密/加密的操作。

    二. 生成密钥对
    /**
    * 2015-02-03
    * zhaomaoxin
    * generate keys
    */

    var fs = require('fs');
    var ursa = require('ursa');

    var modulusBit = 512;
    var key = ursa.generatePrivateKey(modulusBit, 65537);

    var privatePem = ursa.createPrivateKey(key.toPrivatePem()); //生成私钥
    var privateKey = privatePem.toPrivatePem('utf8');
    fs.writeFile('private.pem', privateKey, 'utf8', function(error){
    if(error){
    throw error;
    }
    console.log(' 私钥privateKey已经保存 ');
    console.log(' 私钥privateKey: ' + privateKey);
    });


    var publicPem = ursa.createPublicKey(key.toPublicPem()); //生成公钥
    var publicKey = publicPem.toPublicPem('utf8');
    fs.writeFile('public.pub', publicKey, 'utf8', function(error){
    if(error){
    throw error;
    }
    console.log(' 私钥publicKey已经保存 ');
    console.log(' 私钥publicKey: ' + publicKey);
    });

    说明: 将密钥写入文件或者直接使用打印出来,需要使用toPublicPem或者toPrivatePem转换成UTF8编码形式。

    三. 密钥导入
    加密/解密的双方各自生成密钥对,需保证能导入对方的密钥,并使用公钥进行加密 。
    key.js

    /**
    * 2015-02-03
    * zhaomaoxin
    * import keys
    */

    var ursa = require('ursa');


    var client_public = '-----BEGIN PUBLIC KEY----- '+
    'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ6d7vkyWjQpHDgpcwBxiAoecUZPeKA7 '+
    '5cCgZlnYnXNR08yPAzJuBUrTUODloj3OFxN2WE/VpPSQsu2KGpBIO9MCAwEAAQ== '+
    '-----END PUBLIC KEY-----';


    var client_private = '-----BEGIN RSA PRIVATE KEY----- '+
    'MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAnp3u+TJaNCkcOClz ' +
    'AHGICh5xRk94oDvlwKBmWdidc1HTzI8DMm4FStNQ4OWiPc4XE3ZYT9Wk9JCy7Yoa ' +
    'kEg70wIDAQABAkAZ0wDLVaVWBLNxeV4d3l4Vt6sdlWbAP8BCQlsnmZrY4WIIy1HE ' +
    'Jqa2eCdedlHzuEPxsGnmHxO4nQpGFug1IGqBAiEAzIjiNZ3Ej4Ko8XkLkNxPcnbR ' +
    'VR/iG2THWtPr2cuTcbMCIQDGh0DfD1l/EGj/OpwDao8Dk0dnUbWXrTskrlfmXSO9 ' +
    'YQIhAIXnA3kofVuapbHYlgrTQKvmP6tkASn/80dyQBDI5xFjAiEApy22fyBZ6RpU ' +
    'kLk2L9pH3Gbltiekl7mVGGqIMsE0G4ECIFb3wKlEXS2q1zDLz58SdLyLh/no19CD ' +
    '170XCkCMYq+7 '+
    '-----END RSA PRIVATE KEY-----';


    var server_public = '-----BEGIN PUBLIC KEY----- '+
    'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALFSzfjrIl8Vj0dbUVTvVEXnGMOL3O6I '+
    'OWsK0EJcEk18BktuaIGC0gWb8f9MpL3cqouRaDuS2JM+xIZrocyA5YECAwEAAQ== '+
    '-----END PUBLIC KEY-----';


    var server_private = '-----BEGIN RSA PRIVATE KEY----- '+
    'MIIBPQIBAAJBALFSzfjrIl8Vj0dbUVTvVEXnGMOL3O6IOWsK0EJcEk18BktuaIGC '+
    '0gWb8f9MpL3cqouRaDuS2JM+xIZrocyA5YECAwEAAQJBAJzBNaIZws3JklqjSFfM '+
    'JnSRIZwkNQ+Mzy1oZshy+h8RznAxD0yQRgHvlU+cUhjLr4znQpyVSZ5686Ay9LI1 '+
    'eVECIQDhrcCUzOKsVhEjlotfDpBHixWdJNzt62UcLwdXthW/dQIhAMkl3fDGE7wR '+
    'ZIjRSVOgGU8VgR67WV14DNXD8cqVffhdAiEAkVu8wxsElUQKXgXFV0CmJa6sCT+J '+
    'HaWUxoZ0EEaz01ECIQC++sUOpgJ2vczGWm9Uht2AyNofY6IlrKYDEFeyEN3ZwQIh '+
    'AJ+UaCEDeiFRwSxFYnCpkhTU1ZUVrzo4HaZuAt780KBD '+
    '-----END RSA PRIVATE KEY-----';

    var server = {
    pem :ursa.createPrivateKey(server_private),
    pub :ursa.createPublicKey(server_public)
    };

    var client = {
    pub :ursa.createPublicKey(client_public),
    pem :ursa.createPrivateKey(client_private)
    };

    exports.server = server;
    exports.client = client;

    说明: 在使用ursa导入密钥的时候,可能遇到下面的错误,表明密钥无法导入,这个的原因可能是密钥字符串拼接的问题,可以先先用ursa模块生成密钥,然后直接在public.pub和private.pem(这两个文件是用ursa生成密钥后用fs写的文件)中直接拼接好再拷过来重新试试,多半是拼接的问题。

    throw new Error("Not a key.");
    ^
    Error: Not a key.
    at Object.createKey (/home/zhaomaoxin/project/node/xxt-crypt/node_modules/ursa/lib/ursa.js:402:15)
    at Object.<anonymous> (/home/zhaomaoxin/project/node/xxt-crypt/pems/index.js:37:15)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

    四. 加密,解密方法

    /**
    * 2015-02-03
    * zhaomaoxin
    * encrypt and decrypt data
    */
    var ursa = require('ursa');
    var encoding = require('encoding');
    var pems = require('../pems');
    var client = pems.client; //客户端密钥
    var server = pems.server; //服务端密钥
    var clientPublic = client.pub; //客户端公钥
    var serverPublic = server.pub; //服务端公钥
    var clientPrivate = client.pem; //客户端私钥
    var serverPrivate = server.pem; //服务端私钥
    var clientModulusBit = 512;
    var serverModulusBit = 512;
    var clientMaxBit = clientModulusBit/8;
    var serverMaxBit = serverModulusBit/8;
    var clientRealBit = clientMaxBit - 11;
    var serverRealBit = serverMaxBit - 11;
    var padding = ursa.RSA_PKCS1_PADDING;

    //加密,使用客户端公钥加密
    exports.clientEncrypt = function(plain){
    plain = plain || "";
    return encrypt(plain, clientPublic, clientRealBit, padding);
    };

    //解密,使用客户端私钥解密
    exports.clientDecrypt = function(cipher){
    cipher = cipher || "";
    return decrypt(cipher, clientPrivate, clientMaxBit, padding);
    };

    //加密,使用服务端公钥加密
    exports.serverEncrypt = function(plain){
    plain = plain || "";
    return encrypt(plain, serverPublic, serverRealBit, padding);
    };

    //解密,使用服务端私钥解密
    exports.serverDecrypt = function(cipher){
    cipher = cipher || "";
    return decrypt(cipher, serverPrivate, serverMaxBit, padding);
    };

    //用于获取内容的字节数
    function bytes(text, coding) {
    if (typeof text === 'undefined') {
    throw new Error("must have a arg.");
    }

    coding = coding || 'utf8';
    return Buffer.byteLength(text.toString(), coding);
    }

    function encrypt(plain, publicKey, realBit, padding){
    var start1 = 0;
    var end1 = realBit;
    var result1 = '';
    var originBuff = new Buffer(plain);
    var originByte = bytes(plain, 'utf8');
    while(start1 < originByte){
    var originTmp = originBuff.slice(start1, end1);
    result1 += publicKey.encrypt(originTmp, 'binary', 'binary', padding);
    start1 += realBit;
    end1 += realBit;
    }

    var encrypted = encoding.convert(result1, 'binary', 'base64');

    return encrypted.toString();
    }

    function decrypt(cipher, privateKey, maxBit, padding){
    var start2 = 0;
    var end2 = maxBit;
    var result2 = '';
    var cipherBuff = encoding.convert(cipher, 'base64', 'binary'); //这个地方很关键,直接使用new Buffer(cipher, 'base64') 报编码错误
    var cipherByte = bytes(cipher, 'base64');
    while(start2 < cipherByte){
    var cipherTmp = cipherBuff.slice(start2, end2); //请注意slice函数的用法
    result2 += privateKey.decrypt(cipherTmp, 'binary', 'binary', padding); //先保存成二进制,待完成解密后再转换成字符串
    start2 += maxBit;
    end2 += maxBit;
    }

    var decrypted = encoding.convert(result2, 'binary', 'utf8');
    return decrypted.toString();
    }

    说明: 这个部分是比较重要的,也是主要的加密,解密方法,ursa模块没有对明文或者密文过长的情况做处理,这个需要自己做分段处理(另外一个模块node-rsa中无需自己处理),在加密,解密过程中需求还需要注意数据的编码方式,以及相互之间的转换,否则在加密或者解密的时候会报错。下面是加密,解密过程中遇到的问题。

    明文过长

    return encodeBuffer(rsa.publicEncrypt(buf, padding), outEncoding);
    ^
    Error: error:0406D06E:rsa routines:RSA_padding_add_PKCS1_type_2:data too large for key size
    at Object.encrypt (/home/zhaomaoxin/project/node/xxt-crypto/node_modules/ursa/lib/ursa.js:235:33)
    at Object.<anonymous> (/home/zhaomaoxin/project/node/xxt-crypto/rsa.js:58:24)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

    密文过长

    return encodeBuffer(rsa.privateDecrypt(buf, padding), outEncoding);
    ^
    Error: error:0406506C:rsa routines:RSA_EAY_PRIVATE_DECRYPT:data greater than mod len
    at Object.decrypt (/home/zhaomaoxin/project/node/xxt-crypto/node_modules/ursa/lib/ursa.js:291:33)
    at Object.<anonymous> (/home/zhaomaoxin/project/node/xxt-crypto/rsa.js:120:24)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

    编码错误

    return encodeBuffer(rsa.privateDecrypt(buf, padding), outEncoding);
    ^
    Error: error:0407109F:rsa routines:RSA_padding_check_PKCS1_type_2:pkcs decoding error
    at Object.decrypt (/home/closure/project/node/xxt-crypto/node_modules/ursa/lib/ursa.js:291:33)
    at Object.<anonymous> (/home/closure/project/node/xxt-crypto/rsa.js:113:30)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

    说明: 编码错误的问题,引入了encoding模块进行换转,这个问题绕了挺久的,后面直接将每次切割的片段console.log()出来,发现还是base64编码的密文,猜测错误可能在编码上,做了转换之后没有报错,而且解密成功了。

    总结: 初次使用RSA操作,遇到了很多问题,一一解决了,至少达到加密解密的目的了,以上就是我的分享,毕竟新手,如果有不对或者不妥之处还望指点,相互学习。
    ————————————————
    版权声明:本文为CSDN博主「赵茂鑫」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/jameslong108159/java/article/details/43566843

  • 相关阅读:
    'NODE_ENV' 不是内部或外部命令,也不是可运行的程序 或批处理文件
    DOM property 和HTML attribute 的区别详解
    python语言和R语言实现机器学习算法
    特征工程二
    特征工程(一)
    梯度提升树GBD
    一种新型的聚类算法
    pcA降维 SVD
    xgboost原理
    互联网广告之工程点击率特征工程
  • 原文地址:https://www.cnblogs.com/zzsdream/p/12827405.html
Copyright © 2011-2022 走看看