Esempio n. 1
0
        internal void ExportCertificate(X509Certificate2 certificate, string path, bool includePrivateKey, string password, CertificateKeyExportFormat format)
        {
            Log.ExportCertificateStart(GetDescription(certificate), path, includePrivateKey);
            if (includePrivateKey && password == null)
            {
                Log.NoPasswordForCertificate();
            }

            var targetDirectoryPath = Path.GetDirectoryName(path);

            if (targetDirectoryPath != "")
            {
                Log.CreateExportCertificateDirectory(targetDirectoryPath);
                Directory.CreateDirectory(targetDirectoryPath);
            }

            byte[] bytes;
            byte[] keyBytes;
            byte[] pemEnvelope = null;
            RSA    key         = null;

            try
            {
                if (includePrivateKey)
                {
                    switch (format)
                    {
                    case CertificateKeyExportFormat.Pfx:
                        bytes = certificate.Export(X509ContentType.Pkcs12, password);
                        break;

                    case CertificateKeyExportFormat.Pem:
                        key = certificate.GetRSAPrivateKey();

                        char[] pem;
                        if (password != null)
                        {
                            keyBytes    = key.ExportEncryptedPkcs8PrivateKey(password, new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 100000));
                            pem         = PemEncoding.Write("ENCRYPTED PRIVATE KEY", keyBytes);
                            pemEnvelope = Encoding.ASCII.GetBytes(pem);
                        }
                        else
                        {
                            // Export the key first to an encrypted PEM to avoid issues with System.Security.Cryptography.Cng indicating that the operation is not supported.
                            // This is likely by design to avoid exporting the key by mistake.
                            // To bypass it, we export the certificate to pem temporarily and then we import it and export it as unprotected PEM.
                            keyBytes = key.ExportEncryptedPkcs8PrivateKey("", new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 1));
                            pem      = PemEncoding.Write("ENCRYPTED PRIVATE KEY", keyBytes);
                            key.Dispose();
                            key = RSA.Create();
                            key.ImportFromEncryptedPem(pem, "");
                            Array.Clear(keyBytes, 0, keyBytes.Length);
                            Array.Clear(pem, 0, pem.Length);
                            keyBytes    = key.ExportPkcs8PrivateKey();
                            pem         = PemEncoding.Write("PRIVATE KEY", keyBytes);
                            pemEnvelope = Encoding.ASCII.GetBytes(pem);
                        }

                        Array.Clear(keyBytes, 0, keyBytes.Length);
                        Array.Clear(pem, 0, pem.Length);

                        bytes = certificate.Export(X509ContentType.Cert);
                        break;

                    default:
                        throw new InvalidOperationException("Unknown format.");
                    }
                }
                else
                {
                    bytes = certificate.Export(X509ContentType.Cert);
                }
            }
            catch (Exception e)
            {
                Log.ExportCertificateError(e.ToString());
                throw;
            }
            finally
            {
                key?.Dispose();
            }

            try
            {
                Log.WriteCertificateToDisk(path);
                File.WriteAllBytes(path, bytes);
            }
            catch (Exception ex)
            {
                Log.WriteCertificateToDiskError(ex.ToString());
                throw;
            }
            finally
            {
                Array.Clear(bytes, 0, bytes.Length);
            }

            if (includePrivateKey && format == CertificateKeyExportFormat.Pem)
            {
                try
                {
                    var keyPath = Path.ChangeExtension(path, ".key");
                    Log.WritePemKeyToDisk(keyPath);
                    File.WriteAllBytes(keyPath, pemEnvelope);
                }
                catch (Exception ex)
                {
                    Log.WritePemKeyToDiskError(ex.ToString());
                    throw;
                }
                finally
                {
                    Array.Clear(pemEnvelope, 0, pemEnvelope.Length);
                }
            }
        }
