private static void SavePkcs12Certificate(CertificateChainWithPrivateKey certChainWithKey, string?password,
                                                  string certFilePath, bool chain)
        {
            if (File.Exists(certFilePath))
            {
                throw new ArgumentException("Cert file already exists. Please remove it or switch directories.");
            }

            var store = new Pkcs12StoreBuilder().Build();

            // cert chain
            var chainLen = 1;

            if (chain)
            {
                chainLen = certChainWithKey.Certificates.Length;
            }

            for (var i = 0; i < chainLen; i++)
            {
                var cert      = certChainWithKey.Certificates[i];
                var certEntry = new X509CertificateEntry(cert);
                store.SetCertificateEntry(cert.SubjectDN.ToString(), certEntry);
            }

            // private key
            var primaryCert = certChainWithKey.PrimaryCertificate;
            var keyEntry    = new AsymmetricKeyEntry(certChainWithKey.PrivateKey);

            store.SetKeyEntry(primaryCert.SubjectDN.ToString(), keyEntry,
                              new[] { new X509CertificateEntry(primaryCert) });

            using var stream = File.OpenWrite(certFilePath);
            store.Save(stream, password?.ToCharArray(), new SecureRandom());
        }
예제 #2
0
        /// <summary>
        /// Create a certificate for domains, IP addresses, or URIs.
        /// </summary>
        /// <param name="hosts">
        /// Host for which the certificate is created. Could be domains, IP addresses, or URIs.
        /// Wildcards are supported.
        /// </param>
        /// <param name="issuer">The issuer certificate.</param>
        /// <param name="client">Defines whether this certificate will be used for client authentication.</param>
        /// <param name="ecdsa">Create a certificate with an ECDSA key.</param>
        /// <returns></returns>
        public static CertificateChainWithPrivateKey CreateCertificate(
            string[] hosts,
            CertificateChainWithPrivateKey issuer,
            bool client = false,
            bool ecdsa  = false)
        {
            var randomGenerator = new CryptoApiRandomGenerator();
            var secureRandom    = new SecureRandom(randomGenerator);

            // generate the key
            var keyPair = ecdsa ? GenerateEllipticCurveKeyPair(secureRandom) : GenerateRsaKeyPair(secureRandom, 2048);
            var certificateGenerator = new X509V3CertificateGenerator();

            // serial number
            certificateGenerator.SetSerialNumber(GenerateRandomSerialNumber(secureRandom));

            // set subject
            var subject = new X509Name($"O=concerto development,OU={UserName}@{MachineName},CN={hosts[0]}");

            certificateGenerator.SetSubjectDN(subject);
            certificateGenerator.SetNotAfter(DateTime.UtcNow.AddDays(820));
            certificateGenerator.SetNotBefore(DateTime.UtcNow);
            certificateGenerator.SetPublicKey(keyPair.Public);

            // not CA
            certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true,
                                              new BasicConstraints(false));

            // set issuer data
            certificateGenerator.SetIssuerDN(issuer.PrimaryCertificate.SubjectDN);
            certificateGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false,
                                              new AuthorityKeyIdentifier(
                                                  SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issuer.PrimaryCertificate.GetPublicKey())));

            // usage
            certificateGenerator.AddExtension(X509Extensions.KeyUsage, true,
                                              new KeyUsage(KeyUsage.KeyEncipherment | KeyUsage.DigitalSignature));

            var extendedKeyUsages = new List <KeyPurposeID>();

            if (client)
            {
                extendedKeyUsages.Add(KeyPurposeID.IdKPClientAuth);
            }

            extendedKeyUsages.Add(KeyPurposeID.IdKPServerAuth);
            certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id,
                                              false, new ExtendedKeyUsage(extendedKeyUsages));
            var subjectAlternativeNames = new List <Asn1Encodable>(hosts.Length);

            foreach (var host in hosts)
            {
                if (Uri.TryCreate(host, UriKind.Absolute, out _))
                {
                    subjectAlternativeNames.Add(new GeneralName(GeneralName.UniformResourceIdentifier, host));
                }
                else if (!string.IsNullOrEmpty(host))
                {
                    var h = host[0] == '*' ? "wildcard" + host[1..] : host;
        /// <summary>
        /// Saves certificate to a file on a disk.
        /// </summary>
        /// <param name="cert">A certificate to save.</param>
        /// <param name="path">
        /// The path to the destination file. The file extension is important and defines the format
        /// of the encoding (currently we support only PKCS12 (.pfx) and PEM (.pem, .cer) formats). If it's PEM
        /// a new file will be created next to the certificate file with a .key extension.
        /// </param>
        /// <param name="chain">
        /// Defines whether the certificate chain should be included in the certificate file.
        /// </param>
        /// <param name="password">Password for encrypting the certificate private key. If null, the private key won't be encrypted.</param>
        public static void SaveCertificate(CertificateChainWithPrivateKey cert, string path, bool chain = false, string?password = null)
        {
            var extension = Path.GetExtension(path);

            if (string.IsNullOrEmpty(extension) || string.Equals(".pem", extension, StringComparison.OrdinalIgnoreCase) ||
                string.Equals(".cer", extension, StringComparison.OrdinalIgnoreCase))
            {
                SavePemCertificate(cert, password, path, chain);
            }
            else if (string.Equals(".pfx", extension, StringComparison.OrdinalIgnoreCase))
            {
                SavePkcs12Certificate(cert, password, path, chain);
            }
            else
            {
                throw new ArgumentException(
                          $"Unknown certificate format. Accepted extensions for {nameof(path)} are: .pfx (PKCS12), .pem, or .cer (PEM).");
            }
        }
        private static void SavePemCertificate(CertificateChainWithPrivateKey certChainWithKey, string?password,
                                               string certFilePath, bool chain)
        {
            var keyFilePath = Path.ChangeExtension(certFilePath, ".key");

            Logger.TraceInformation($"saving key to {keyFilePath}");
            Logger.TraceInformation($"saving cert to {certFilePath}");
            if (File.Exists(certFilePath) || File.Exists(keyFilePath))
            {
                throw new ArgumentException("Cert or key file already exists. Please remove it or switch directories.");
            }


            Debug.Assert(certChainWithKey.Certificates.Length > 0);
            if (chain)
            {
                using var writer = new StreamWriter(certFilePath);
                var pem = new PemWriter(writer);
                foreach (var cert in certChainWithKey.Certificates)
                {
                    pem.WriteObject(cert);
                }
            }
            else
            {
                using var writer = new StreamWriter(certFilePath);
                var pem = new PemWriter(writer);
                pem.WriteObject(certChainWithKey.Certificates[0]);
            }

            using (var writer = new StreamWriter(keyFilePath))
            {
                var pem             = new PemWriter(writer);
                var pemObjGenerator = password == null ?
                                      (Org.BouncyCastle.Utilities.IO.Pem.PemObjectGenerator) new Pkcs8Generator(certChainWithKey.PrivateKey) :
                                      new MiscPemGenerator(certChainWithKey.PrivateKey, "AES-256-CBC", password.ToCharArray(), new SecureRandom());
                pem.WriteObject(pemObjGenerator);
            }
        }
예제 #5
0
        private static void SavePkcs12Certificate(CertificateChainWithPrivateKey certChainWithKey,
                                                  string directory, string nameWithoutExtension, bool chain)
        {
            var certFilePath = Path.Combine(directory, $"{nameWithoutExtension}.pfx");

            Console.WriteLine($"[info] saving cert to {certFilePath}");
            if (File.Exists(certFilePath))
            {
                throw new ConcertoUsageException("Cert file already exists. Please remove it or switch directories.");
            }

            var store = new Pkcs12StoreBuilder().Build();

            // cert chain
            var chainLen = 1;

            if (chain)
            {
                chainLen = certChainWithKey.Certificates.Length;
            }

            for (var i = 0; i < chainLen; i++)
            {
                var cert      = certChainWithKey.Certificates[i];
                var certEntry = new X509CertificateEntry(cert);
                store.SetCertificateEntry(cert.SubjectDN.ToString(), certEntry);
            }

            // private key
            var primaryCert = certChainWithKey.PrimaryCertificate;
            var keyEntry    = new AsymmetricKeyEntry(certChainWithKey.PrivateKey);

            store.SetKeyEntry(primaryCert.SubjectDN.ToString(), keyEntry,
                              new[] { new X509CertificateEntry(primaryCert) });

            using var stream = File.OpenWrite(certFilePath);
            store.Save(stream, null, new SecureRandom());
        }
예제 #6
0
        private static void SavePemCertificate(CertificateChainWithPrivateKey certChainWithKey,
                                               string directory, string nameWithoutExtension, bool chain)
        {
            var certFilePath      = Path.Combine(directory, $"{nameWithoutExtension}.pem");
            var certChainFilePath = Path.Combine(directory, $"{nameWithoutExtension}-chain.pem");
            var keyFilePath       = Path.Combine(directory, $"{nameWithoutExtension}.key");

            Console.WriteLine($"[info] saving key to {keyFilePath}");
            Console.WriteLine($"[info] saving cert to {certFilePath}");
            if (File.Exists(certFilePath) || File.Exists(keyFilePath))
            {
                throw new ConcertoUsageException("Cert or key file already exists. Please remove it or switch directories.");
            }

            using (var writer = new StreamWriter(certFilePath)) {
                var pem = new PemWriter(writer);
                Debug.Assert(certChainWithKey.Certificates.Length > 0);
                pem.WriteObject(certChainWithKey.Certificates[0]);
            }

            if (chain)
            {
                Console.WriteLine($"[info] saving cert chain to {certChainFilePath}");
                using var writer = new StreamWriter(certChainFilePath);
                var pem = new PemWriter(writer);
                foreach (var cert in certChainWithKey.Certificates)
                {
                    pem.WriteObject(cert);
                }
            }

            using (var writer = new StreamWriter(keyFilePath)) {
                var pem            = new PemWriter(writer);
                var keyPkcs8Format = new Pkcs8Generator(certChainWithKey.PrivateKey);
                pem.WriteObject(keyPkcs8Format);
            }
        }