Skip to content

Commit 7c35815

Browse files
authored
Merge pull request #105 from Keyfactor/58570-Add_Cert_With_No_PW
58570 add cert with no pw
2 parents bfc9fdc + ce61ae2 commit 7c35815

File tree

5 files changed

+149
-28
lines changed

5 files changed

+149
-28
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
2.4.1
2+
* Modified the CertUtil logic to use the -addstore argument when no password is sent with the certificate information.
3+
* Added additional error trapping and trace logs
4+
15
2.4.0
26
* Changed the way certificates are added to cert stores. CertUtil is now used to import the PFX certificate into the associated store. The CSP is now considered when maintaining certificates, empty CSP values will result in using the machines default CSP.
37
* Added the Crypto Service Provider and SAN Entry Parameters to be used on Inventory queries, Adding and ReEnrollments for the WinCert, WinSQL and IISU extensions.

IISU/ClientPSCertStoreManager.cs

Lines changed: 142 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
using Keyfactor.Orchestrators.Common.Enums;
1616
using Keyfactor.Orchestrators.Extensions;
1717
using Microsoft.CodeAnalysis;
18+
using Microsoft.CodeAnalysis.FlowAnalysis;
1819
using Microsoft.Extensions.Logging;
1920
using System;
2021
using System.IO;
22+
using System.Linq.Expressions;
2123
using System.Management.Automation;
2224
using System.Management.Automation.Runspaces;
2325
using 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();

IISU/ImplementedStoreTypes/Win/Management.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ private JobResult performAddition(ManagementJobConfiguration config)
145145

146146
// Using certutil on the remote computer, import the pfx file using a supplied csp if any.
147147
_logger.LogTrace($"Importing temporary PFX File: {filePath}.");
148-
JobResult result = manager.ImportPFXFile(filePath, privateKeyPassword, cryptoProvider);
148+
JobResult result = manager.ImportPFXFile(filePath, privateKeyPassword, cryptoProvider, storePath);
149149

150150
// Delete the temporary file
151151
_logger.LogTrace($"Deleting temporary PFX File: {filePath}.");

IISU/ImplementedStoreTypes/WinIIS/Management.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ private JobResult PerformAddCertificate(ManagementJobConfiguration config, strin
148148
_logger.LogTrace($"{filePath} was created.");
149149

150150
// Using certutil on the remote computer, import the pfx file using a supplied csp if any.
151-
JobResult result = manager.ImportPFXFile(filePath, privateKeyPassword, cryptoProvider);
151+
JobResult result = manager.ImportPFXFile(filePath, privateKeyPassword, cryptoProvider, storePath);
152152

153153
// Delete the temporary file
154154
manager.DeletePFXFile(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath));

IISU/ImplementedStoreTypes/WinSQL/Management.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ private JobResult PerformAddCertificate(ManagementJobConfiguration config, strin
145145
_logger.LogTrace($"{filePath} was created.");
146146

147147
// Using certutil on the remote computer, import the pfx file using a supplied csp if any.
148-
JobResult result = manager.ImportPFXFile(filePath, privateKeyPassword, cryptoProvider);
148+
JobResult result = manager.ImportPFXFile(filePath, privateKeyPassword, cryptoProvider, storePath);
149149

150150
// Delete the temporary file
151151
manager.DeletePFXFile(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath));

0 commit comments

Comments
 (0)