首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >无法在Node.js中验证RSA-PSS签名

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

Stack Overflow用户
提问于 2020-09-26 17:29:15
回答 1查看 1.3K关注 0票数 2

我在JavaScript中有一个客户机,在Node.JS中有一个服务器,我试图在客户机中签名一个简单的文本,并将签名连同publicKey一起发送到服务器,这样服务器就可以验证publicKey了。

客户端的任何东西都可以!但我无法在服务器端验证签名。我认为您没有必要阅读客户端代码,但为了保证我也会提供它。

客户代码:

代码语言:javascript
复制
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);
})

服务器代码(为此目的使用快件):

代码语言:javascript
复制
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);
    }
})

服务器控制台日志:

代码语言:javascript
复制
rsa undefined public
false

注:

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

为什么要验证服务器中的公钥?你为什么不在客户端使用“X”库呢?

  1. 请随时更改导出格式(Pem)、公钥格式(‘spki’)、算法格式(RSA-PSS)等。
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-09-27 08:16:35

核查失败有两个原因:

  • 必须显式指定PSS填充,因为PKCS#1 v1.5填充是默认的s. 这里
  • 签名的转换破坏了数据:行: 设signature = Buffer.from(req.body.signature,'base64').toString(); 执行UTF8解码s. 这里,这将不可逆转地更改数据s. 这里。签名由通常不兼容UTF8的二进制数据组成。只有在适当的二进制编码到文本编码(如Base64、十六进制等)和s. 这里的情况下,才有可能转换到字符串。 但除此之外,实际上根本不需要转换,因为签名可以直接作为缓冲区s. 这里传递。

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

代码语言:javascript
复制
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
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64080651

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档