|
8 | 8 | using WebSocketSharp; |
9 | 9 | using System.Security.Authentication; |
10 | 10 | using WebSocketSharp.Net; |
11 | | -using System.Security.Cryptography; |
12 | 11 | using System.Security.Cryptography.X509Certificates; |
13 | 12 | using System.IO; |
| 13 | +using Org.BouncyCastle.Asn1; |
14 | 14 | using Org.BouncyCastle.Asn1.X509; |
| 15 | +using Org.BouncyCastle.Crypto; |
| 16 | +using Org.BouncyCastle.Crypto.Generators; |
| 17 | +using Org.BouncyCastle.Crypto.Operators; |
| 18 | +using Org.BouncyCastle.Math; |
| 19 | +using Org.BouncyCastle.Pkcs; |
| 20 | +using Org.BouncyCastle.Security; |
| 21 | +using Org.BouncyCastle.X509; |
15 | 22 | using Serilog.Formatting; |
16 | 23 | using Serilog.Formatting.Json; |
17 | 24 |
|
@@ -114,51 +121,62 @@ private static void CreateCert() |
114 | 121 | var subjectName = string.Format("CN={0}.{1}", hostName, domainName); |
115 | 122 | var fqdn = string.Format("{0}.{1}", hostName, domainName); |
116 | 123 |
|
117 | | - using (var rsa = RSA.Create(2048)) |
| 124 | + var random = new SecureRandom(); |
| 125 | + |
| 126 | + // Generate RSA 2048 key pair |
| 127 | + var keyPairGenerator = new RsaKeyPairGenerator(); |
| 128 | + keyPairGenerator.Init(new KeyGenerationParameters(random, 2048)); |
| 129 | + var keyPair = keyPairGenerator.GenerateKeyPair(); |
| 130 | + |
| 131 | + // Build certificate |
| 132 | + var certGenerator = new X509V3CertificateGenerator(); |
| 133 | + certGenerator.SetSerialNumber(BigInteger.ValueOf(Math.Abs(DateTime.UtcNow.Ticks))); |
| 134 | + certGenerator.SetIssuerDN(new X509Name(subjectName)); |
| 135 | + certGenerator.SetSubjectDN(new X509Name(subjectName)); |
| 136 | + certGenerator.SetNotBefore(DateTime.UtcNow); |
| 137 | + certGenerator.SetNotAfter(DateTime.UtcNow.AddYears(2)); |
| 138 | + certGenerator.SetPublicKey(keyPair.Public); |
| 139 | + |
| 140 | + // Extended Key Usage: server + client auth |
| 141 | + certGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, false, |
| 142 | + new ExtendedKeyUsage(new[] { KeyPurposeID.id_kp_serverAuth, KeyPurposeID.id_kp_clientAuth })); |
| 143 | + |
| 144 | + // Subject Alternative Names: DNS + IP |
| 145 | + System.Net.IPAddress parsedIp; |
| 146 | + if (System.Net.IPAddress.TryParse(ipAddress, out parsedIp)) |
118 | 147 | { |
| 148 | + certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false, |
| 149 | + new GeneralNames(new GeneralName[] { |
| 150 | + new GeneralName(GeneralName.DnsName, fqdn), |
| 151 | + new GeneralName(GeneralName.IPAddress, ipAddress) |
| 152 | + })); |
| 153 | + } |
| 154 | + else |
| 155 | + { |
| 156 | + certGenerator.AddExtension(X509Extensions.SubjectAlternativeName, false, |
| 157 | + new GeneralNames(new GeneralName(GeneralName.DnsName, fqdn))); |
| 158 | + } |
119 | 159 |
|
120 | | - var request = new CertificateRequest( |
121 | | - subjectName, |
122 | | - rsa, |
123 | | - HashAlgorithmName.SHA256, |
124 | | - RSASignaturePadding.Pkcs1); |
125 | | - |
126 | | - // Subject Key Identifier |
127 | | - request.CertificateExtensions.Add( |
128 | | - new X509SubjectKeyIdentifierExtension(request.PublicKey, false)); |
129 | | - |
130 | | - // Extended Key Usage: server + client auth |
131 | | - request.CertificateExtensions.Add( |
132 | | - new X509EnhancedKeyUsageExtension( |
133 | | - new OidCollection |
134 | | - { |
135 | | - new Oid("1.3.6.1.5.5.7.3.1"), // id-kp-serverAuth |
136 | | - new Oid("1.3.6.1.5.5.7.3.2") // id-kp-clientAuth |
137 | | - }, |
138 | | - false)); |
139 | | - |
140 | | - // Subject Alternative Names: DNS + IP |
141 | | - var sanBuilder = new SubjectAlternativeNameBuilder(); |
142 | | - sanBuilder.AddDnsName(fqdn); |
143 | | - if (System.Net.IPAddress.TryParse(ipAddress, out var ip)) |
144 | | - sanBuilder.AddIpAddress(ip); |
145 | | - request.CertificateExtensions.Add(sanBuilder.Build()); |
146 | | - |
147 | | - var notBefore = DateTimeOffset.UtcNow; |
148 | | - var notAfter = notBefore.AddYears(2); |
149 | | - |
150 | | - using (var cert = request.CreateSelfSigned(notBefore, notAfter)) |
151 | | - { |
| 160 | + // Sign with SHA256withRSA |
| 161 | + var signatureFactory = new Asn1SignatureFactory("SHA256WITHRSA", keyPair.Private, random); |
| 162 | + var certificate = certGenerator.Generate(signatureFactory); |
152 | 163 |
|
153 | | - var separator = Path.DirectorySeparatorChar; |
154 | | - var outputPath = string.Format("{0}user{1}{2}.pfx", separator, separator, _certificateName); |
| 164 | + // Export as PKCS12/PFX |
| 165 | + var pkcs12Store = new Pkcs12StoreBuilder().Build(); |
| 166 | + var certEntry = new X509CertificateEntry(certificate); |
| 167 | + pkcs12Store.SetCertificateEntry(_certificateName, certEntry); |
| 168 | + pkcs12Store.SetKeyEntry(_certificateName, new AsymmetricKeyEntry(keyPair.Private), new[] { certEntry }); |
155 | 169 |
|
156 | | - var pfxBytes = cert.Export(X509ContentType.Pfx, _certificatePassword); |
157 | | - File.WriteAllBytes(outputPath, pfxBytes); |
| 170 | + var separator = Path.DirectorySeparatorChar; |
| 171 | + var outputPath = string.Format("{0}user{1}{2}.pfx", separator, separator, _certificateName); |
158 | 172 |
|
159 | | - CrestronConsole.PrintLine(string.Format("CreateCert: Certificate written to {0}", outputPath)); |
160 | | - } |
| 173 | + using (var ms = new MemoryStream()) |
| 174 | + { |
| 175 | + pkcs12Store.Save(ms, _certificatePassword.ToCharArray(), random); |
| 176 | + File.WriteAllBytes(outputPath, ms.ToArray()); |
161 | 177 | } |
| 178 | + |
| 179 | + CrestronConsole.PrintLine(string.Format("CreateCert: Certificate written to {0}", outputPath)); |
162 | 180 | } |
163 | 181 | catch (Exception ex) |
164 | 182 | { |
|
0 commit comments