Sign a CSR with Azure Key Vault

拈花ヽ惹草 提交于 2020-12-26 04:09:06

问题


How can I achieve the very basic CSR Signing HSM functionality with Azure Key Vault?

I had found a very long and manual process to somehow achieve it:

  1. Create a private key in Key Vault
  2. Create a CSR, digest it with SHA256
  3. Sign the digest with the previous private key using the Sign() method
  4. Create a local x.509 cert and append the signature
  5. Upload the new signed cert to Key Vault

Problem is, it is manual, long (also, quite a bit of latency) and error prone. Also I haven't found a single C# code example for this, and I'm looking for EC and not RSA.

The question is, is there a simple CertificateRequest.Sign() function in Key Vault? this seems to be so basic for an HSM-like service...

Thanks


回答1:


This blog post by Vitaliy Slepakov describes a solution that he created which implements the steps you listed above using C#/.NET Core.

The code is available here: https://github.com/vslepakov/keyvault-ca/

The heart of it is the following:

byte[] certificateRequest = /* ... */;
string issuerCertificateName = /* ... */;
KeyVaultServiceClient keyVaultServiceClient = /* ... */;
X509SignatureGenerator generator = /* see next section */;

var pkcs10CertificationRequest = new Pkcs10CertificationRequest(certificateRequest);
//TODO: Validate CSR via pkcs10CertificationRequest.Verify()

var info = pkcs10CertificationRequest.GetCertificationRequestInfo();
var notBefore = DateTime.UtcNow.AddDays(-1);

// Get the RSA public key from the CSR
var asymmetricKeyParameter = Org.BouncyCastle.Security.PublicKeyFactory.CreateKey(info.SubjectPublicKeyInfo);
var rsaKeyParameters = (Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters)asymmetricKeyParameter;
var rsaKeyInfo = new RSAParameters
{
    Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned(),
    Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned()
};
var publicKey = RSA.Create(rsaKeyInfo);
//TODO: Validate publicKey

var certBundle = await keyVaultServiceClient.GetCertificateAsync(issuerCertificateName).ConfigureAwait(false);

var signingCert = new X509Certificate2(certBundle.Cer);

// new serial number
var serialNumber = new byte[SerialNumberLength];
RandomNumberGenerator.Fill(serialNumber);
serialNumber[0] &= 0x7F;

var subjectDN = new X500DistinguishedName(subjectName);
var request = new CertificateRequest(subjectDN, publicKey, GetRSAHashAlgorithmName(hashSizeInBits), RSASignaturePadding.Pkcs1);

// Basic constraints
request.CertificateExtensions.Add(
    new X509BasicConstraintsExtension(caCert, caCert, 0, true));

// Subject Key Identifier
var ski = new X509SubjectKeyIdentifierExtension(
    request.PublicKey,
    X509SubjectKeyIdentifierHashAlgorithm.Sha1,
    false);

request.CertificateExtensions.Add(ski);

// Authority Key Identifier
if (issuerCAKeyCert != null)
    request.CertificateExtensions.Add(BuildAuthorityKeyIdentifier(issuerCAKeyCert));
else
    request.CertificateExtensions.Add(BuildAuthorityKeyIdentifier(subjectDN, serialNumber.Reverse().ToArray(), ski));

if (caCert)
    request.CertificateExtensions.Add(
        new X509KeyUsageExtension(
            X509KeyUsageFlags.DigitalSignature |
            X509KeyUsageFlags.KeyCertSign |
            X509KeyUsageFlags.CrlSign,
            true));
else
{
    // Key Usage
    var defaultFlags =
        X509KeyUsageFlags.DigitalSignature |
        X509KeyUsageFlags.DataEncipherment |
        X509KeyUsageFlags.NonRepudiation |
        X509KeyUsageFlags.KeyEncipherment;

    request.CertificateExtensions.Add(new X509KeyUsageExtension(defaultFlags, true));

    // Enhanced key usage
    request.CertificateExtensions.Add(
        new X509EnhancedKeyUsageExtension(
            new OidCollection {
            new Oid("1.3.6.1.5.5.7.3.1"),
            new Oid("1.3.6.1.5.5.7.3.2") }, true));
}

if (issuerCAKeyCert != null)
{
    if (notAfter > issuerCAKeyCert.NotAfter)
    {
        notAfter = issuerCAKeyCert.NotAfter;
    }
    if (notBefore < issuerCAKeyCert.NotBefore)
    {
        notBefore = issuerCAKeyCert.NotBefore;
    }
}

var issuerSubjectName = issuerCAKeyCert != null ? issuerCAKeyCert.SubjectName : subjectDN;
X509Certificate2 signedCert = request.Create(
    issuerSubjectName,
    generator,
    notBefore,
    notAfter,
    serialNumber);

The custom X509SignatureGenerator implementation is here and used the following Key Vault SDK method:

HashAlgorithm hash = /* see GitHub */;
var digest = hash.ComputeHash(data);

var resultKeyVaultPkcs = await keyVaultClient.SignAsync(signingKey, algorithm, digest, RSASignaturePadding.Pkcs1);

Hopefully you can adapt this code to meet your needs. I'll be doing that as well. 😀




回答2:


KV has a sign operation, but it's not specific to any data type (such as a cert request).

The KV .NET SDK offers this https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.keyvault.keyvaultclient.signwithhttpmessagesasync?view=azure-dotnet-legacy which is just a wrapper over REST APIs https://docs.microsoft.com/en-us/rest/api/keyvault/

This isn't exactly what you want, but I wrote some code to demonstrate C# KV encrypt/decrypt which should help you get started https://github.com/x509cert/AzureKeyVault



来源:https://stackoverflow.com/questions/62495165/sign-a-csr-with-azure-key-vault

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!