繁体   English   中英

无法在 Node.js 中验证 RSA-PSS 签名

[英]Unable to verify RSA-PSS signature in Node.js

我有一个JavaScript客户端和一个Node.JS服务器。 我试图在客户端签署一个简单的文本,并将签名与公钥一起发送到服务器,然后服务器可以验证公钥。

客户端什么都行! 但我无法在服务器端验证签名。 我认为您不需要阅读客户端代码,但只是为了保证我也会提供它。

客户端代码:

 let privateKey = 0; let publicKey = 0; let encoded = ''; let signatureAsBase64 = ''; let pemExported = '' function ab2str(buf) { return String.fromCharCode.apply(null, new Uint8Array(buf)); } function str2ab(str) { const buf = new ArrayBuffer(str.length); const bufView = new Uint8Array(buf); for (let i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf; } let keygen = crypto.subtle.generateKey({ name: 'RSA-PSS', modulusLength: 4096, publicExponent: new Uint8Array([1,0,1]), hash: 'SHA-256' }, true, ['sign', 'verify']); keygen.then((value)=>{ publicKey = value.publicKey; privateKey = value.privateKey; let exported = crypto.subtle.exportKey('spki', publicKey); return exported }).then((value)=>{ console.log('successful'); const exportedAsString = ab2str(value); const exportedAsBase64 = btoa(exportedAsString); pemExported = `-----BEGIN PUBLIC KEY-----\\n${exportedAsBase64}\\n-----END PUBLIC KEY-----`; //signing: encoded = new TextEncoder().encode('test'); let signing = crypto.subtle.sign({ name: "RSA-PSS", saltLength: 32 }, privateKey, encoded); return signing; }).then((signature)=>{ const signatureAsString = ab2str(signature); signatureAsBase64 = btoa(signatureAsString); //verifying just to be sure everything is OK: return crypto.subtle.verify({ name: 'RSA-PSS', saltLength: 32 }, publicKey, signature, encoded) }).then((result)=>{ console.log(result); //send information to server: let toSend = new XMLHttpRequest(); toSend.onreadystatechange = ()=>{ console.log(this.status); }; toSend.open("POST", "http://127.0.0.1:3000/authentication", true); let data = { signature: signatureAsBase64, publicKey: pemExported }; toSend.setRequestHeader('Content-Type', 'application/json'); toSend.send(JSON.stringify(data)); //to let you see the values, I'll print them to console in result: console.log("signature is:\\n", signatureAsBase64); console.log("publicKey is:\\n", pemExported); }).catch((error)=>{ console.log("error",error.message); })

服务器代码(我为此使用 express):

const express = require('express');
const crypto = require('crypto');
const router = express.Router(); 

function str2ab(str) {
  const buf = new ArrayBuffer(str.length);
  const bufView = new Uint8Array(buf);
  for (let i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return buf;
}

router.post('/authentication',  async (req, res)=>{
    try{
        const publicKey = crypto.createPublicKey({
            key: req.body.publicKey,
            format: 'pem',
            type: 'spki'
        });
        console.log(publicKey.asymmetricKeyType, publicKey.asymmetricKeySize, publicKey.type);
        let signature = Buffer.from(req.body.signature, 'base64').toString();
        signature = str2ab(signature);
        const result = crypto.verify('rsa-sha256', new TextEncoder().encode('test'),
                        publicKey, new Uint8Array(signature));
        console.log(result);
    }catch(error){
        console.log('Error when autheticating user: ', error.message);
    }
})

服务器控制台日志:

rsa undefined public
false

笔记:

  1. 我认为公钥在服务器中正确导入,因为当我再次在服务器中导出公钥时,双方(客户端和服务器)的pem格式pem 所以我认为问题与服务器中的“验证”或“转换签名”有关
  2. 如果可能的话,我更喜欢使用内置的crypto模块,所以其他库,比如微妙的加密是我的第二个选择,我在这里看看这是否可以用 crypto 来完成
  3. 我想学习如何验证由JavaScript SubtleCrypto签名的签名,因此,请不要问一些问题,例如:

为什么要验证服务器中的公钥?

为什么不在客户端使用“X”库?

  1. 随意更改导出格式(pem)、公钥格式('spki')、算法格式(RSA-PSS)等。

验证失败有两个原因:

  • PSS 填充必须明确指定,因为 PKCS#1 v1.5 填充是默认值,s。 在这里

  • 签名的转换破坏了数据: 该行:

     let signature = Buffer.from(req.body.signature, 'base64').toString();

    执行 UTF8 解码,s。 这里,它不可逆转地改变了数据,s。 在这里 签名由二进制数据组成,通常与UTF8 不兼容 只有使用合适的二进制到文本编码(如 Base64、十六进制等),才能转换为字符串。 在这里
    但除此之外,实际上根本不需要转换,因为签名可以直接作为缓冲区传递,s。 在这里

以下 NodeJS 代码执行成功验证(针对使用客户端代码生成的签名和公钥):

const publicKey = crypto.createPublicKey(
    {
        key: req.body.publicKey,
        format: 'pem',
        type: 'spki'
    });

const result = crypto.verify(
    'rsa-sha256', 
    new TextEncoder().encode('test'), 
    {
        key: publicKey, 
        padding: crypto.constants.RSA_PKCS1_PSS_PADDING
    }, 
    Buffer.from(req.body.signature, 'base64'));

console.log(result); // true

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM