/// <summary>
        /// Creates a new key pair with certificate offline and signs it with KeyVault.
        /// </summary>
        public override async Task <Opc.Ua.Gds.Server.X509Certificate2KeyPair> NewKeyPairRequestAsync(
            ApplicationRecordDataType application,
            string subjectName,
            string[] domainNames,
            string privateKeyFormat,
            string privateKeyPassword)
        {
            if (!privateKeyFormat.Equals("PFX", StringComparison.OrdinalIgnoreCase) &&
                !privateKeyFormat.Equals("PEM", StringComparison.OrdinalIgnoreCase))
            {
                throw new ServiceResultException(StatusCodes.BadInvalidArgument, "Invalid private key format");
            }


            DateTime notBefore = DateTime.UtcNow.AddDays(-1);

            // create public/private key pair
            using (RSA keyPair = RSA.Create(Configuration.DefaultCertificateKeySize))
            {
                await LoadPublicAssets().ConfigureAwait(false);

                string authorityInformationAccess = BuildAuthorityInformationAccessUrl();

                // sign public key with KeyVault
                var signedCert = await KeyVaultCertFactory.CreateSignedCertificate(
                    application.ApplicationUri,
                    application.ApplicationNames.Count > 0?application.ApplicationNames[0].Text : "ApplicationName",
                    subjectName,
                    domainNames,
                    Configuration.DefaultCertificateKeySize,
                    notBefore,
                    notBefore.AddMonths(Configuration.DefaultCertificateLifetime),
                    Configuration.DefaultCertificateHashSize,
                    Certificate,
                    keyPair,
                    new KeyVaultSignatureGenerator(_keyVaultServiceClient, _caCertKeyIdentifier, Certificate),
                    extensionUrl : authorityInformationAccess);

                // Create a PEM or PFX
                using (var signedCertWithPrivateKey = CreateCertificateWithPrivateKey(signedCert, keyPair))
                {
                    byte[] privateKey;
                    if (privateKeyFormat.Equals("PFX", StringComparison.OrdinalIgnoreCase))
                    {
                        privateKey = signedCertWithPrivateKey.Export(X509ContentType.Pfx, privateKeyPassword);
                    }
                    else if (privateKeyFormat.Equals("PEM", StringComparison.OrdinalIgnoreCase))
                    {
                        privateKey = CertificateFactory.ExportPrivateKeyAsPEM(signedCertWithPrivateKey);
                    }
                    else
                    {
                        throw new ServiceResultException(StatusCodes.BadInvalidArgument, "Invalid private key format");
                    }
                    return(new Opc.Ua.Gds.Server.X509Certificate2KeyPair(new X509Certificate2(signedCertWithPrivateKey.RawData), privateKeyFormat, privateKey));
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Creates a new signed application certificate in group id.
        /// </summary>
        /// <remarks>
        /// The key for the certificate is created in KeyVault, then exported.
        /// In order to deleted the created key, the impersonated user needs
        /// create, get and delete rights for KeyVault certificates
        /// </remarks>
        public async Task <X509Certificate2> CreateSignedKeyPairCertAsync(
            string caCertId,
            X509Certificate2 issuerCert,
            string applicationUri,
            string applicationName,
            string subjectName,
            string[] domainNames,
            DateTime notBefore,
            DateTime notAfter,
            int keySize,
            int hashSize,
            KeyVaultSignatureGenerator generator,
            string authorityInformationAccess,
            CancellationToken ct = default)
        {
            CertificateOperation createResult = null;
            var certName = KeyStoreName(caCertId, Guid.NewGuid().ToString());

            try
            {
                // policy unknown issuer, new key, exportable
                var policyUnknownNewExportable = CreateCertificatePolicy(subjectName, keySize, false, false, true);
                var attributes = CreateCertificateAttributes(notBefore, notAfter);

                // create the CSR
                createResult = await _keyVaultClient.CreateCertificateAsync(
                    _vaultBaseUrl,
                    certName,
                    policyUnknownNewExportable,
                    attributes,
                    null,
                    ct)
                               .ConfigureAwait(false);

                if (createResult.Csr == null)
                {
                    throw new ServiceResultException(StatusCodes.BadInvalidArgument, "Failed to read CSR from CreateCertificate.");
                }

                // decode the CSR and verify consistency
                var pkcs10CertificationRequest = new Org.BouncyCastle.Pkcs.Pkcs10CertificationRequest(createResult.Csr);
                var info = pkcs10CertificationRequest.GetCertificationRequestInfo();
                if (createResult.Csr == null ||
                    pkcs10CertificationRequest == null ||
                    !pkcs10CertificationRequest.Verify())
                {
                    throw new ServiceResultException(StatusCodes.BadInvalidArgument, "Invalid CSR.");
                }

                // create the self signed app cert
                var publicKey  = KeyVaultCertFactory.GetRSAPublicKey(info.SubjectPublicKeyInfo);
                var signedcert = await KeyVaultCertFactory.CreateSignedCertificate(
                    applicationUri,
                    applicationName,
                    subjectName,
                    domainNames,
                    (ushort)keySize,
                    notBefore,
                    notAfter,
                    (ushort)hashSize,
                    issuerCert,
                    publicKey,
                    generator,
                    extensionUrl : authorityInformationAccess);

                // merge signed cert with keystore
                var mergeResult = await _keyVaultClient.MergeCertificateAsync(
                    _vaultBaseUrl,
                    certName,
                    new X509Certificate2Collection(signedcert)
                    );

                X509Certificate2 keyPair = null;
                var secret = await _keyVaultClient.GetSecretAsync(mergeResult.SecretIdentifier.Identifier, ct);

                if (secret.ContentType == CertificateContentType.Pfx)
                {
                    var certBlob = Convert.FromBase64String(secret.Value);
                    keyPair = CertificateFactory.CreateCertificateFromPKCS12(certBlob, string.Empty);
                }
                else if (secret.ContentType == CertificateContentType.Pem)
                {
                    Encoding encoder    = Encoding.UTF8;
                    var      privateKey = encoder.GetBytes(secret.Value.ToCharArray());
                    keyPair = CertificateFactory.CreateCertificateWithPEMPrivateKey(signedcert, privateKey, string.Empty);
                }

                return(keyPair);
            }
            catch
            {
                throw new ServiceResultException(StatusCodes.BadInternalError, "Failed to create new key pair certificate");
            }
            finally
            {
                try
                {
                    var deletedCertBundle = await _keyVaultClient.DeleteCertificateAsync(_vaultBaseUrl, certName, ct);

                    await _keyVaultClient.PurgeDeletedCertificateAsync(_vaultBaseUrl, certName, ct);
                }
                catch
                {
                    // intentionally fall through, purge may fail
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Creates a new Root CA certificate in group id, tags it for trusted or issuer store.
        /// </summary>
        public async Task <X509Certificate2> CreateCACertificateAsync(
            string id,
            string subject,
            DateTime notBefore,
            DateTime notAfter,
            int keySize,
            int hashSize,
            bool trusted,
            string crlDistributionPoint = null,
            CancellationToken ct        = default)
        {
            try
            {
                // delete pending operations
                await _keyVaultClient.DeleteCertificateOperationAsync(_vaultBaseUrl, id);
            }
            catch
            {
                // intentionally ignore errors
            }

            string caTempCertIdentifier = null;

            try
            {
                // policy self signed, new key
                var policySelfSignedNewKey = CreateCertificatePolicy(subject, keySize, true, false);
                var tempAttributes         = CreateCertificateAttributes(DateTime.UtcNow.AddMinutes(-10), DateTime.UtcNow.AddMinutes(10));
                var createKey = await _keyVaultClient.CreateCertificateAsync(
                    _vaultBaseUrl,
                    id,
                    policySelfSignedNewKey,
                    tempAttributes,
                    null,
                    ct)
                                .ConfigureAwait(false);

                CertificateOperation operation;
                do
                {
                    await Task.Delay(1000);

                    operation = await _keyVaultClient.GetCertificateOperationAsync(_vaultBaseUrl, id, ct);
                } while (operation.Status == "inProgress" && !ct.IsCancellationRequested);
                if (operation.Status != "completed")
                {
                    throw new ServiceResultException(StatusCodes.BadUnexpectedError, "Failed to create new key pair.");
                }
                var createdCertificateBundle = await _keyVaultClient.GetCertificateAsync(_vaultBaseUrl, id).ConfigureAwait(false);

                var caCertKeyIdentifier = createdCertificateBundle.KeyIdentifier.Identifier;
                caTempCertIdentifier = createdCertificateBundle.CertificateIdentifier.Identifier;

                // policy unknown issuer, reuse key
                var policyUnknownReuse = CreateCertificatePolicy(subject, keySize, false, true);
                var attributes         = CreateCertificateAttributes(notBefore, notAfter);
                var tags = CreateCertificateTags(id, trusted);

                // create the CSR
                var createResult = await _keyVaultClient.CreateCertificateAsync(
                    _vaultBaseUrl,
                    id,
                    policyUnknownReuse,
                    attributes,
                    tags,
                    ct)
                                   .ConfigureAwait(false);

                if (createResult.Csr == null)
                {
                    throw new ServiceResultException(StatusCodes.BadInvalidArgument, "Failed to read CSR from CreateCertificate.");
                }

                // decode the CSR and verify consistency
                var pkcs10CertificationRequest = new Org.BouncyCastle.Pkcs.Pkcs10CertificationRequest(createResult.Csr);
                var info = pkcs10CertificationRequest.GetCertificationRequestInfo();
                if (createResult.Csr == null ||
                    pkcs10CertificationRequest == null ||
                    !pkcs10CertificationRequest.Verify())
                {
                    throw new ServiceResultException(StatusCodes.BadInvalidArgument, "Invalid CSR.");
                }

                // create the self signed root CA cert
                var publicKey  = KeyVaultCertFactory.GetRSAPublicKey(info.SubjectPublicKeyInfo);
                var signedcert = await KeyVaultCertFactory.CreateSignedCertificate(
                    null,
                    null,
                    subject,
                    null,
                    (ushort)keySize,
                    notBefore,
                    notAfter,
                    (ushort)hashSize,
                    null,
                    publicKey,
                    new KeyVaultSignatureGenerator(this, caCertKeyIdentifier, null),
                    true,
                    crlDistributionPoint);

                // merge Root CA cert with
                var mergeResult = await _keyVaultClient.MergeCertificateAsync(
                    _vaultBaseUrl,
                    id,
                    new X509Certificate2Collection(signedcert)
                    );

                return(signedcert);
            }
            catch (KeyVaultErrorException kex)
            {
                var ex = kex;
                throw new ServiceResultException(StatusCodes.BadInternalError, "Failed to create new Root CA certificate");
            }
            finally
            {
                if (caTempCertIdentifier != null)
                {
                    try
                    {
                        // disable the temp cert for self signing operation
                        var attr = new CertificateAttributes()
                        {
                            Enabled = false
                        };
                        await _keyVaultClient.UpdateCertificateAsync(caTempCertIdentifier, null, attr);
                    }
                    catch
                    {
                        // intentionally ignore error
                    }
                }
            }
        }
        /// <summary>
        /// Creates a KeyVault signed certficate from signing request.
        /// </summary>
        public override async Task <X509Certificate2> SigningRequestAsync(
            ApplicationRecordDataType application,
            string[] domainNames,
            byte[] certificateRequest
            )
        {
            try
            {
                var pkcs10CertificationRequest = new Org.BouncyCastle.Pkcs.Pkcs10CertificationRequest(certificateRequest);
                if (!pkcs10CertificationRequest.Verify())
                {
                    throw new ServiceResultException(StatusCodes.BadInvalidArgument, "CSR signature invalid.");
                }

                var info             = pkcs10CertificationRequest.GetCertificationRequestInfo();
                var altNameExtension = GetAltNameExtensionFromCSRInfo(info);
                if (altNameExtension != null)
                {
                    if (altNameExtension.Uris.Count > 0)
                    {
                        if (!altNameExtension.Uris.Contains(application.ApplicationUri))
                        {
                            throw new ServiceResultException(StatusCodes.BadCertificateUriInvalid,
                                                             "CSR AltNameExtension does not match " + application.ApplicationUri);
                        }
                    }

                    if (altNameExtension.IPAddresses.Count > 0 || altNameExtension.DomainNames.Count > 0)
                    {
                        var domainNameList = new List <string>();
                        domainNameList.AddRange(altNameExtension.DomainNames);
                        domainNameList.AddRange(altNameExtension.IPAddresses);
                        domainNames = domainNameList.ToArray();
                    }
                }

                var authorityInformationAccess = BuildAuthorityInformationAccessUrl();

                DateTime notBefore = DateTime.UtcNow.AddDays(-1);
                await LoadPublicAssets().ConfigureAwait(false);

                var signingCert = Certificate;
                {
                    var publicKey = KeyVaultCertFactory.GetRSAPublicKey(info.SubjectPublicKeyInfo);
                    return(await KeyVaultCertFactory.CreateSignedCertificate(
                               application.ApplicationUri,
                               application.ApplicationNames.Count > 0?application.ApplicationNames[0].Text : "ApplicationName",
                               info.Subject.ToString(),
                               domainNames,
                               Configuration.DefaultCertificateKeySize,
                               notBefore,
                               notBefore.AddMonths(Configuration.DefaultCertificateLifetime),
                               Configuration.DefaultCertificateHashSize,
                               signingCert,
                               publicKey,
                               new KeyVaultSignatureGenerator(_keyVaultServiceClient, _caCertKeyIdentifier, signingCert),
                               extensionUrl : authorityInformationAccess
                               ));
                }
            }
            catch (Exception ex)
            {
                if (ex is ServiceResultException)
                {
                    throw ex as ServiceResultException;
                }
                throw new ServiceResultException(StatusCodes.BadInvalidArgument, ex.Message);
            }
        }