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

问题描述

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

客户端中的任何内容都可以!但我无法在服务器端验证签名。我认为您无需阅读客户端代码,只是为了确保我也会提供它。

客户代码:

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,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',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'),new Uint8Array(signature));
        console.log(result);
    }catch(error){
        console.log('Error when autheticating user: ',error.message);
    }
})

服务器控制台日志:

rsa undefined public
false

注意:

  1. 我认为公钥已正确导入服务器,因为当我导出 再次在服务器中使用公钥,双方(客户端和服务器)的pem格式完全相同 等于。所以我认为该问题与服务器中的“验证”或“转换签名”有关。
  2. 如果可能的话,我更喜欢使用内置的crypto模块,因此,第二种选择是使用subtle-crypto之类的其他库,并且我在这里看看是否可以完成此操作是否使用密码
  3. 因此,我想学习如何验证由JavaScript SubtleCrypto签名的签名,请不要问一些问题,例如:

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

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

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

解决方法

验证失败有两个原因:

  • 必须明确指定PSS填充,因为默认情况下为PKCS#1 v1.5填充s。 here

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

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

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

以下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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...