关于使用Cloud KMS 进行签名验证的问题
Question about signature verification using Cloud KMS
我正在尝试验证使用 Google 的云 KMS 生成的签名,但我不断收到无效响应。
以下是我的测试方式:
const versionName = client.cryptoKeyVersionPath(
projectId,
locationId,
keyRingId,
keyId,
versionId
)
const [publicKey] = await client.getPublicKey({
name: versionName,
})
const valueToSign = 'hola, que tal'
const digest = crypto.createHash('sha256').update(valueToSign).digest()
const [signResponse] = await client.asymmetricSign({
name: versionName,
digest: {
sha256: digest,
},
})
const valid = crypto.createVerify('sha256').update(digest).verify(publicKey.pem, signResponse.signature)
if (!valid) return console.log('INVALID SIGNATURE')
console.log('SIGNATURE IS VALID!')
// output: INVALID SIGNATURE
此代码将始终记录 'INVALID SIGNATURE' 除非 我使用原始消息而不是其哈希值:
const valid = crypto.createVerify('sha256').update(valueToSign).verify(publicKey.pem, signResponse.signature) // true
但是使用本地私钥,我能够签署消息并使用它们的哈希值验证它们:
const valueToSign = 'hola, the tal'
const msgHash = crypto.createHash("sha256").update(valueToSign).digest('base64');
const signer = crypto.createSign('sha256');
signer.update(msgHash);
const signature = signer.sign(pk, 'base64');
const verifier = crypto.createVerify('sha256');
verifier.update(msgHash);
const valid = verifier.verify(pubKey, signature, 'base64');
console.log(valid) // true
这是为什么? kms 签名有什么不同吗?
根据加密模块文档中的 this example 和您的观察,我认为您可能误解了 client.asymmetricSign
的工作原理。让我们分析一下发生了什么:
您本地的私钥代码:
const valueToSign = 'hola, the tal'
// Create sha256 hash
const msgHash = crypto.createHash("sha256").update(valueToSign).digest('base64');
// Let signer sign sha256(hash)
const signer = crypto.createSign('sha256');
signer.update(msgHash);
const signature = signer.sign(pk, 'base64');
// We now got sign(sha256(hash))
// Let verifier verify sha256(hash)
const verifier = crypto.createVerify('sha256');
verifier.update(msgHash);
const valid = verifier.verify(pubKey, signature, 'base64');
console.log(valid) // true
我们正在使用 verify(sha256(hash))
验证 sign(sha256(hash))
。
您的 KMS 代码:
const valueToSign = 'hola, que tal'
// Create sha256 hash
const digest = crypto.createHash('sha256').update(valueToSign).digest()
// Let KMS sign the hash
const [signResponse] = await client.asymmetricSign({
name: versionName,
digest: {
sha256: digest, // we already say "we hashed our data using sha256"
},
});
// We now got `sign(hash)`, NOT `sign(sha256(hash))` (where hash == digest)
// Let verifier verify sha256(hash)
const valid = crypto.createVerify('sha256').update(digest).verify(publicKey.pem, signResponse.signature)
我们正在使用 verify(sha256(hash))
验证 sign(hash)
。
基本上,您在本地签署您的散列并验证签名的散列。使用 KMS,您正在签署 您的数据 并验证签名哈希,这实际上是您签名的 数据 ,因此您第二次尝试 .update(valueToSign)
有效。
解决方案?在让 KMS 签名之前再次散列您的 sha256 散列,因为 KMS 需要待签名数据的 sha256 散列 ,而 crypto
需要待签名数据数据 (根据您传递给 createSign
的算法,它会自行散列).
答案与凯文的答案非常相似,但换句话说,从不同的角度来看。
当您使用 crypto.createSign(<algorithm>)
and crypto.createVerify(<algorithm>)
时,您表示将分别用于签名创建和验证的摘要 algorithm
。
当您在返回的 Sign
and Verify
对象上调用 update
时,您需要 按原样提供数据 ,crypto
会处理稍后 sign
或 verify
时适当消化该信息。
相比之下,生成的GCP KMS asymmetricSign
operation requires a message digest用指定的algorithm
覆盖你的原始数据作为参数。这就是为什么你需要先用crypto.createHash
计算消息摘要。
但请注意,如前所述,这一事实不会改变 crypto
验证过程的行为,它始终需要原始数据作为输入,这就是为什么您的代码在通过时有效未经哈希处理的原始数据。
虽然您在问题中提供了一个工作示例以供参考,但 GCP documentation 提供了其他示例。
我正在尝试验证使用 Google 的云 KMS 生成的签名,但我不断收到无效响应。
以下是我的测试方式:
const versionName = client.cryptoKeyVersionPath(
projectId,
locationId,
keyRingId,
keyId,
versionId
)
const [publicKey] = await client.getPublicKey({
name: versionName,
})
const valueToSign = 'hola, que tal'
const digest = crypto.createHash('sha256').update(valueToSign).digest()
const [signResponse] = await client.asymmetricSign({
name: versionName,
digest: {
sha256: digest,
},
})
const valid = crypto.createVerify('sha256').update(digest).verify(publicKey.pem, signResponse.signature)
if (!valid) return console.log('INVALID SIGNATURE')
console.log('SIGNATURE IS VALID!')
// output: INVALID SIGNATURE
此代码将始终记录 'INVALID SIGNATURE' 除非 我使用原始消息而不是其哈希值:
const valid = crypto.createVerify('sha256').update(valueToSign).verify(publicKey.pem, signResponse.signature) // true
但是使用本地私钥,我能够签署消息并使用它们的哈希值验证它们:
const valueToSign = 'hola, the tal'
const msgHash = crypto.createHash("sha256").update(valueToSign).digest('base64');
const signer = crypto.createSign('sha256');
signer.update(msgHash);
const signature = signer.sign(pk, 'base64');
const verifier = crypto.createVerify('sha256');
verifier.update(msgHash);
const valid = verifier.verify(pubKey, signature, 'base64');
console.log(valid) // true
这是为什么? kms 签名有什么不同吗?
根据加密模块文档中的 this example 和您的观察,我认为您可能误解了 client.asymmetricSign
的工作原理。让我们分析一下发生了什么:
您本地的私钥代码:
const valueToSign = 'hola, the tal'
// Create sha256 hash
const msgHash = crypto.createHash("sha256").update(valueToSign).digest('base64');
// Let signer sign sha256(hash)
const signer = crypto.createSign('sha256');
signer.update(msgHash);
const signature = signer.sign(pk, 'base64');
// We now got sign(sha256(hash))
// Let verifier verify sha256(hash)
const verifier = crypto.createVerify('sha256');
verifier.update(msgHash);
const valid = verifier.verify(pubKey, signature, 'base64');
console.log(valid) // true
我们正在使用 verify(sha256(hash))
验证 sign(sha256(hash))
。
您的 KMS 代码:
const valueToSign = 'hola, que tal'
// Create sha256 hash
const digest = crypto.createHash('sha256').update(valueToSign).digest()
// Let KMS sign the hash
const [signResponse] = await client.asymmetricSign({
name: versionName,
digest: {
sha256: digest, // we already say "we hashed our data using sha256"
},
});
// We now got `sign(hash)`, NOT `sign(sha256(hash))` (where hash == digest)
// Let verifier verify sha256(hash)
const valid = crypto.createVerify('sha256').update(digest).verify(publicKey.pem, signResponse.signature)
我们正在使用 verify(sha256(hash))
验证 sign(hash)
。
基本上,您在本地签署您的散列并验证签名的散列。使用 KMS,您正在签署 您的数据 并验证签名哈希,这实际上是您签名的 数据 ,因此您第二次尝试 .update(valueToSign)
有效。
解决方案?在让 KMS 签名之前再次散列您的 sha256 散列,因为 KMS 需要待签名数据的 sha256 散列 ,而 crypto
需要待签名数据数据 (根据您传递给 createSign
的算法,它会自行散列).
答案与凯文的答案非常相似,但换句话说,从不同的角度来看。
当您使用 crypto.createSign(<algorithm>)
and crypto.createVerify(<algorithm>)
时,您表示将分别用于签名创建和验证的摘要 algorithm
。
当您在返回的 Sign
and Verify
对象上调用 update
时,您需要 按原样提供数据 ,crypto
会处理稍后 sign
或 verify
时适当消化该信息。
相比之下,生成的GCP KMS asymmetricSign
operation requires a message digest用指定的algorithm
覆盖你的原始数据作为参数。这就是为什么你需要先用crypto.createHash
计算消息摘要。
但请注意,如前所述,这一事实不会改变 crypto
验证过程的行为,它始终需要原始数据作为输入,这就是为什么您的代码在通过时有效未经哈希处理的原始数据。
虽然您在问题中提供了一个工作示例以供参考,但 GCP documentation 提供了其他示例。