/// <summary> /// Parse buffer into cert /// </summary> /// <param name="buffer"></param> /// <param name="key"></param> /// <param name="policies"></param> /// <param name="revoked"></param> /// <returns></returns> public static Certificate Create(byte[] buffer, KeyHandle key = null, IssuerPolicies policies = null, RevocationInfo revoked = null) { using (var cert = new X509Certificate2(buffer)) { return(ToCertificate(cert, policies, key, revoked)); } }
/// <summary> /// Clone issuer policies /// </summary> /// <param name="policies"></param> /// <returns></returns> public static IssuerPolicies Clone(this IssuerPolicies policies) { if (policies == null) { return(null); } return(new IssuerPolicies { IssuedLifetime = policies.IssuedLifetime, SignatureType = policies.SignatureType }); }
/// <summary> /// Compare /// </summary> /// <param name="policies"></param> /// <param name="other"></param> /// <returns></returns> public static bool SameAs(this IssuerPolicies policies, IssuerPolicies other) { if (policies == null) { return(other == null); } if (other == null) { return(false); } if (policies.IssuedLifetime != other.IssuedLifetime) { return(false); } if (policies.SignatureType != other.SignatureType) { return(false); } return(true); }
/// <summary> /// Validate issuer policies /// </summary> /// <param name="policies"></param> /// <param name="parent"></param> /// <param name="keyParams"></param> /// <returns></returns> public static IssuerPolicies Validate(this IssuerPolicies policies, IssuerPolicies parent = null, CreateKeyParams keyParams = null) { if (policies == null) { policies = new IssuerPolicies(); } if (policies.IssuedLifetime == null) { policies.IssuedLifetime = parent?.IssuedLifetime != null ? parent.IssuedLifetime.Value / 2 : TimeSpan.FromDays(1); } if (policies.SignatureType == null) { policies.SignatureType = keyParams.Type == KeyType.RSA ? SignatureType.RS256 : SignatureType.ES256; } if (parent != null) { if (policies.IssuedLifetime > parent.IssuedLifetime) { throw new ArgumentException( "Issued lifetime cannot be greater than parent issuer policy"); } } if (keyParams != null) { if (policies.SignatureType.Value.IsRSA() && keyParams.Type != KeyType.RSA) { throw new ArgumentException( "Cannot create rsa signature with mismatch key"); } if (policies.SignatureType.Value.IsECC() && keyParams.Type != KeyType.ECC) { throw new ArgumentException( "Cannot create ecc signature with mismatch key"); } } return(policies); }
/// <summary> /// Parse buffer into cert /// </summary> /// <param name="cert"></param> /// <param name="policies"></param> /// <param name="key"></param> /// <param name="revoked"></param> /// <returns></returns> public static Certificate ToCertificate(this X509Certificate2 cert, IssuerPolicies policies = null, KeyHandle key = null, RevocationInfo revoked = null) { if (cert == null) { return(null); } // We store big-endian but GetSerialNumber returns little-endian var serialNumber = cert.GetSerialNumber(); // .net creates clone Array.Reverse(serialNumber); var certificate = new Certificate { RawData = cert.RawData, KeyHandle = key, IssuerPolicies = cert.IsCa() ? policies : null, Revoked = revoked, NotAfterUtc = cert.NotAfter.ToUniversalTime(), NotBeforeUtc = cert.NotBefore.ToUniversalTime(), Subject = cert.SubjectName, Thumbprint = cert.Thumbprint, Issuer = cert.IssuerName, SerialNumber = serialNumber, Extensions = new List <X509Extension>(cert.Extensions.OfType <X509Extension>()) }; // Set issuer serial number certificate.IssuerSerialNumber = certificate.GetAuthorityKeyIdentifierExtension()?.SerialNumber.Value; if (certificate.IssuerSerialNumber == null && certificate.IsSelfSigned()) { certificate.IssuerSerialNumber = certificate.SerialNumber.ToArray(); } return(certificate); }
/// <inheritdoc/> public async Task <Certificate> NewRootCertificateAsync(string certificateName, X500DistinguishedName subjectName, DateTime?notBefore, TimeSpan lifetime, CreateKeyParams keyParams, IssuerPolicies policies, Func <byte[], IEnumerable <X509Extension> > extensions, CancellationToken ct) { if (string.IsNullOrEmpty(certificateName)) { throw new ArgumentNullException(nameof(certificateName)); } // Validate policies policies = policies.Validate(null, keyParams); // Create new signing key var keyHandle = await _keys.CreateKeyAsync(Guid.NewGuid().ToString(), keyParams, new KeyStoreProperties { Exportable = false }, ct); try { // Get public key var publicKey = await _keys.GetPublicKeyAsync(keyHandle, ct); // Create certificate var certificate = await _factory.CreateCertificateAsync(_keys, keyHandle, subjectName, publicKey, GetNotAfter(notBefore, lifetime, DateTime.MaxValue, out var notAfter), notAfter, policies.SignatureType.Value, true, extensions, ct); using (certificate) { // Import certificate var result = certificate.ToCertificate(policies, keyHandle); await _repo.AddCertificateAsync(certificateName, result, null, ct); return(result); } } catch (Exception ex) { _logger.Verbose(ex, "Failed to add certificate, delete key"); await Try.Async(() => _keys.DeleteKeyAsync(keyHandle, ct)); throw; } }
/// <inheritdoc/> public async Task <Certificate> NewIssuerCertificateAsync(string rootCertificate, string certificateName, X500DistinguishedName subjectName, DateTime?notBefore, CreateKeyParams keyParams, IssuerPolicies policies, Func <byte[], IEnumerable <X509Extension> > extensions, CancellationToken ct) { try { // (0) Retrieve issuer certificate var caCertBundle = await _keyVaultClient.GetCertificateAsync( _vaultBaseUrl, rootCertificate, ct); if (caCertBundle == null) { throw new ResourceNotFoundException("Issuer cert not found."); } var caCert = await _certificates.FindCertificateAsync( caCertBundle.CertificateIdentifier.Identifier); if (caCert?.IssuerPolicies == null) { throw new ArgumentException("Certificate cannot issue."); } // Validate policies policies = policies.Validate(caCert.IssuerPolicies, keyParams); // (1) Create key in key vault and get CSR. // policy unknown issuer, new key, exportable key var policyUnknownNewExportable = CreateCertificatePolicy( subjectName.Name, keyParams, false, _keyStoreIsHsm, false, true); var attributes = CreateCertificateAttributes(notBefore, caCert.IssuerPolicies.IssuedLifetime.Value, caCert.NotAfterUtc); var createResult = await CreateCertificateAsync(certificateName, policyUnknownNewExportable, attributes, null, ct); if (createResult.Csr == null) { throw new CryptographicUnexpectedOperationException( "Failed to read CSR from CreateCertificate."); } // decode the CSR and verify consistency var info = createResult.Csr.ToCertificationRequest(); try { // (2) - Issue X509 Certificate with csr and root certificate. var signedcert = await _factory.CreateCertificateAsync(this, caCert, subjectName, info.PublicKey, attributes.NotBefore.Value, attributes.Expires.Value, caCert.IssuerPolicies.SignatureType.Value, true, extensions, ct); // (3) - Complete certificate creation with merger of X509 Certificate. var mergeResult = await _keyVaultClient.MergeCertificateAsync( _vaultBaseUrl, certificateName, new X509Certificate2Collection(signedcert), null, null, ct); // (4) - Get merged certificate and key identifier var mergedCert = await _keyVaultClient.GetCertificateAsync( mergeResult.CertificateIdentifier.Identifier, ct); var cert = CertificateEx.Create(mergedCert.Cer, new KeyVaultKeyHandle(mergedCert), policies); if (!cert.IsIssuer()) { throw new ArgumentException("Certifcate created is not issuer."); } await _certificates.AddCertificateAsync(certificateName, cert, mergedCert.CertificateIdentifier.Identifier, ct); return(cert); } catch { await Try.Async(() => _keyVaultClient.DeleteCertificateAsync( _vaultBaseUrl, certificateName, ct)); await Try.Async(() => _keyVaultClient.PurgeDeletedCertificateAsync( _vaultBaseUrl, certificateName, ct)); throw; } } catch (KeyVaultErrorException ex) { throw new ExternalDependencyException( "Failed to create new key pair certificate", ex); } }
/// <inheritdoc/> public async Task <Certificate> NewRootCertificateAsync(string certificateName, X500DistinguishedName subjectName, DateTime?notBefore, TimeSpan lifetime, CreateKeyParams keyParams, IssuerPolicies policies, Func <byte[], IEnumerable <X509Extension> > extensions, CancellationToken ct) { if (string.IsNullOrEmpty(certificateName)) { throw new ArgumentNullException(nameof(certificateName)); } // Validate policies policies = policies.Validate(null, keyParams); string caTempCertIdentifier = null; try { // (1) Create key in key vault and get CSR. // policy self signed, new key, not exportable key var policySelfSignedNewKey = CreateCertificatePolicy( subjectName.Name, keyParams, true, _keyStoreIsHsm, false, false); var tempAttributes = CreateCertificateAttributes( DateTime.UtcNow.AddMinutes(-10), TimeSpan.FromMinutes(10), DateTime.MaxValue); await CreateCertificateAsync(certificateName, policySelfSignedNewKey, tempAttributes, null, ct); // We have the cert - get it and key identifier to do the signing var createdCertificateBundle = await _keyVaultClient.GetCertificateAsync( _vaultBaseUrl, certificateName, ct); caTempCertIdentifier = createdCertificateBundle.CertificateIdentifier.Identifier; // policy unknown issuer, reuse key - not exportable var policyUnknownReuse = CreateCertificatePolicy( subjectName.Name, keyParams, false, _keyStoreIsHsm, true, false); var attributes = CreateCertificateAttributes(notBefore, lifetime, DateTime.MaxValue); // create the CSR var createResult = await CreateCertificateAsync(certificateName, policyUnknownReuse, attributes, null, ct); if (createResult.Csr == null) { throw new CryptographicUnexpectedOperationException( "Failed to read CSR from CreateCertificate."); } // decode the CSR and verify consistency var info = createResult.Csr.ToCertificationRequest(); // (2) - Issue root X509 Certificate with the csr. var signedcert = await _factory.CreateCertificateAsync(this, new KeyVaultKeyHandle(createdCertificateBundle), subjectName, info.PublicKey, attributes.NotBefore.Value, attributes.Expires.Value, policies.SignatureType.Value, true, extensions, ct); // (3) - Complete certificate creation with merger of X509 Certificate. var mergeResult = await _keyVaultClient.MergeCertificateAsync( _vaultBaseUrl, certificateName, new X509Certificate2Collection(signedcert), null, null, ct); // (4) - Get merged certificate and key identifier var mergedCert = await _keyVaultClient.GetCertificateAsync( mergeResult.CertificateIdentifier.Identifier, ct); var cert = CertificateEx.Create(mergedCert.Cer, new KeyVaultKeyHandle(mergedCert), policies); await _certificates.AddCertificateAsync(certificateName, cert, mergedCert.CertificateIdentifier.Identifier, ct); return(cert); } catch (KeyVaultErrorException kex) { throw new ExternalDependencyException( "Failed to create new Root CA certificate", kex); } finally { if (caTempCertIdentifier != null) { // disable the temp cert for self signing operation var attr = new CertificateAttributes { Enabled = false }; await Try.Async(() => _keyVaultClient.UpdateCertificateAsync( caTempCertIdentifier, null, attr)); } } }