NodeJS RSA 预散列符号
NodeJS RSA prehashed sign
我是 运行 NodeJS 8.12.0,必须在散列上设置签名,而无需重新散列,执行原始签名。或者换句话说,用私钥加密散列值。
const crypto = require('crypto');
// 4096 bits key.
let pk = "<BASE64 DER>";
let pub = "<BASE64 DER>";
// Transform them to PEM.
pk = `-----BEGIN PRIVATE KEY-----\n${pk.replace('\n', '')}\n-----END PRIVATE KEY-----\n`;
pub = `-----BEGIN PUBLIC KEY-----\n${pub.replace('\n', '')}\n-----END PUBLIC KEY-----\n`;
// Load the data to sign and set the signature.
const fingerprint = Buffer.from('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824','hex');
const signature = crypto.privateEncrypt({
key: pk,
padding: crypto.constants.RSA_PKCS1_PADDING
},
fingerprint
);
// Unfortunately, the server is not able to verify the signature...
console.log(signature.toString('hex'));
所以我用我的私钥查看了消息散列的原始加密,最后得到了某种 EMSA encoding and followed these steps:
- 对消息应用哈希函数
- 将散列函数的算法 ID 和散列编码为 DigestInfo 的 ASN.1 值(附录 A.2.4)
- 生成一个八位字节串PS,由emLen - tLen - 0xff的3个八位字节组成
- 连接 PS、DER 编码值 T 和其他填充以形成编码消息 EM 作为
EM = 0x00 || 0x01 || PS || 0x00 || T
所以,解决这个问题
// 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
H = 2c f2 4d ba 5f b0 a3 0e 26 e8 3b 2a c5 b9 e2 9e 1b 16 1e 5c 1f a7 42 5e 73 04 33 62 93 8b 98 24
emLen = 512
T = 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 2c f2 4d ba 5f b0 a3 0e 26 e8 3b 2a c5 b9 e2 9e 1b 16 1e 5c 1f a7 42 5e 73 04 33 62 93 8b 98 24
PS = 04 06 02 00 33 ff ff ff
// 00010406020033ffffff003031300d0609608648016503040201050004202cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
EM = 00 01 04 06 02 00 33 ff ff ff 00 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 2c f2 4d ba 5f b0 a3 0e 26 e8 3b 2a c5 b9 e2 9e 1b 16 1e 5c 1f a7 42 5e 73 04 33 62 93 8b 98 24
但是当我把它放入 privateEncrypt
时,我也没有得到正确的输出。有人可以帮我吗?
您正在尝试 PKCS1
-填充 (RSASSA-PKCS1-V1_5
) 手动。但这不是必须的。连接 Hash-ID(此处为 SHA-256
)和您的数据 (fingerprint
) 就足够了,其余的由 implicitly
选定的填充 (crypto.constants.RSA_PKCS1_PADDING
) 完成, 即
// Signing
var fingerprint = Buffer.from('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824','hex');
var id = Buffer.from([0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20]);
var allData = Buffer.concat([id, fingerprint]);
var signature = crypto.privateEncrypt(privateKey, allData); // crypto.constants.RSA_PKCS1_PADDING by default
// Verifying with createVerify
var verify = crypto.createVerify('RSA-SHA256');
verify.update('<the signed data>');
var verified = verify.verify(publicKey, signature); // provides true
// Verifying with publicDecrypt
var decryptedFingerprint = crypto.publicDecrypt(publicKey, Buffer.from(signature)).slice(-32); // provides fingerprint
注意:如果你想手动填充,你必须在allData
-buffer之前设置字节序列0x00 || 0x01 || PS || 0x00
。 PS
由达到密钥长度(以字节为单位)所需的 0xff
字节组成。此外,国旗
crypto.constants.RSA_NO_PADDING
必须在 privateEncrypt
调用中明确设置。但是,这不是必需的,因为结果是一样的。 RFC 8017, Section 8.2.1
中描述了详细信息
我是 运行 NodeJS 8.12.0,必须在散列上设置签名,而无需重新散列,执行原始签名。或者换句话说,用私钥加密散列值。
const crypto = require('crypto');
// 4096 bits key.
let pk = "<BASE64 DER>";
let pub = "<BASE64 DER>";
// Transform them to PEM.
pk = `-----BEGIN PRIVATE KEY-----\n${pk.replace('\n', '')}\n-----END PRIVATE KEY-----\n`;
pub = `-----BEGIN PUBLIC KEY-----\n${pub.replace('\n', '')}\n-----END PUBLIC KEY-----\n`;
// Load the data to sign and set the signature.
const fingerprint = Buffer.from('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824','hex');
const signature = crypto.privateEncrypt({
key: pk,
padding: crypto.constants.RSA_PKCS1_PADDING
},
fingerprint
);
// Unfortunately, the server is not able to verify the signature...
console.log(signature.toString('hex'));
所以我用我的私钥查看了消息散列的原始加密,最后得到了某种 EMSA encoding and followed these steps:
- 对消息应用哈希函数
- 将散列函数的算法 ID 和散列编码为 DigestInfo 的 ASN.1 值(附录 A.2.4)
- 生成一个八位字节串PS,由emLen - tLen - 0xff的3个八位字节组成
- 连接 PS、DER 编码值 T 和其他填充以形成编码消息 EM 作为
EM = 0x00 || 0x01 || PS || 0x00 || T
所以,解决这个问题
// 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
H = 2c f2 4d ba 5f b0 a3 0e 26 e8 3b 2a c5 b9 e2 9e 1b 16 1e 5c 1f a7 42 5e 73 04 33 62 93 8b 98 24
emLen = 512
T = 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 2c f2 4d ba 5f b0 a3 0e 26 e8 3b 2a c5 b9 e2 9e 1b 16 1e 5c 1f a7 42 5e 73 04 33 62 93 8b 98 24
PS = 04 06 02 00 33 ff ff ff
// 00010406020033ffffff003031300d0609608648016503040201050004202cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
EM = 00 01 04 06 02 00 33 ff ff ff 00 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 2c f2 4d ba 5f b0 a3 0e 26 e8 3b 2a c5 b9 e2 9e 1b 16 1e 5c 1f a7 42 5e 73 04 33 62 93 8b 98 24
但是当我把它放入 privateEncrypt
时,我也没有得到正确的输出。有人可以帮我吗?
您正在尝试 PKCS1
-填充 (RSASSA-PKCS1-V1_5
) 手动。但这不是必须的。连接 Hash-ID(此处为 SHA-256
)和您的数据 (fingerprint
) 就足够了,其余的由 implicitly
选定的填充 (crypto.constants.RSA_PKCS1_PADDING
) 完成, 即
// Signing
var fingerprint = Buffer.from('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824','hex');
var id = Buffer.from([0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20]);
var allData = Buffer.concat([id, fingerprint]);
var signature = crypto.privateEncrypt(privateKey, allData); // crypto.constants.RSA_PKCS1_PADDING by default
// Verifying with createVerify
var verify = crypto.createVerify('RSA-SHA256');
verify.update('<the signed data>');
var verified = verify.verify(publicKey, signature); // provides true
// Verifying with publicDecrypt
var decryptedFingerprint = crypto.publicDecrypt(publicKey, Buffer.from(signature)).slice(-32); // provides fingerprint
注意:如果你想手动填充,你必须在allData
-buffer之前设置字节序列0x00 || 0x01 || PS || 0x00
。 PS
由达到密钥长度(以字节为单位)所需的 0xff
字节组成。此外,国旗
crypto.constants.RSA_NO_PADDING
必须在 privateEncrypt
调用中明确设置。但是,这不是必需的,因为结果是一样的。 RFC 8017, Section 8.2.1