跳转到内容

Nodejs和PHP进行RSA加密签名

1.转换 PHP 给的 pem 格式

getKey.js

js
/**
 * 1.密钥格式通常有两种,分别是pkcs1和pkcs8,pkcs代表Public Key Cryptography Standards
 * 2. pkcs1格式开头BEGIN RSA PRIVATE KEY  pkcs8格式 开头BEGIN PRIVATE KEY  中间是BASE64 ENCODED DATA
 * 3.phpseclib 生成的密钥对中,公钥使用的是 pkcs8 格式,私钥使用 pkcs1 格式,但文档中对它们的描述都是 pkcs1 格式
<? php

//转换publickey格式
 function getPublicKey()
  // 系统公钥
  $publickey = 'LKDKFJIDOWEIRLKLDSJKJFDLK';
  $key = trim(publickey);
  if (!$key) throw new \Exception('publickkey not found');
  $key = preg_replace('#^-----[^-]+-----|-----[^-]+-----$|\s|\r|\n#', "", $key);
  $chunk = str_split($key, 64);
  $key = "-----BEGIN PUBLIC KEY-----\n".join("\n", $chunk)."\n-----END PUBLIC KEY-----";
  echo ("<script>console.log(" . json_encode($key) . ");</script>");
  return openssl_get_publickey($key);
}

//转换privatekey格式
 function getPrivateKey() {
  // 私钥
  $privatekey = 'WLEKOWQKFJDSSHFIHDFD';
  $key = trim( privatekey);
  if (!$key) throw new \Exception('privatekey not found');
  $key = preg_replace('#^-----[^-]+-----|-----[^-]+-----$|\s|\r|\n#', "", $key);
  $chunk = str_split($key, 64);
  $pad = strlen(end($chunk)) % 4;
  if ($pad > 2) {
    $pad = 4 - $pad;
  }
  $key = "-----BEGIN PRIVATE KEY-----\n".join("\n", $chunk).str_repeat('=', $pad)."\n-----END PRIVATE KEY-----";
  echo ("<script>console.log(" . json_encode($key) . ");</script>");
  return openssl_get_privatekey($key);
}
 */

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;
}
/**
 *生成格式化RSA PKCS#8格式的私钥
 * @param {string} key  私钥字符串
 * @return {string}  返回PKCS8格式的RSA私钥
 */
const getPrivateKey = function (key) {
  let arr = []
  for (var i = 0; i < key.length; i += 64) { arr.push(key.substring(i, i + 64)) }
  let endStr = arr.pop()
  let pend = endStr.length % 4
  pend = pend > 2 ? 4-pend : pend
  arr.push(endStr += '='.repeat(pend))
  return '-----BEGIN PRIVATE KEY-----\n' + arr.join('\n') + '\n-----END PRIVATE KEY-----';
};
/**
 *生成格式化RSA PKCS#8格式的公钥
 * @param {string} key 公钥字符串
 * @return {string}  返回PKCS8格式的RSA公钥
 */
const getPublicKey = function (key) {
  const result = insertStr(key, '\n', 64);
  return '-----BEGIN PUBLIC KEY-----\n' + result + '-----END PUBLIC KEY-----';
};
//存入文件
// const { writeFileSync } = require('fs')
// const key = require('../config/keys');
// let domain = 'zzjtnb'
// writeFileSync(`public/pem/${domain}/private.pem`, getPrivateKey(key[domain].private_key))
// writeFileSync(`public/pem/${domain}/public.pem`, getPublicKey(key[domain].public_key))
module.exports = {
  getPrivateKey, getPublicKey
};

2.加密签名

encrypt.js

js
/**
 * 对应PHP的签名加密
 * 1.将接口的参数转成jsonstr
 * 2.以系统公钥对jsonstr进行加密
 * 3.以客户端私钥对jsonstr进行签名
<? php
include('config.php');// include clientprivatekey, systempublickey, apikey
//签名
openssl_sign($jsonstr, $signature, $clientprivatekey);
$postdata['sign'] = base64_encode($signature);
//加密
$chunk = str_split($jsonstr, 117);
$output = '';
foreach($chunk as $str){
if (!openssl_public_encrypt($str, $crypted, $systempublickey))
throw new Exception('encrypt err');
$output.= $crypted;
}
$postdata['data'] = base64_encode($output);
echo "请求的资料: ".http_build_query($postdata);
*/

const crypto = require('crypto');
/**
 * 创建签名(使用私钥和数据)
 * @param data
 * @param privateKey
 * @returns {string}
 */
function createSign(data, privateKey) {
  // 'RSA-SHA1'--签名算法的名称
  const signer = crypto.createSign('RSA-SHA1');
  signer.update(data);
  signer.end();
  return signer.sign(privateKey, 'base64')
}
/**
 * 加密数据(使用公钥和数据)
 * @param {string} data
 * @param {string} publicKey
 * @returns {string}
 */

function publicEncrypt(data, publicKey) {
  const MAX_ENCRYPT_BLOCK = 117;
  //得到公钥
  // var publicPem = fs.readFileSync(path.join(__dirname, "../../properties/rsa_public_key.pem"));//替换你自己的路径
  // var publicKey = publicPem.toString();
  var bufferToEncrypt = Buffer.from(data, 'utf8'); //加密信息用bufferToEncrypt封装
  var inputLen = bufferToEncrypt.byteLength;
  var bufs = []; //密文
  var offSet = 0;  //开始长度
  var endOffSet = MAX_ENCRYPT_BLOCK;  //结束长度
  //分段加密
  while (inputLen - offSet > 0) {
    if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
      var bufTmp = bufferToEncrypt.slice(offSet, endOffSet);
      bufs.push(crypto.publicEncrypt({ key: publicKey, padding: crypto.constants.RSA_PKCS1_PADDING }, bufTmp));
    } else {
      var bufTmp = bufferToEncrypt.slice(offSet, inputLen);
      bufs.push(crypto.publicEncrypt({ key: publicKey, padding: crypto.constants.RSA_PKCS1_PADDING }, bufTmp));
    }
    offSet += MAX_ENCRYPT_BLOCK;
    endOffSet += MAX_ENCRYPT_BLOCK;
  }
  const result = Buffer.concat(bufs);
  const base64Str = result.toString("base64");  //密文BASE64编码
  return base64Str;
}

// 下面这种报错 Error: error:0406D06E:rsa routines:RSA_padding_add_PKCS1_type_2:data too large for key size
// function publicEncrypt(data, publicKey) {
//   var bufferToEncrypt = Buffer.from(data);
//   var encrypted = crypto.publicEncrypt({ "key": publicKey, padding: crypto.constants.RSA_PKCS1_PADDING }, bufferToEncrypt).toString("base64")
//   return encrypted
// }
module.exports = {
  createSign, publicEncrypt
};