Joh*_*ohn 2 javascript openssl cryptography elliptic-curve node.js
首先,我们使用椭圆加密包。符号函数如下所示:
signByPriv = function (privKeyData, text) {
let msgHash = getmsgHash(text, "SHA-384");
let key = ec.keyFromPrivate(Buffer.from(privKeyData,'base64').toString('hex'), 'hex')
let signature = key.sign(msgHash);
return signature
}
Run Code Online (Sandbox Code Playgroud)
然后我们想把它改成 nodejs 版本,因为 nodejs 在引擎盖下使用 openssl?所以它会更快
首先我的符号功能如下:
signByPriv = function (privKeyData, text) {
const sign1 = crypto.createSign('SHA384'); //hash do inside
sign1.write(text);
sign1.end();
const signature = sign1.sign(privKeyData, 'hex');
return signature;
}
Run Code Online (Sandbox Code Playgroud)
它会抱怨错误:
internal/crypto/sig.js:86 const ret = this[kHandle].sign(data, format, type, passphrase, rsaPadding,
错误:错误:0909006C:PEM 例程:get_name:无起始行
所以我检查了nodejs docs,发现它需要以pem格式传递privKey?
signByPriv = function (privKeyData, text) {
let key = turnBase64PrivToPemKey(privKeyData) //base64 => pem
const sign1 = crypto.createSign('SHA384'); //hash do inside
sign1.write(text);
sign1.end();
const signature = sign1.sign(privKeyData, 'hex');
return signature;
}
turnBase64PrivToPemKey = function (base64Priv) {
var key_hex = Buffer.from(base64Priv, 'base64').toString('hex');
ecdh.setPrivateKey(key_hex, 'hex')
var pubKey_hex = ecdh.getPublicKey().toString('hex');
//pem???????????????????????==????????????
var mykey = '308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b0201010420' + key_hex + 'a144034200' + pubKey_hex;
privKey = '-----BEGIN PRIVATE KEY-----\n' + Buffer.from(mykey, 'hex').toString('base64') + '\n-----END PRIVATE KEY-----';
pubKey = crypto.createPublicKey(privKey); //???????
let Key = {
privKey,
pubKey
}
return Key;
}
Run Code Online (Sandbox Code Playgroud)
太好了,签名和验证功能都完美无缺?
但后端可能会做同样愚蠢的事情......
我们选择的曲线是 prime256v1
const ecdh = crypto.createECDH('prime256v1')
所以,我想知道为什么 nodejs sign func 不能只接受 base64 priv?
因为pem格式只由私钥、公钥等固定字符串组成。
Sign并且Verify不只是支持PEM格式,而且还DER格式(以两者中描述的答案@Maarten Bodewes的)。此外,Pkcs8-(RFC 5208和此处)和 Sec1-EC-keys(SECG SEC1,C.4 节和此处)都可以使用。但是,不直接支持原始EC 密钥。因此,如果密钥仅可用作原始密钥,则始终需要进行转换。但是这种转换比发布的代码更容易实现,因此(在我看来)没有显着的额外工作,例如签名:
var buf1 = Buffer.from('308141020100301306072a8648ce3d020106082a8648ce3d030107042730250201010420', 'hex'); // specific byte-sequence for curve prime256v1
var buf2 = Buffer.from('<Raw private key as hex string>', 'hex'); // raw private key (32 bytes)
var privateKeyPkcs8Der = Buffer.concat([buf1, buf2], buf1.length + buf2.length);
var sign = crypto.createSign('SHA384');
sign.write(<data to sign>);
sign.end();
var signature = sign.sign({ key: privateKeyPkcs8Der, format: 'der', type: 'pkcs8' }); // specify format and type
Run Code Online (Sandbox Code Playgroud)
用于签名privateKeyPkcs8Der的密钥是 DER 格式的 Pkcs8 密钥(没有原始公钥)。
与发布的代码相反:
buf1包含属于 prime256v1 (secp256r1) 的字节序列和没有原始公钥的 Pkcs8 容器。
注意:字节序列与发布代码中使用的字节序列略有不同。这是因为字节序列还包含长度信息,这取决于是否嵌入了原始公钥。这同样适用于验证:
var buf1 = Buffer.from('3059301306072a8648ce3d020106082a8648ce3d030107034200', 'hex'); // specific byte-sequence for curve prime256v1
var buf2 = Buffer.from('<Raw public key as hex string>', 'hex'); // raw public key (uncompressed, 65 bytes, startting with 04)
var publicKeyX509Der = Buffer.concat([buf1, buf2], buf1.length + buf2.length);
var verify = crypto.createVerify('SHA384');
verify.write(<data to sign>);
verify.end();
var verified = verify.verify({ key: publicKeyX509Der, format: 'der', type: 'spki' }, signature); // specify format and type
Run Code Online (Sandbox Code Playgroud)
用于验证的密钥publicKeyX509Der是DER 格式的 X.509-SubjectPublicKeyInfo 密钥(SECG SEC1,C.3 节)。
与签名一样:
buf1 包含属于 prime256v1 的字节序列。在发布的代码中,ECDH-class的方法用于从原始私钥派生原始公钥。相反,createPublicKey- 和export- 方法可用于从 Pkcs8 密钥派生 X.509-SubjectPublicKeyInfo 密钥:
var publicKey = crypto.createPublicKey({ key: privKeyPkcs8DER, type: 'pkcs8', format: 'der' });
var publicKeyX509Der = publicKey.export({type: 'spki', format: 'der'})
Run Code Online (Sandbox Code Playgroud)
这privateKeyPkcs8Der是一个 Pkcs8 密钥(带有或不带有原始公钥)和publicKeyX509Der一个 X.509-SubjectPublicKeyInfo 密钥,均为 DER 格式。
注意:也可以使用 Sec1 容器代替 Pkcs8 容器。但是,密钥的结构和字节序列必须相应地进行调整。此处描述了 Sec1 容器的使用,但对于不同的曲线 (secp256 k 1),因此不能简单地复制字节序列。