签名是私钥签名,公钥验签,私钥可以分解为模数和指数两个参数,这两个参数也可以签名,公钥分解的模数和私钥是一样的
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("签名无效: 填充格式不匹配"); }

浙公网安备 33010602011771号