杨新春

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

签名是私钥签名,公钥验签,私钥可以分解为模数和指数两个参数,这两个参数也可以签名,公钥分解的模数和私钥是一样的

1、使用pem签名和验签,需要安装 npm install jsrsasign

const jsrsasign = require('jsrsasign');

// 定义消息
const message = "Hello, RSA";

// 使用生成的密钥对
const privateKeyPEM = `-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDt8la68mvGoMEH
idIp6rydcViQxk1NjStbUR6hDzr0XOCxdxo8+b1itGbZ552PiYLjsrhTqRj3arEH
RgHVcY4Ck9TRGUSq3w7S8U8sjm2csG9po6Zmq/FXZgMRxtVjeblxeyzhEQssWIV2
6cYcrPsTwXGdepw2Wp0kwk1GqKmjLtsoedLSN8y/k3W3GEwFH3MePQLoLr7LRHfr
QeM93xrl4x+lXve9VFxBprT/F8CUUnxUp206erLMEMhYOByBzz/LgTxVDayEgRFf
RMwTWXfl3WFCPJAHFH3rY/R/u04cqxdFyqhG5wAdK7JUer19BYTWFwFnKZovsECO
WWGde+yNAgMBAAECggEBANOHjWyyV/eDLzbZYbkb0cORYO+LKmuX5QRQEA+UL4/P
BX0eaXfteFxIDesHSX2x/GcZmV3eRLCm62uQ485xHZLTAu1ELlRM6avDE+SDVZRn
ERRzhFlf37QXnwDtpwPA0Ye9vb7zXOB2bWeyGQmjCa2dWbhqqHZ1hZ+JEBORmwTR
yu6oJ42W9qMX04JQ3W0VTHxXml13rGRyt19+DvQ6K2WqR2GYuHcYYk9v+IHS/6Yf
YjyGrC9s5xhF/BumdAofKLPTq/ZUJhfAyb2KmlViE1jlZHbUxv8koR5HKKOLy+3m
m17nXVartMn6txhO1Q1j4yiulv1Gg67P5VCycRmPGC0CgYEA+bhj6ghW71Ey+uQy
9b1E9fbaIiTnh7dLvF13Psr2wsHhjG0jR3/nf+gcEPDEXDkAtkljsMUvOgu1R/C9
qR3sTisYGwu43P/iNLdquFiooikggWby/DRNmdBR4pG0+dngpPL29ej0F0wVCE39
WxggckExkDLSUQ596mpkhI5FtrsCgYEA8+4nbofOwHctEk15Y+lBfbImJSapdi80
jJk0kSqpG3Jf+vsIZZK7PU8awrNkyA52pqqs9yYPu5ILSp09t8xTvBcFbR5Sd30a
WEwsa+rUcNrmRNfnKBLTZ3BjV5pIFWSA9tTuPo27z5DRwt55SHxqkJzgfLjGRj7q
TOSQpmXWyVcCgYBNxQkIx5Qbb9RKomrkcQ9a8oe4fnnEoGbrwMf0PE1hFfRiQVLt
X33/LZAgDnt4pR/9pRlw4df1YdQRFX5bfsArD+yv75Sg7ufJtx17T9Ekv14qLp+Z
v8gzENePkgUkkqANfsU+jFAIOV+LeI5gokDolDkVSV6Kc9LvADdRuOUpJQKBgB6e
/941ODQbAdJxXVK4flssGPPC9bT7WgpoVRwAEfh7FCdqifRORij9HCvi3zzp6+Ui
OdJQudrfC4HTWWMQSFatQSYgj1Ompb792Pm3IfIE2LE9tSvnqRgsGJxvpqrg0fuq
YToDCteUo3BmAy2hSLNcriWaAHHjBgCfFLV+xUbDAoGAfzD8QYPfp7Vht+MHRohd
F3CmCWa7Z0jwEU0Twjo1MPzV9oN1Y34qJ8IFWhKM1LDUUSbIeqUwBX8sMns9VAgA
g/OFg56demMzS5i0k+f3n1zszZBNXJjUZsOW3oPl6STOAfi2ryA2F1D4EEbWTknU
JR4c+nTC/wkLhzpRtU/Kv7Y=
-----END PRIVATE KEY-----
`;
const publicKeyPEM = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7fJWuvJrxqDBB4nSKeq8
nXFYkMZNTY0rW1EeoQ869FzgsXcaPPm9YrRm2eedj4mC47K4U6kY92qxB0YB1XGO
ApPU0RlEqt8O0vFPLI5tnLBvaaOmZqvxV2YDEcbVY3m5cXss4RELLFiFdunGHKz7
E8FxnXqcNlqdJMJNRqipoy7bKHnS0jfMv5N1txhMBR9zHj0C6C6+y0R360HjPd8a
5eMfpV73vVRcQaa0/xfAlFJ8VKdtOnqyzBDIWDgcgc8/y4E8VQ2shIERX0TME1l3
5d1hQjyQBxR962P0f7tOHKsXRcqoRucAHSuyVHq9fQWE1hcBZymaL7BAjllhnXvs
jQIDAQAB
-----END PUBLIC KEY-----
`;

// 加载密钥
const privateKey = jsrsasign.KEYUTIL.getKey(privateKeyPEM);
const publicKey = jsrsasign.KEYUTIL.getKey(publicKeyPEM);

// 初始化签名对象
const sig = new jsrsasign.KJUR.crypto.Signature({ alg: "SHA256withRSA" });
sig.init(privateKey); // 使用私钥初始化签名对象
sig.updateString(message); // 更新要签名的消息
const signature = sig.sign(); // 获取签名
console.log("签名: ", signature);

// 验证签名
const sigVerify = new jsrsasign.KJUR.crypto.Signature({ alg: "SHA256withRSA" });
sigVerify.init(publicKey); // 使用公钥初始化签名对象
sigVerify.updateString(message); // 更新要验证的消息
const isValid = sigVerify.verify(signature); // 验证签名
console.log("签名是否有效: ", isValid);

// 提取私钥的相关参数
const modulus = privateKey.n.toString(16);  // 模数
const privateExponent = privateKey.d.toString(16);  // 私有指数
const publicExponent = privateKey.e.toString(16);  // 公共指数

// 如果使用 CRT 优化,提取系数
const coefficient = privateKey.coefficient ? privateKey.coefficient.toString(16) : null;

console.log("模数 (Modulus): ", modulus);
console.log("私有指数 (Private Exponent): ", privateExponent);
console.log("公共指数 (Public Exponent): ", publicExponent);
console.log("系数 (Coefficient): ", coefficient);

2、使用模数和指数签名和验签

const crypto = require('crypto');

// 模数(modulus)和私钥指数(private exponent)
const modulus = "edf256baf26bc6a0c10789d229eabc9d715890c64d4d8d2b5b511ea10f3af45ce0b1771a3cf9bd62b466d9e79d8f8982e3b2b853a918f76ab1074601d5718e0293d4d11944aadf0ed2f14f2c8e6d9cb06f69a3a666abf157660311c6d56379b9717b2ce1110b2c588576e9c61cacfb13c1719d7a9c365a9d24c24d46a8a9a32edb2879d2d237ccbf9375b7184c051f731e3d02e82ebecb4477eb41e33ddf1ae5e31fa55ef7bd545c41a6b4ff17c094527c54a76d3a7ab2cc10c858381c81cf3fcb813c550dac8481115f44cc135977e5dd61423c9007147deb63f47fbb4e1cab1745caa846e7001d2bb2547abd7d0584d6170167299a2fb0408e59619d7bec8d"; // 用十六进制表示
const privateExponent = "d3878d6cb257f7832f36d961b91bd1c39160ef8b2a6b97e50450100f942f8fcf057d1e6977ed785c480deb07497db1fc6719995dde44b0a6eb6b90e3ce711d92d302ed442e544ce9abc313e48355946711147384595fdfb4179f00eda703c0d187bdbdbef35ce0766d67b21909a309ad9d59b86aa87675859f891013919b04d1caeea8278d96f6a317d38250dd6d154c7c579a5d77ac6472b75f7e0ef43a2b65aa476198b87718624f6ff881d2ffa61f623c86ac2f6ce71845fc1ba6740a1f28b3d3abf6542617c0c9bd8a9a55621358e56476d4c6ff24a11e4728a38bcbede69b5ee75d56abb4c9fab7184ed50d63e328ae96fd4683aecfe550b271198f182d"; // 用十六进制表示


// 分块模幂运算函数
function modExp(base, exp, mod) {
  let result = 1n;
  let b = base % mod;
  let e = exp;

  while (e > 0n) {
      if (e % 2n === 1n) {
          result = (result * b) % mod;
      }
      b = (b * b) % mod;
      e = e / 2n;
  }

  return result;
}

// 转换为 BigInt
const N = BigInt("0x" + modulus);
const d = BigInt("0x" + privateExponent);

// 要签名的消息
const message = "Hello, RSA";

// 计算消息的 SHA-256 哈希值
const hash = crypto.createHash('sha256').update(message).digest();

// ASN.1 前缀(SHA-256)
const asn1Prefix = Buffer.from("3031300d060960864801650304020105000420", "hex");

// 构造填充后的数据
const paddedData = Buffer.concat([
    Buffer.from([0x00, 0x01]), // 固定的 PKCS#1 v1.5 填充头部
    Buffer.alloc(256 - asn1Prefix.length - hash.length - 3, 0xff), // 填充 0xFF
    Buffer.from([0x00]), // 填充结束
    asn1Prefix,
    hash,
]);

// 转换填充后的数据为 BigInt
const paddedBigInt = BigInt("0x" + paddedData.toString("hex"));

// 对填充后的数据进行签名 (paddedBigInt^d % N)
const signature = modExp(paddedBigInt, d, N);

console.log("签名 (Signature): ", signature.toString(16));  // 输出签名(十六进制表示)




// 公钥信息(公钥模数和公钥指数)
const publicExponent = "010001";  // 公钥指数(通常为 65537)
const e = BigInt("0x" + publicExponent);

// 签名(假设你已经得到签名值)
const signatureHex = signature.toString(16);  // 这里是签名的十六进制字符串
const signatureBigInt = BigInt("0x" + signatureHex);

// 计算恢复出的填充数据 (S^e % N)
const recoveredPaddedBigInt = modExp(signatureBigInt, e, N);

// 转换恢复的填充数据为 Buffer
const recoveredPaddedData = Buffer.from(
    recoveredPaddedBigInt.toString(16).padStart(512, "0"), // 确保对齐为 256 字节(2048 位密钥)
    "hex"
);

// 检查填充格式
if (
    recoveredPaddedData[0] === 0x00 &&
    recoveredPaddedData[1] === 0x01 &&
    recoveredPaddedData.includes(0x00, 2)
) {
    const separatorIndex = recoveredPaddedData.indexOf(0x00, 2);
    const recoveredHash = recoveredPaddedData.slice(separatorIndex + 1);

    // 比较恢复的哈希值与原始哈希值
    const hashForVerification = crypto.createHash('sha256').update(message).digest();
    const isValid = recoveredHash.equals(
        Buffer.concat([asn1Prefix, hashForVerification])
    );

    console.log("签名是否有效: ", isValid);  // 如果匹配,返回 true,否则返回 false
} else {
    console.log("签名无效: 填充格式不匹配");
}

 

posted on 2024-12-14 21:37  杨新春  阅读(215)  评论(0)    收藏  举报