问题描述
我的代码使用 jsrsasign
和 JWK 格式的密钥为 ECDSA 签名生成串联 (r-s) 签名:
I have code that generates a concatenated (r-s) signature for the ECDSA signature using jsrsasign
and a key in JWK format:
const sig = new Signature({ alg: 'SHA256withECDSA' });
sig.init(KEYUTIL.getKey(key));
sig.updateHex(dataBuffer.toString('hex'));
const asn1hexSig = sig.sign();
const concatSig = ECDSA.asn1SigToConcatSig(asn1hexSig);
return new Buffer(concatSig, 'hex');
似乎有效.我也有使用 SubtleCrypto
来实现相同目的的代码:
Seems to work. I also have code that uses SubtleCrypto
to achieve the same thing:
importEcdsaKey(key, 'sign') // importKey JWK -> raw
.then((privateKey) => subtle.sign(
{ name: 'ECDSA', hash: {name: 'SHA-256'} },
privateKey,
dataBuffer
))
它们都返回 128 字节的缓冲区;他们交叉验证(即我可以用 SubtleCrypto
验证 jsrsasign
签名,反之亦然).但是,当我在 Node.js crypto
模块中使用 Sign
类时,我似乎得到了完全不同的东西.
These both return 128-byte buffers; and they cross-verify (i.e. I can verify jsrsasign
signatures with SubtleCrypto
and vice versa). However, when I use the Sign
class in the Node.js crypto
module, I seem to get something quite different.
key = require('jwk-to-pem')(key, {'private': true});
const sign = require('crypto').createSign('sha256');
sign.update(dataBuffer);
return sign.sign(key);
这里我得到一个可变长度的缓冲区,大约 70 个字节;它不会与 jsrsa
进行交叉验证(它会抱怨 r-s 签名的长度无效).
Here I get a buffer of variable length, roughly 70 bytes; it does not cross-verify with jsrsa
(which bails complaining about an invalid length for an r-s signature).
如何使用 Node crypto
获得由 jsrsasign
和 SubtleCrypto
生成的 r-s 签名?
How can I get an r-s signature, as generated by jsrsasign
and SubtleCrypto
, using Node crypto
?
推荐答案
答案原来是 Node crypto
模块生成 ASN.1/DER 签名,而其他 API 如 jsrsasign
和 SubtleCrypto
产生一个串联"签名.在这两种情况下,签名都是 (r, s)
的串联.不同之处在于 ASN.1 使用最少的字节数,加上一些有效载荷长度数据;而 P1363 格式使用两个 32 位十六进制编码的整数,必要时补零.
The answer turns out to be that the Node crypto
module generates ASN.1/DER signatures, while other APIs like jsrsasign
and SubtleCrypto
produce a "concatenated" signature. In both cases, the signature is a concatenation of (r, s)
. The difference is that ASN.1 does so with the minimum number of bytes, plus some payload length data; while the P1363 format uses two 32-bit hex encoded integers, zero-padding them if necessary.
以下解决方案假定规范"格式是 SubtleCrypto
使用的连接样式.
The below solution assumes that the "canonical" format is the concatenated style used by SubtleCrypto
.
const asn1 = require('asn1.js');
const BN = require('bn.js');
const crypto = require('crypto');
const EcdsaDerSig = asn1.define('ECPrivateKey', function() {
return this.seq().obj(
this.key('r').int(),
this.key('s').int()
);
});
function asn1SigSigToConcatSig(asn1SigBuffer) {
const rsSig = EcdsaDerSig.decode(asn1SigBuffer, 'der');
return Buffer.concat([
rsSig.r.toArrayLike(Buffer, 'be', 32),
rsSig.s.toArrayLike(Buffer, 'be', 32)
]);
}
function concatSigToAsn1SigSig(concatSigBuffer) {
const r = new BN(concatSigBuffer.slice(0, 32).toString('hex'), 16, 'be');
const s = new BN(concatSigBuffer.slice(32).toString('hex'), 16, 'be');
return EcdsaDerSig.encode({r, s}, 'der');
}
function ecdsaSign(hashBuffer, key) {
const sign = crypto.createSign('sha256');
sign.update(asBuffer(hashBuffer));
const asn1SigBuffer = sign.sign(key, 'buffer');
return asn1SigSigToConcatSig(asn1SigBuffer);
}
function ecdsaVerify(data, signature, key) {
const verify = crypto.createVerify('SHA256');
verify.update(data);
const asn1sig = concatSigToAsn1Sig(signature);
return verify.verify(key, new Buffer(asn1sig, 'hex'));
}
感谢
- https://crypto.stackexchange.com/questions/1795/how-can-i-convert-a-der-ecdsa-signature-to-asn-1
- ECDSA 签名在 Node.js 和WebCrypto 似乎不兼容?
这篇关于使用 Node.js/crypto 生成 ECDSA 签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!