1515using Keyfactor . Orchestrators . Common . Enums ;
1616using Keyfactor . Orchestrators . Extensions ;
1717using Microsoft . CodeAnalysis ;
18+ using Microsoft . CodeAnalysis . FlowAnalysis ;
1819using Microsoft . Extensions . Logging ;
1920using System ;
2021using System . IO ;
22+ using System . Linq . Expressions ;
2123using System . Management . Automation ;
2224using System . Management . Automation . Runspaces ;
2325using System . Security . Cryptography . X509Certificates ;
@@ -48,6 +50,10 @@ public ClientPSCertStoreManager(ILogger logger, Runspace runSpace, long jobNumbe
4850
4951 public string CreatePFXFile ( string certificateContents , string privateKeyPassword )
5052 {
53+ _logger . LogTrace ( "Entering CreatePFXFile" ) ;
54+ if ( ! string . IsNullOrEmpty ( privateKeyPassword ) ) { _logger . LogTrace ( "privateKeyPassword was present" ) ; }
55+ else _logger . LogTrace ( "No privateKeyPassword Presented" ) ;
56+
5157 try
5258 {
5359 // Create the x509 certificate
@@ -79,11 +85,13 @@ public string CreatePFXFile(string certificateContents, string privateKeyPasswor
7985 var results = ps . Invoke ( ) ;
8086
8187 // Get the result (temporary file path) returned by the script
88+ _logger . LogTrace ( $ "Results after creating PFX File: { results [ 0 ] . ToString ( ) } ") ;
8289 return results [ 0 ] . ToString ( ) ;
8390 }
8491 }
85- catch ( Exception )
92+ catch ( Exception ex )
8693 {
94+ _logger . LogError ( ex . ToString ( ) ) ;
8795 throw new Exception ( "An error occurred while attempting to create and write the X509 contents." ) ;
8896 }
8997 }
@@ -108,55 +116,137 @@ public void DeletePFXFile(string filePath, string fileName)
108116 }
109117 }
110118
111- public JobResult ImportPFXFile ( string filePath , string privateKeyPassword , string cryptoProviderName )
119+ public JobResult ImportPFXFile ( string filePath , string privateKeyPassword , string cryptoProviderName , string storePath )
112120 {
113121 try
114122 {
123+ _logger . LogTrace ( "Entering ImportPFX" ) ;
124+
115125 using ( PowerShell ps = PowerShell . Create ( ) )
116126 {
117127 ps . Runspace = _runspace ;
118128
119129 if ( cryptoProviderName == null )
120130 {
121- string script = @"
122- param($pfxFilePath, $privateKeyPassword, $cspName)
123- $output = certutil -importpfx -p $privateKeyPassword $pfxFilePath 2>&1
124- $c = $LASTEXITCODE
125- $output
126- " ;
131+ if ( privateKeyPassword == null )
132+ {
133+ // If no private key password is provided, import the pfx file directory to the store using addstore argument
134+ string script = @"
135+ param($pfxFilePath, $storePath)
136+ $output = certutil -addstore $storePath $pfxFilePath 2>&1
137+ $exit_message = ""LASTEXITCODE:$($LASTEXITCODE)""
138+
139+ if ($output.GetType().Name -eq ""String"")
140+ {
141+ $output = @($output, $exit_message)
142+ }
143+ else
144+ {
145+ $output += $exit_message
146+ }
147+ $output
148+ " ;
149+
150+ ps . AddScript ( script ) ;
151+ ps . AddParameter ( "pfxFilePath" , filePath ) ;
152+ ps . AddParameter ( "storePath" , storePath ) ;
153+ }
154+ else
155+ {
156+ // Use ImportPFX to import the pfx file with private key password to the appropriate cert store
157+
158+ string script = @"
159+ param($pfxFilePath, $privateKeyPassword)
160+ $output = certutil -importpfx -p $privateKeyPassword $storePath $pfxFilePath 2>&1
161+ $exit_message = ""LASTEXITCODE:$($LASTEXITCODE)""
162+ $stuff = certutil -dump
163+ if ($stuff.GetType().Name -eq ""String"")
164+ {
165+ $stuff = @($stuff, $exit_message)
166+ }
167+ else
168+ {
169+ $stuff += $exit_message
170+ }
171+ $output
172+ $stuff
173+ " ;
127174
128- ps . AddScript ( script ) ;
129- ps . AddParameter ( "pfxFilePath" , filePath ) ;
130- ps . AddParameter ( "privateKeyPassword" , privateKeyPassword ) ;
175+ ps . AddScript ( script ) ;
176+ ps . AddParameter ( "pfxFilePath" , filePath ) ;
177+ ps . AddParameter ( "privateKeyPassword" , privateKeyPassword ) ;
178+ ps . AddParameter ( "storePath" , storePath ) ;
179+ }
131180 }
132181 else
133182 {
134- string script = @"
135- param($pfxFilePath, $privateKeyPassword, $cspName)
136- $output = certutil -importpfx -csp $cspName -p $privateKeyPassword $pfxFilePath 2>&1
137- $c = $LASTEXITCODE
138- $output
139- " ;
183+ if ( privateKeyPassword == null )
184+ {
185+ string script = @"
186+ param($pfxFilePath, $cspName, $storePath)
187+ $output = certutil -csp $cspName -addstore $storePath $pfxFilePath 2>&1
188+ $exit_message = ""LASTEXITCODE:$($LASTEXITCODE)""
140189
141- ps . AddScript ( script ) ;
142- ps . AddParameter ( "pfxFilePath" , filePath ) ;
143- ps . AddParameter ( "privateKeyPassword" , privateKeyPassword ) ;
144- ps . AddParameter ( "cspName" , cryptoProviderName ) ;
190+ $stuff = certutil -dump
191+ if ($stuff.GetType().Name -eq ""String"")
192+ {
193+ $stuff = @($stuff, $exit_message)
194+ }
195+ else
196+ {
197+ $stuff += $exit_message
198+ }
199+ $output
200+ $stuff
201+ " ;
202+
203+ ps . AddScript ( script ) ;
204+ ps . AddParameter ( "pfxFilePath" , filePath ) ;
205+ ps . AddParameter ( "cspName" , cryptoProviderName ) ;
206+ ps . AddParameter ( "storePath" , storePath ) ;
207+ }
208+ else
209+ {
210+ string script = @"
211+ param($pfxFilePath, $privateKeyPassword, $cspName)
212+ $output = certutil -importpfx -csp $cspName -p $privateKeyPassword $storePath $pfxFilePath 2>&1
213+ $exit_message = ""LASTEXITCODE:$($LASTEXITCODE)""
214+
215+ $stuff = certutil -dump
216+ if ($stuff.GetType().Name -eq ""String"")
217+ {
218+ $stuff = @($stuff, $exit_message)
219+ }
220+ else
221+ {
222+ $stuff += $exit_message
223+ }
224+ $output
225+ $stuff
226+ " ;
227+
228+ ps . AddScript ( script ) ;
229+ ps . AddParameter ( "pfxFilePath" , filePath ) ;
230+ ps . AddParameter ( "privateKeyPassword" , privateKeyPassword ) ;
231+ ps . AddParameter ( "cspName" , cryptoProviderName ) ;
232+ ps . AddParameter ( "storePath" , storePath ) ;
233+ }
145234 }
146235
147236 // Invoke the script
237+ _logger . LogTrace ( "Attempting to import the PFX" ) ;
148238 var results = ps . Invoke ( ) ;
149239
150240 // Get the last exist code returned from the script
151- // This statement is in a try/catch block because PSVariable.GetValue() is not a valid method on a remote PS Session and throws an exception.
152- // Due to security reasons and Windows architecture, retreiving values from a remote system is not supported.
153241 int lastExitCode = 0 ;
154242 try
155243 {
156- lastExitCode = ( int ) ps . Runspace . SessionStateProxy . PSVariable . GetValue ( "c" ) ;
244+ lastExitCode = GetLastExitCode ( results [ ^ 1 ] . ToString ( ) ) ;
245+ _logger . LogTrace ( $ "Last exit code: { lastExitCode } ") ;
157246 }
158247 catch ( Exception )
159248 {
249+ _logger . LogTrace ( "Unable to get the last exit code." ) ;
160250 }
161251
162252
@@ -182,7 +272,10 @@ public JobResult ImportPFXFile(string filePath, string privateKeyPassword, strin
182272 foreach ( var result in results )
183273 {
184274 string outputLine = result . ToString ( ) ;
185- if ( ! string . IsNullOrEmpty ( outputLine ) && outputLine . Contains ( "Error" ) )
275+
276+ _logger . LogTrace ( outputLine ) ;
277+
278+ if ( ! string . IsNullOrEmpty ( outputLine ) && outputLine . Contains ( "Error" ) || outputLine . Contains ( "permissions are needed" ) )
186279 {
187280 isError = true ;
188281 _logger . LogError ( outputLine ) ;
@@ -218,6 +311,30 @@ public JobResult ImportPFXFile(string filePath, string privateKeyPassword, strin
218311 }
219312 }
220313
314+ private int GetLastExitCode ( string result )
315+ {
316+ // Split the string by colon
317+ string [ ] parts = result . Split ( ':' ) ;
318+
319+ // Ensure the split result has the expected parts
320+ if ( parts . Length == 2 && parts [ 0 ] == "LASTEXITCODE" )
321+ {
322+ // Parse the second part into an integer
323+ if ( int . TryParse ( parts [ 1 ] , out int lastExitCode ) )
324+ {
325+ return lastExitCode ;
326+ }
327+ else
328+ {
329+ throw new Exception ( "Failed to parse the LASTEXITCODE value." ) ;
330+ }
331+ }
332+ else
333+ {
334+ throw new Exception ( "The last element does not contain the expected format." ) ;
335+ }
336+ }
337+
221338 public void RemoveCertificate ( string thumbprint , string storePath )
222339 {
223340 using var ps = PowerShell . Create ( ) ;
0 commit comments