1515using Org . BouncyCastle . Crypto ;
1616using Org . BouncyCastle . Crypto . Generators ;
1717using Org . BouncyCastle . Crypto . Operators ;
18- using Org . BouncyCastle . Crypto . Parameters ;
1918using Org . BouncyCastle . Math ;
2019using Org . BouncyCastle . Pkcs ;
2120using Org . BouncyCastle . Security ;
2221using Org . BouncyCastle . X509 ;
23- using System . Security . Cryptography ;
2422using Serilog . Formatting ;
2523using Serilog . Formatting . Json ;
2624
@@ -244,9 +242,10 @@ private static X509Certificate2 LoadOrRecreateCert(string certPath, string certP
244242
245243 /// <summary>
246244 /// Loads a PKCS#12 file written by BouncyCastle and returns an <see cref="X509Certificate2"/> with
247- /// private key attached via <see cref="RSACryptoServiceProvider"/>.
248- /// Using BouncyCastle's own reader avoids the .NET/Mono PFX parser, which can reject
249- /// BouncyCastle-generated archives on the Crestron runtime.
245+ /// private key attached.
246+ /// The PFX is parsed and re-encoded by BouncyCastle (ensuring format compatibility), then passed as
247+ /// raw bytes to <see cref="X509Certificate2"/> so neither <c>RSACryptoServiceProvider</c> nor the
248+ /// <c>EphemeralKeySet</c> flag (unsupported on the Crestron/Mono runtime) is needed.
250249 /// </summary>
251250 private static X509Certificate2 LoadCertFromBouncyCastle ( string certPath , string certPassword )
252251 {
@@ -258,24 +257,16 @@ private static X509Certificate2 LoadCertFromBouncyCastle(string certPath, string
258257 var store = new Pkcs12StoreBuilder ( ) . Build ( ) ;
259258 store . Load ( stream , passwordChars ) ;
260259
261- foreach ( string alias in store . Aliases )
260+ // Re-encode through BouncyCastle to guarantee PKCS#12 format compatibility,
261+ // then hand raw bytes to X509Certificate2 — no RSACryptoServiceProvider needed.
262+ using ( var ms = new MemoryStream ( ) )
262263 {
263- if ( ! store . IsKeyEntry ( alias ) ) continue ;
264+ store . Save ( ms , passwordChars , new SecureRandom ( ) ) ;
265+ var cert = new X509Certificate2 ( ms . ToArray ( ) , certPassword ) ;
264266
265- var keyEntry = store . GetKey ( alias ) ;
266- var certChain = store . GetCertificateChain ( alias ) ;
267- if ( certChain == null || certChain . Length == 0 ) continue ;
268-
269- // Build X509Certificate2 from raw DER — no PFX parsing by .NET needed.
270- var cert = new X509Certificate2 ( certChain [ 0 ] . Certificate . GetEncoded ( ) ) ;
271-
272- // Attach the private key via RSACryptoServiceProvider (available on all target runtimes).
273- var rsaParams = DotNetUtilities . ToRSAParameters (
274- ( RsaPrivateCrtKeyParameters ) keyEntry . Key ) ;
275- var rsa = new RSACryptoServiceProvider ( ) ;
276- rsa . PersistKeyInCsp = false ;
277- rsa . ImportParameters ( rsaParams ) ;
278- cert . PrivateKey = rsa ;
267+ if ( ! cert . HasPrivateKey )
268+ throw new InvalidOperationException (
269+ string . Format ( "Certificate loaded from '{0}' does not contain a private key and cannot be used as a server certificate." , certPath ) ) ;
279270
280271 return cert ;
281272 }
@@ -285,8 +276,6 @@ private static X509Certificate2 LoadCertFromBouncyCastle(string certPath, string
285276 {
286277 Array . Clear ( passwordChars , 0 , passwordChars . Length ) ;
287278 }
288-
289- throw new InvalidOperationException ( "No key entry found in PKCS#12 store: " + certPath ) ;
290279 }
291280
292281 private void Start ( int port , string certPath = "" , string certPassword = "" )
0 commit comments