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()); }
/// <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); } }
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()); }
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); } }