azure keyvault:在本地验证由 EC-HSM 签名的数据
azure keyvault : locally verify data signed by EC-HSM
感谢 EC-HSM,我使用 Azure JS SDK 进行了测试,以使用密钥保管库对数据进行签名和验证
当我使用 Azure JS SDK 验证它是否正常工作时,我将输出放在评论中
describe('crypto services', () => {
it('Azure HSM', async () => {
const client = new KeyClient(
'https://xxx.vault.azure.net',
new EnvironmentCredential(),
);
const keys: { [key: string]: KeyVaultKey } = {};
for await (const key of client.listPropertiesOfKeys()) {
keys[key.name] = await client.getKey(key.name);
}
console.log('available keys', Object.keys(keys)); //logging ['tmp']
const cryptoClient = new CryptographyClient(
keys['tmp'],
new EnvironmentCredential(),
);
const content = Buffer.from('Hello world');
const sig = await cryptoClient.signData('ES256', content);
console.log('signature', Buffer.from(sig.result).toString('base64')); //logging different output on each call
console.log('key crv', keys['tmp'].key.crv); //P-256
console.log('key x', Buffer.from(keys['tmp'].key?.x).toString('base64')); //OC76WxZ/TMzJnRqv/cy9llDqSIMWlplgREY3jMxDCks=
console.log('key y', Buffer.from(keys['tmp'].key?.y).toString('base64')); //Dxgw8nqXoO3xXQruejfQa/Z+aFpo/4ilC64JUHoRoog=
const verif = await cryptoClient.verifyData('ES256', content, sig.result);
expect(verif.result).toBe(true);
});
})
现在,我想在不调用 azure sdk 的情况下在本地进行验证(这正是首先使用非对称加密的原因)
it.only('local HSM verif', () => {
const content = Buffer.from('Hello world'); //same content
const sigs = [ //some values I received
'bxw1nS8Q39mnQVkAhvJctXuLHz4n0wUjLbE+phj1XlUeWDxl7DCK5bG4d7YrL7zGtAnUq3YT9AdrrAXjpwhCzQ==',
'hlh1CJSKivFYcdVygI0KJfUCGYor+whUu6NsJZhNTCllHHRmgh9FvcvxBSFVu0am7A9lryG/N5vLAv/B1Niiew==',
];
const base64X = 'OC76WxZ/TMzJnRqv/cy9llDqSIMWlplgREY3jMxDCks='; //same as output of previous test
const base64Y = 'Dxgw8nqXoO3xXQruejfQa/Z+aFpo/4ilC64JUHoRoog=';//same as output of previous test
//converting the jwk to PEM thanks to the jwk-to-pem package
const publicKey = jwkToPem({
kty: 'EC',
crv: 'P-256',
x: base64X,
y: base64Y,
});
for (const sig of sigs) {
const verifyLocal = crypto.createVerify('sha256');
verifyLocal.update(content);
const verifLocal = verifyLocal.verify(publicKey, sig);
expect(verifLocal).toBe(true); //it always fails here
}
});
谁能解释一下这是怎么回事?
我的猜测是 public 签名不正确,因此 x
和 y
在 base64
中不正确
例如,如果我使用 mkjwk.org 生成 JWK
{
kty: 'EC',
d: '7LU9Y16XKiFMcPVm39B5fVOtG0s-bnJwaeEtMrk9udE',
crv: 'P-256',
x: 'g-68Nakmi41xMv6zKduBn4dqcqJ0KXDqdS2rFpxUQOA',
y: 'pYRqhl2YDKBwGGkIXdYQQWuNjOtPcCe1bz_VYalXFW0',
}
它们看起来更短并且没有“/”或“=”
问题是 NodeJS 需要 ASN.1 DER 签名,而 Azure Key Vault 创建 IEEE-P1363 签名(又名串联签名)。
Generating ECDSA signature with Node.js/crypto 对此进行了详细介绍。
以下代码片段可用于将串联签名转换为 NodeJS 期望的格式(在 jwk-to-pem
、asn1.js
和 bn.js
包的帮助下):
import { CryptographyClient, KeyClient } from "@azure/keyvault-keys";
import { AzureCliCredential } from "@azure/identity";
import jwkToPem from "jwk-to-pem";
import * as crypto from "crypto";
import asn1 from "asn1.js";
import BN from "bn.js";
const EcdsaDerSig = asn1.define("ECPrivateKey", function (this: any) {
return this.seq().obj(this.key("r").int(), this.key("s").int());
});
function concatSigToAsn1SigSig(concatSigBuffer: any) {
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");
}
async function main() {
const content = Buffer.from("Hello world"); // some content
const client = new KeyClient(
"https://<your_vault_name>.vault.azure.net",
new AzureCliCredential()
);
const key = await client.createEcKey("eckeytest");
const cryptoClient = new CryptographyClient(key, new AzureCliCredential());
const signature = await cryptoClient.signData("ES256", content); // sign data remotely in Azure Key Vault
const sig = concatSigToAsn1SigSig(signature.result); // convert sig to the node.js expected format
const base64X = Buffer.from(key.key!.x!).toString("base64");
const base64Y = Buffer.from(key.key!.y!).toString("base64");
// convert JWK to PEM for verification
const publicKey = jwkToPem({
kty: "EC",
crv: "P-256",
x: base64X,
y: base64Y,
});
// Depending on the version of Node used, you could use `crypto.verify` directly.
const keyObj = crypto.createPublicKey(publicKey);
let result = crypto.verify("sha256", content, keyObj, sig);
console.log("Result using crypto.verify", result);
// using a verifier which can be used directly with the `publicKey`
let verify = crypto.createVerify("sha256");
verify.update(content);
verify.end();
result = verify.verify(publicKey, sig);
console.log("Result using crypto.createVerify", result);
}
main()
.then(() => console.log("done"))
.catch((err) => console.log(err));
感谢 EC-HSM,我使用 Azure JS SDK 进行了测试,以使用密钥保管库对数据进行签名和验证
当我使用 Azure JS SDK 验证它是否正常工作时,我将输出放在评论中
describe('crypto services', () => {
it('Azure HSM', async () => {
const client = new KeyClient(
'https://xxx.vault.azure.net',
new EnvironmentCredential(),
);
const keys: { [key: string]: KeyVaultKey } = {};
for await (const key of client.listPropertiesOfKeys()) {
keys[key.name] = await client.getKey(key.name);
}
console.log('available keys', Object.keys(keys)); //logging ['tmp']
const cryptoClient = new CryptographyClient(
keys['tmp'],
new EnvironmentCredential(),
);
const content = Buffer.from('Hello world');
const sig = await cryptoClient.signData('ES256', content);
console.log('signature', Buffer.from(sig.result).toString('base64')); //logging different output on each call
console.log('key crv', keys['tmp'].key.crv); //P-256
console.log('key x', Buffer.from(keys['tmp'].key?.x).toString('base64')); //OC76WxZ/TMzJnRqv/cy9llDqSIMWlplgREY3jMxDCks=
console.log('key y', Buffer.from(keys['tmp'].key?.y).toString('base64')); //Dxgw8nqXoO3xXQruejfQa/Z+aFpo/4ilC64JUHoRoog=
const verif = await cryptoClient.verifyData('ES256', content, sig.result);
expect(verif.result).toBe(true);
});
})
现在,我想在不调用 azure sdk 的情况下在本地进行验证(这正是首先使用非对称加密的原因)
it.only('local HSM verif', () => {
const content = Buffer.from('Hello world'); //same content
const sigs = [ //some values I received
'bxw1nS8Q39mnQVkAhvJctXuLHz4n0wUjLbE+phj1XlUeWDxl7DCK5bG4d7YrL7zGtAnUq3YT9AdrrAXjpwhCzQ==',
'hlh1CJSKivFYcdVygI0KJfUCGYor+whUu6NsJZhNTCllHHRmgh9FvcvxBSFVu0am7A9lryG/N5vLAv/B1Niiew==',
];
const base64X = 'OC76WxZ/TMzJnRqv/cy9llDqSIMWlplgREY3jMxDCks='; //same as output of previous test
const base64Y = 'Dxgw8nqXoO3xXQruejfQa/Z+aFpo/4ilC64JUHoRoog=';//same as output of previous test
//converting the jwk to PEM thanks to the jwk-to-pem package
const publicKey = jwkToPem({
kty: 'EC',
crv: 'P-256',
x: base64X,
y: base64Y,
});
for (const sig of sigs) {
const verifyLocal = crypto.createVerify('sha256');
verifyLocal.update(content);
const verifLocal = verifyLocal.verify(publicKey, sig);
expect(verifLocal).toBe(true); //it always fails here
}
});
谁能解释一下这是怎么回事?
我的猜测是 public 签名不正确,因此 x
和 y
在 base64
例如,如果我使用 mkjwk.org 生成 JWK
{
kty: 'EC',
d: '7LU9Y16XKiFMcPVm39B5fVOtG0s-bnJwaeEtMrk9udE',
crv: 'P-256',
x: 'g-68Nakmi41xMv6zKduBn4dqcqJ0KXDqdS2rFpxUQOA',
y: 'pYRqhl2YDKBwGGkIXdYQQWuNjOtPcCe1bz_VYalXFW0',
}
它们看起来更短并且没有“/”或“=”
问题是 NodeJS 需要 ASN.1 DER 签名,而 Azure Key Vault 创建 IEEE-P1363 签名(又名串联签名)。
Generating ECDSA signature with Node.js/crypto 对此进行了详细介绍。
以下代码片段可用于将串联签名转换为 NodeJS 期望的格式(在 jwk-to-pem
、asn1.js
和 bn.js
包的帮助下):
import { CryptographyClient, KeyClient } from "@azure/keyvault-keys";
import { AzureCliCredential } from "@azure/identity";
import jwkToPem from "jwk-to-pem";
import * as crypto from "crypto";
import asn1 from "asn1.js";
import BN from "bn.js";
const EcdsaDerSig = asn1.define("ECPrivateKey", function (this: any) {
return this.seq().obj(this.key("r").int(), this.key("s").int());
});
function concatSigToAsn1SigSig(concatSigBuffer: any) {
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");
}
async function main() {
const content = Buffer.from("Hello world"); // some content
const client = new KeyClient(
"https://<your_vault_name>.vault.azure.net",
new AzureCliCredential()
);
const key = await client.createEcKey("eckeytest");
const cryptoClient = new CryptographyClient(key, new AzureCliCredential());
const signature = await cryptoClient.signData("ES256", content); // sign data remotely in Azure Key Vault
const sig = concatSigToAsn1SigSig(signature.result); // convert sig to the node.js expected format
const base64X = Buffer.from(key.key!.x!).toString("base64");
const base64Y = Buffer.from(key.key!.y!).toString("base64");
// convert JWK to PEM for verification
const publicKey = jwkToPem({
kty: "EC",
crv: "P-256",
x: base64X,
y: base64Y,
});
// Depending on the version of Node used, you could use `crypto.verify` directly.
const keyObj = crypto.createPublicKey(publicKey);
let result = crypto.verify("sha256", content, keyObj, sig);
console.log("Result using crypto.verify", result);
// using a verifier which can be used directly with the `publicKey`
let verify = crypto.createVerify("sha256");
verify.update(content);
verify.end();
result = verify.verify(publicKey, sig);
console.log("Result using crypto.createVerify", result);
}
main()
.then(() => console.log("done"))
.catch((err) => console.log(err));