【问题标题】:Invalid signature when creating a certificate using BouncyCastle with an external Azure KeyVault (HSM) Key使用带有外部 Azure KeyVault (HSM) 密钥的 BouncyCastle 创建证书时签名无效
【发布时间】:2020-11-25 20:18:27
【问题描述】:

我正在尝试生成由存储在 Azure KeyVault 中的密钥对自签名的证书。

我的最终结果是一个带有无效签名的证书:

生成证书参数:

     DateTime startDate = DateTime.Now.AddDays(-30);
     DateTime expiryDate = startDate.AddYears(100);

     BigInteger serialNumber = new BigInteger(32, new Random());
     X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();

     X509Name selfSignedCA = new X509Name("CN=Test Root CA");

     certGen.SetSerialNumber(serialNumber);
     certGen.SetIssuerDN(selfSignedCA); //Self Signed
     certGen.SetNotBefore(startDate);
     certGen.SetNotAfter(expiryDate);
     certGen.SetSubjectDN(selfSignedCA);
      

获取对 Azure KeyVault 存储密钥的引用(类似 HSM 的服务):

    //Create a client connector to Azure KeyVault
    var keyClient = new Azure.Security.KeyVault.Keys.KeyClient(
         vaultUri: new Uri("https://xxxx.vault.azure.net/"),
         credential: new ClientSecretCredential(
             tenantId: "xxxx", //Active Directory
             clientId: "xxxx", //Application id?
             clientSecret: "xxxx"
             )
         );

        var x = keyClient.GetKey("key-new-ec"); //Fetch the reference to the key

密钥已成功检索。 然后我尝试使用密钥的公共数据生成 ECPublicKeyParameters 对象:

    X9ECParameters x9 = ECNamedCurveTable.GetByName("P-256");
    Org.BouncyCastle.Math.EC.ECCurve curve = x9.Curve;

    var ecPoint = curve.CreatePoint(new Org.BouncyCastle.Math.BigInteger(1, x.Value.Key.X), new Org.BouncyCastle.Math.BigInteger(1, x.Value.Key.Y));
    ECDomainParameters dParams = new ECDomainParameters(curve, ecPoint, x9.N);
    ECPublicKeyParameters pubKey = new ECPublicKeyParameters(ecPoint, dParams);

    certGen.SetPublicKey(pubKey); //Setting the certificate's public key with the fetched one

下一步是生成使用密钥签名的证书。我实现了一个新的 ISignatureFactory 对象,该对象应该使用 KeyVault 的外部签名功能进行签名:

      AzureKeyVaultSignatureFactory customSignatureFactory = new AzureKeyVaultSignatureFactory(1);
      Org.BouncyCastle.X509.X509Certificate cert = certGen.Generate(customSignatureFactory);

这是我的自定义 AzureKeyVaultSignatureFactory

public class AzureKeyVaultSignatureFactory : ISignatureFactory
{
    private readonly int _keyHandle;

    public AzureKeyVaultSignatureFactory(int keyHandle)
    {
        this._keyHandle = keyHandle;
    }

    public IStreamCalculator CreateCalculator()
    {
        var sig = new CustomAzureKeyVaultDigestSigner(this._keyHandle);

        sig.Init(true, null);

        return new DefaultSignatureCalculator(sig);
    }

    internal class CustomAzureKeyVaultDigestSigner : ISigner
    {
        private readonly int _keyHandle;
        private byte[] _input;

        public CustomAzureKeyVaultDigestSigner(int keyHandle)
        {
            this._keyHandle = keyHandle;
        }

        public void Init(bool forSigning, ICipherParameters parameters)
        {
            this.Reset();
        }

        public void Update(byte input)
        {
            return;
        }

        public void BlockUpdate(byte[] input, int inOff, int length)
        {
            this._input = input.Skip(inOff).Take(length).ToArray();
        }

