1313// limitations under the License.
1414
1515using System ;
16+ using System . Collections . Generic ;
1617using System . IO ;
1718using System . Linq ;
1819using System . Security . Cryptography . X509Certificates ;
2122using Keyfactor . Logging ;
2223using Keyfactor . Orchestrators . Common . Enums ;
2324using Keyfactor . Orchestrators . Extensions ;
25+ using Keyfactor . PKI . X509 ;
2426using Microsoft . Extensions . Logging ;
2527using Org . BouncyCastle . Crypto ;
2628using Org . BouncyCastle . OpenSsl ;
@@ -66,16 +68,16 @@ protected virtual async Task<JobResult> PerformAddition(string alias, string pfx
6668 /// if there is a cert and key, import as "pair"
6769 /// if there is no key, and the issuer and subject match, import as "clca" (trusted CA)
6870 /// if there is no key, and the issuer and subject are different, import as "inca" (intermediate CA)
69-
71+
7072 byte [ ] bytes ;
7173 X509Certificate2 x509 ;
72- string pemCert , privateKeyString ;
74+ string pemCert , pemKey ;
7375
7476 try
7577 {
7678 bytes = Convert . FromBase64String ( entryContents ) ;
7779 x509 = new X509Certificate2 ( bytes , pfxPassword , X509KeyStorageFlags . Exportable ) ;
78- ( pemCert , privateKeyString ) = GetPemFromPfx ( bytes , pfxPassword . ToCharArray ( ) ) ;
80+ ( pemCert , pemKey ) = GetPemFromPfx ( bytes , pfxPassword ) ;
7981 }
8082 catch ( Exception ex )
8183 {
@@ -117,7 +119,7 @@ protected virtual async Task<JobResult> PerformAddition(string alias, string pfx
117119 // add key and cert separately.
118120 // this needs to be done in the following order: key, then cert (per Alteon support)
119121 logger . LogTrace ( $ "adding key and then certificate for certificate with alias { alias } ") ;
120- await aClient . AddCertificate ( alias , pfxPassword , Pemify ( privateKeyString ) , AlteonCertTypes . KEY_ONLY ) ;
122+ await aClient . AddCertificate ( alias , pfxPassword , pemKey , AlteonCertTypes . KEY_ONLY ) ;
121123 await aClient . AddCertificate ( alias , pfxPassword , pemCert , AlteonCertTypes . CERT_ONLY ) ;
122124 }
123125 else
@@ -158,7 +160,7 @@ protected virtual async Task<JobResult> PerformRemoval(string alias, long jobHis
158160 try
159161 {
160162 await aClient . RemoveCertificate ( alias ) ;
161- complete . Result = OrchestratorJobStatusJobResult . Success ;
163+ complete . Result = OrchestratorJobStatusJobResult . Success ;
162164 }
163165
164166 catch ( Exception ex )
@@ -169,61 +171,70 @@ protected virtual async Task<JobResult> PerformRemoval(string alias, long jobHis
169171 return complete ;
170172 }
171173
172- private ( string , string ) GetPemFromPfx ( byte [ ] pfxBytes , char [ ] pfxPassword )
174+ private ( string , string ) GetPemFromPfx ( byte [ ] pfxBytes , string pfxPassword )
173175 {
174176 try
175177 {
176- logger . LogDebug ( "Entering GetPemFromPfx(byte[] pfxBytes, char[] pfxPassword)" ) ;
177- var p = new Pkcs12Store ( new MemoryStream ( pfxBytes ) , pfxPassword ) ;
178-
179- // Extract private key
180- var memoryStream = new MemoryStream ( ) ;
181- TextWriter streamWriter = new StreamWriter ( memoryStream ) ;
182- var pemWriter = new PemWriter ( streamWriter ) ;
183-
184- var alias = p . Aliases . Cast < string > ( ) . SingleOrDefault ( a => p . IsKeyEntry ( a ) ) ;
185- logger . LogTrace ( $ "alias: { alias } ") ;
186-
187- var publicKey = p . GetCertificate ( alias ) . Certificate . GetPublicKey ( ) ;
188- if ( p . GetKey ( alias ) == null ) throw new Exception ( $ "Unable to get the key for alias: { alias } ") ;
189- var privateKey = p . GetKey ( alias ) . Key ;
190- var keyPair = new AsymmetricCipherKeyPair ( publicKey , privateKey ) ;
191-
192- pemWriter . WriteObject ( keyPair . Private ) ;
193- streamWriter . Flush ( ) ;
194- var privateKeyString = Encoding . ASCII . GetString ( memoryStream . GetBuffer ( ) ) . Trim ( ) . Replace ( "\r " , "" )
195- . Replace ( "\0 " , "" ) ;
196- memoryStream . Close ( ) ;
197- streamWriter . Close ( ) ;
198-
199- // Extract server certificate
200- var certStart = "-----BEGIN CERTIFICATE-----\n " ;
201- var certEnd = "\n -----END CERTIFICATE-----" ;
202-
203- string Pemify ( string ss )
178+ logger . MethodEntry ( ) ;
179+
180+ CertificateCollectionConverter converter = CertificateCollectionConverterFactory . FromDER ( pfxBytes , pfxPassword ) ;
181+ string pfxPem = converter . ToPEM ( pfxPassword ) ;
182+ List < X509Certificate2 > clist = converter . ToX509Certificate2List ( pfxPassword ) ;
183+ StringBuilder certPemBuilder = new StringBuilder ( ) ;
184+
185+ //reordering of certificate chain necessary because of BouncyCastle bug. Being fixed in a later release
186+ if ( clist . Count > 1 )
187+ clist = ReorderPEMLIst ( clist ) ;
188+
189+ logger . LogTrace ( "Building certificate PEM" ) ;
190+ foreach ( X509Certificate2 cert in clist )
204191 {
205- return ss . Length <= 64 ? ss : ss . Substring ( 0 , 64 ) + "\n " + Pemify ( ss . Substring ( 64 ) ) ;
192+ certPemBuilder . AppendLine ( "-----BEGIN CERTIFICATE-----" ) ;
193+ certPemBuilder . AppendLine (
194+ Convert . ToBase64String ( cert . RawData , Base64FormattingOptions . InsertLineBreaks ) ) ;
195+ certPemBuilder . AppendLine ( "-----END CERTIFICATE-----" ) ;
206196 }
207197
208- var certPem =
209- certStart + Pemify ( Convert . ToBase64String ( p . GetCertificate ( alias ) . Certificate . GetEncoded ( ) ) ) +
210- certEnd ;
211- logger . LogTrace ( $ "certPem: { certPem } ") ;
212- logger . LogDebug ( "Exiting GetPemFromPfx(byte[] pfxBytes, char[] pfxPassword)" ) ;
213- return ( certPem , privateKeyString ) ;
198+ logger . LogTrace ( "Building the key PEM" ) ;
199+ byte [ ] pkBytes = PKI . PrivateKeys . PrivateKeyConverterFactory . FromPKCS12 ( pfxBytes , pfxPassword . ToString ( ) ) . ToPkcs8BlobUnencrypted ( ) ;
200+ StringBuilder keyPemBuilder = new StringBuilder ( ) ;
201+ keyPemBuilder . AppendLine ( "-----BEGIN PRIVATE KEY-----" ) ;
202+ keyPemBuilder . AppendLine (
203+ Convert . ToBase64String ( pkBytes , Base64FormattingOptions . InsertLineBreaks ) ) ;
204+ keyPemBuilder . AppendLine ( "-----END PRIVATE KEY-----" ) ;
205+
206+ logger . LogTrace ( $ "certPem: { certPemBuilder } ") ;
207+ logger . MethodExit ( ) ;
208+ return ( certPemBuilder . ToString ( ) , keyPemBuilder . ToString ( ) ) ;
214209 }
215210 catch ( Exception e )
216211 {
217212 logger . LogError (
218- $ "Error Occurred in GetPemFromPfx(byte[] pfxBytes, char[] pfxPassword): { LogHandler . FlattenException ( e ) } ") ;
213+ $ "Error Occurred in GetPemFromPfx(byte[] pfxBytes, string pfxPassword): { LogHandler . FlattenException ( e ) } ") ;
219214 throw ;
220215 }
221216 }
222-
223- private string Pemify ( string ss )
217+ private List < X509Certificate2 > ReorderPEMLIst ( List < X509Certificate2 > certList )
224218 {
225- return ss . Length <= 64 ? ss : ss . Substring ( 0 , 64 ) + "\n " + Pemify ( ss . Substring ( 64 ) ) ;
226- }
219+ List < X509Certificate2 > rtnList = new List < X509Certificate2 > ( ) ;
220+ X509Certificate2 root = certList . FirstOrDefault ( p => p . IssuerName . RawData . SequenceEqual ( p . SubjectName . RawData ) ) ;
221+ if ( root == null || string . IsNullOrEmpty ( root . SerialNumber ) )
222+ throw new Exception ( "Invalid certificate chain. No root CA certificate found." ) ;
227223
224+ rtnList . Add ( root ) ;
225+
226+ X509Certificate2 parentCert = root ;
227+ for ( int i = 1 ; i < certList . Count ; i ++ )
228+ {
229+ X509Certificate2 childCert = certList . FirstOrDefault ( p => p . IssuerName . RawData . SequenceEqual ( parentCert . SubjectName . RawData ) && ! p . IssuerName . RawData . SequenceEqual ( p . SubjectName . RawData ) ) ;
230+ if ( root == null || string . IsNullOrEmpty ( root . SerialNumber ) )
231+ throw new Exception ( "Invalid certificate chain. End entity or issuing CA certificate not found." ) ;
232+
233+ rtnList . Insert ( 0 , childCert ) ;
234+ parentCert = childCert ;
235+ }
236+
237+ return rtnList ;
238+ }
228239 }
229240}
0 commit comments