无法验证 Node.js 中的 RSA-PSS 签名
Unable to verify RSA-PSS signature in Node.js
我在 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,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
注意:
- 我认为 public 密钥已正确导入服务器,因为当我导出
public 再次在服务器中键入,双方(客户端和服务器)的
pem
格式完全
平等的。所以 我认为问题与服务器 . 中的 'verification' 或 'converting signature' 有关
- 我更喜欢使用内置 crypto module if it's possible, so other libraries such as subtle-crypto 是我的第二个选择,我来这里是想看看这是否可以用加密来完成。
- 我想了解如何验证由 JavaScript SubtleCrypto 签署的签名,因此,请不要问一些问题,例如:
Why do you want to verify the public key in server?
Why don't you use 'X' library in client?
- 随意更改导出格式(pem)、Public密钥格式('spki')、算法格式(RSA-PSS)等。
验证失败有两个原因:
必须明确指定 PSS 填充,因为 PKCS#1 v1.5 填充是默认值,s。 here.
签名的转换破坏了数据:行:
let signature = Buffer.from(req.body.signature, 'base64').toString();
执行 UTF8 解码,s。 此处,不可逆地改变了数据,s。 这里。签名由通常 UTF8 不兼容 的二进制数据组成。只有使用合适的 binary-to-text 编码(如 Base64、十六进制等),才能转换为字符串,s。 此处.
但除此之外实际上根本不需要转换,因为签名可以直接作为缓冲区传递,s。 here.
以下 NodeJS 代码执行成功验证(对于签名和使用客户端代码生成的 public 密钥):
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
我在 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,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
注意:
- 我认为 public 密钥已正确导入服务器,因为当我导出
public 再次在服务器中键入,双方(客户端和服务器)的
pem
格式完全 平等的。所以 我认为问题与服务器 . 中的 'verification' 或 'converting signature' 有关
- 我更喜欢使用内置 crypto module if it's possible, so other libraries such as subtle-crypto 是我的第二个选择,我来这里是想看看这是否可以用加密来完成。
- 我想了解如何验证由 JavaScript SubtleCrypto 签署的签名,因此,请不要问一些问题,例如:
Why do you want to verify the public key in server?
Why don't you use 'X' library in client?
- 随意更改导出格式(pem)、Public密钥格式('spki')、算法格式(RSA-PSS)等。
验证失败有两个原因:
必须明确指定 PSS 填充,因为 PKCS#1 v1.5 填充是默认值,s。 here.
签名的转换破坏了数据:行:
let signature = Buffer.from(req.body.signature, 'base64').toString();
执行 UTF8 解码,s。 此处,不可逆地改变了数据,s。 这里。签名由通常 UTF8 不兼容 的二进制数据组成。只有使用合适的 binary-to-text 编码(如 Base64、十六进制等),才能转换为字符串,s。 此处.
但除此之外实际上根本不需要转换,因为签名可以直接作为缓冲区传递,s。 here.
以下 NodeJS 代码执行成功验证(对于签名和使用客户端代码生成的 public 密钥):
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