2016-09-14 192 views
2

我有用於使用jsrsasign的ECDSA簽名和在JWK格式的密鑰產生一個級聯(R-S)碼簽名:用的Node.js /加密生成ECDSA簽名

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代碼來實現同樣的事情:

importEcdsaKey(key, 'sign') // importKey JWK -> raw 
.then((privateKey) => subtle.sign(
    { name: 'ECDSA', hash: {name: 'SHA-256'} }, 
    privateKey, 
    dataBuffer 
)) 

這些都返回128字節的緩衝區;並且它們交叉驗證(即,我可以驗證jsrsasign簽名與SubtleCrypto,反之亦然)。但是,當我在Node.js crypto模塊中使用Sign類時,我似乎得到了完全不同的東西。

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簽名的無效長度)。

如何獲得由jsrsasignSubtleCrypto生成的r-s簽名,使用節點crypto

+0

你確定'要求( 'JWK到PEM')(鍵,{ '私人' :true});'產生加密模塊可以理解的EC私鑰的有效編碼?它是否以'----- BEGIN EC PRIVATE KEY ------'開頭,如[示例](https://nodejs.org/api/crypto.html#crypto_class_sign)中的? –

+0

是的:至少,它確實生成一個帶有「EC PRIVATE KEY」部分的PEM字符串,並且'crypto'模塊不會發出任何類型的錯誤或警告,它只是發出與我預期不同的簽名。 –

回答

2

答案結果是節點crypto模塊生成ASN.1/DER簽名,而其他API如jsrsasignSubtleCrypto產生「連接」簽名。在這兩種情況下,簽名是連接的(r, s)。不同之處在於ASN.1在最小字節數加上一些有效載荷長度數據的情況下這樣做;而P1363格式使用兩個32位的十六進制編碼整數,如果需要則填零。

以下解決方案假定「規範」格式是由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')); 
} 

想通了感謝