Esempio n. 2
0
        public EnsureCertificateResult EnsureAspNetCoreHttpsDevelopmentCertificate(
            DateTimeOffset notBefore,
            DateTimeOffset notAfter,
            string path            = null,
            bool trust             = false,
            bool includePrivateKey = false,
            string password        = null,
            CertificateKeyExportFormat keyExportFormat = CertificateKeyExportFormat.Pfx,
            bool isInteractive = true)
        {
            var result = EnsureCertificateResult.Succeeded;

            var currentUserCertificates = ListCertificates(StoreName.My, StoreLocation.CurrentUser, isValid: true, requireExportable: true);
            var trustedCertificates     = ListCertificates(StoreName.My, StoreLocation.LocalMachine, isValid: true, requireExportable: true);
            var certificates            = currentUserCertificates.Concat(trustedCertificates);

            var filteredCertificates = certificates.Where(c => c.Subject == Subject);
            var excludedCertificates = certificates.Except(filteredCertificates);

            Log.FilteredCertificates(ToCertificateDescription(filteredCertificates));
            Log.ExcludedCertificates(ToCertificateDescription(excludedCertificates));

            certificates = filteredCertificates;

            X509Certificate2 certificate = null;
            var isNewCertificate         = false;

            if (certificates.Any())
            {
                certificate = certificates.First();
                var failedToFixCertificateState = false;
                if (isInteractive)
                {
                    // Skip this step if the command is not interactive,
                    // as we don't want to prompt on first run experience.
                    foreach (var candidate in currentUserCertificates)
                    {
                        var status = CheckCertificateState(candidate, true);
                        if (!status.Result)
                        {
                            try
                            {
                                Log.CorrectCertificateStateStart(GetDescription(candidate));
                                CorrectCertificateState(candidate);
                                Log.CorrectCertificateStateEnd();
                            }
                            catch (Exception e)
                            {
                                Log.CorrectCertificateStateError(e.ToString());
                                result = EnsureCertificateResult.FailedToMakeKeyAccessible;
                                // We don't return early on this type of failure to allow for tooling to
                                // export or trust the certificate even in this situation, as that enables
                                // exporting the certificate to perform any necessary fix with native tooling.
                                failedToFixCertificateState = true;
                            }
                        }
                    }
                }

                if (!failedToFixCertificateState)
                {
                    Log.ValidCertificatesFound(ToCertificateDescription(certificates));
                    certificate = certificates.First();
                    Log.SelectedCertificate(GetDescription(certificate));
                    result = EnsureCertificateResult.ValidCertificatePresent;
                }
            }
            else
            {
                Log.NoValidCertificatesFound();
                try
                {
                    Log.CreateDevelopmentCertificateStart();
                    isNewCertificate = true;
                    certificate      = CreateAspNetCoreHttpsDevelopmentCertificate(notBefore, notAfter);
                }
                catch (Exception e)
                {
                    Log.CreateDevelopmentCertificateError(e.ToString());
                    result = EnsureCertificateResult.ErrorCreatingTheCertificate;
                    return(result);
                }
                Log.CreateDevelopmentCertificateEnd();

                try
                {
                    certificate = SaveCertificate(certificate);
                }
                catch (Exception e)
                {
                    Log.SaveCertificateInStoreError(e.ToString());
                    result = EnsureCertificateResult.ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore;
                    return(result);
                }

                if (isInteractive)
                {
                    try
                    {
                        Log.CorrectCertificateStateStart(GetDescription(certificate));
                        CorrectCertificateState(certificate);
                        Log.CorrectCertificateStateEnd();
                    }
                    catch (Exception e)
                    {
                        Log.CorrectCertificateStateError(e.ToString());
                        // We don't return early on this type of failure to allow for tooling to
                        // export or trust the certificate even in this situation, as that enables
                        // exporting the certificate to perform any necessary fix with native tooling.
                        result = EnsureCertificateResult.FailedToMakeKeyAccessible;
                    }
                }
            }

            if (path != null)
            {
                try
                {
                    ExportCertificate(certificate, path, includePrivateKey, password, keyExportFormat);
                }
                catch (Exception e)
                {
                    Log.ExportCertificateError(e.ToString());
                    // We don't want to mask the original source of the error here.
                    result = result != EnsureCertificateResult.Succeeded && result != EnsureCertificateResult.ValidCertificatePresent ?
                             result :
                             EnsureCertificateResult.ErrorExportingTheCertificate;

                    return(result);
                }
            }

            if (trust)
            {
                try
                {
                    TrustCertificate(certificate);
                }
                catch (UserCancelledTrustException)
                {
                    result = EnsureCertificateResult.UserCancelledTrustStep;
                    return(result);
                }
                catch
                {
                    result = EnsureCertificateResult.FailedToTrustTheCertificate;
                    return(result);
                }
            }

            DisposeCertificates(!isNewCertificate ? certificates : certificates.Append(certificate));

            return(result);
        }