        public byte[] GenerateSignature()
        {
            //Crypto Client (Specific Key)
            try
            {

                //Crypto Client (Specific Key)
                CryptographyClient identitiesCAKey_cryptoClient = new CryptographyClient(
                    keyId: new Uri("https://xxxx.vault.azure.net/keys/key-new-ec/xxxx"),
                    credential: new ClientSecretCredential(

                          tenantId: "xxxx", //Active Directory
                          clientId: "xxxx", //Application id?
                          clientSecret: "xxxx"
                          )
                );

                SignResult signResult = identitiesCAKey_cryptoClient.SignData(SignatureAlgorithm.ES256, this._input);
                return signResult.Signature;


            }
            catch (Exception ex)
            {

                throw ex;
            }

            return null;
        }

        public bool VerifySignature(byte[] signature)
        {
            return false;
        }

        public void Reset() { }

        public string AlgorithmName => "SHA-256withECDSA";
    }

    public object AlgorithmDetails => new AlgorithmIdentifier(X9ObjectIdentifiers.ECDsaWithSha256, DerNull.Instance);
}

然后我将证书转换并写入文件:

 //convert to windows type 2 and get Base64 
 X509Certificate2 cert2 = new X509Certificate2(DotNetUtilities.ToX509Certificate(cert));
 byte[] encoded = cert2.GetRawCertData();
 string certOutString = Convert.ToBase64String(encoded);
 System.IO.File.WriteAllBytes(@"test-signed2.cer", encoded); //-this is good!

我做错了什么?也许从 X/Y 构造 ECCurve 还不够?

谢谢!

【问题讨论】:

  • 哇,对于一个简单的 ecdsa 签名来说,这非常复杂。一件事肯定是错的,你的签名算法不是public string AlgorithmName => "SHA-256withRSA";
  • 是的,因为签名发生在外部,显然这不是一种常见的做法。如果 Azure 有 PKI 服务,我会很高兴,但他们没有。谢谢,我会检查算法名称。
  • 已更改为 SHA-256withECDSA,同样的错误
  • 这把钥匙是 BYOK 吗?它是否根据 Microsoft 指南创建并且是否满足所有先决条件?
  • @mnistic BYOK?那是什么?它只是一个带有 SHA256 证书的普通自签名 ECDSA。我不知道,这是我问题的一部分......

标签: c# encryption .net-core bouncycastle azure-keyvault


【解决方案1】:

问题是密钥库返回的签名是“原始”(64 字节)格式,其中前 32 个是 R,后 32 个是 S。为了让它在 bouncycastle 中工作,您的 GenerateSignature 方法需要在 ASN.1 格式的字节数组中返回它,最终将在 70 到 72 个字节之间。

您可以在线查看这实际上意味着什么,但您会想要:

  1. 为您的结果创建一个新的字节数组
  2. 将密钥库的输出拆分为两个最初为 32 位的数组,RS
  3. 如果 RS 数组的第 0 个元素的 MSB 较高,则需要在相应数组的开头插入 0(否则什么都没有,数组保持 32 字节长)。
  4. 构建必要的 ASN.1 标头(或者像下面显示的那样手动构建,或者 bouncycastle 具有一些库功能来创建 ASN.1 消息)。所以最后,输出字节数组应该包含
0x30
one byte containing the length of the rest of the array*
0x02
a byte containing the length of the R array (either 32 or 33 depending on if + or -)
0x02
a byte containing the length of the S array (either 32 or 33 depending on if + or -)
the entire S array

  1. 将此数组作为GenerateSignature 的输出返回

* 所以整个长度将是 R 的长度 + S 的长度 + 4 个头字节(R 长度,R 头,S 长度,S 头)

我已经使用我自己的密钥测试了这种方法,该密钥由云服务返回,该服务也返回 64 字节 R+S 响应并且它有效。

【讨论】:

  • 非常感谢,我会尝试并报告。
猜你喜欢
  • 2014-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多