Skip to content

Commit 42a094e

Browse files
authored
Merge pull request #63 from Keyfactor/release-2.8
Merge 2.8.0 to main
2 parents 3bc366f + 0c72b17 commit 42a094e

12 files changed

+98
-50
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
v2.8.0
2+
- Added new custom field - Remove Root Certificate from Chain - to allow adding certificate entries with the root CA certificate removed from the chain.
3+
- Added SSH KeyboardInteractive Authentication support if Password Authentication is not enabled.
4+
15
v2.7.0
26
- Modified RFJKS store type support java keystores of both PKCS12 and JKS
37
- Added support for OpenSSH private keys for SSH authentication

README.md

Lines changed: 10 additions & 3 deletions
Large diffs are not rendered by default.

RemoteFile/InventoryBase.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public JobResult ProcessJob(InventoryJobConfiguration config, SubmitInventoryUpd
3131
logger.LogDebug($"Begin {config.Capability} for job id {config.JobId}...");
3232
logger.LogDebug($"Server: { config.CertificateStoreDetails.ClientMachine }");
3333
logger.LogDebug($"Store Path: { config.CertificateStoreDetails.StorePath }");
34+
logger.LogDebug($"Store Properties: {config.CertificateStoreDetails.Properties.ToString()}");
3435
logger.LogDebug($"Job Properties:");
3536
foreach (KeyValuePair<string, object> keyValue in config.JobProperties ?? new Dictionary<string,object>())
3637
{
@@ -54,7 +55,7 @@ public JobResult ProcessJob(InventoryJobConfiguration config, SubmitInventoryUpd
5455

5556
certificateStore = new RemoteCertificateStore(config.CertificateStoreDetails.ClientMachine, userName, userPassword, config.CertificateStoreDetails.StorePath, storePassword, config.JobProperties);
5657
certificateStore.Initialize(sudoImpersonatedUser);
57-
certificateStore.LoadCertificateStore(certificateStoreSerializer, config.CertificateStoreDetails.Properties, true);
58+
certificateStore.LoadCertificateStore(certificateStoreSerializer, true);
5859

5960
List<X509Certificate2Collection> collections = certificateStore.GetCertificateChains();
6061

RemoteFile/ManagementBase.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
2929
{
3030
ILogger logger = LogHandler.GetClassLogger(this.GetType());
3131
logger.LogDebug($"Begin {config.Capability} for job id {config.JobId}...");
32-
logger.LogDebug($"Server: { config.CertificateStoreDetails.ClientMachine }");
33-
logger.LogDebug($"Store Path: { config.CertificateStoreDetails.StorePath }");
32+
logger.LogDebug($"Server: {config.CertificateStoreDetails.ClientMachine}");
33+
logger.LogDebug($"Store Path: {config.CertificateStoreDetails.StorePath}");
34+
logger.LogDebug($"Store Properties: {config.CertificateStoreDetails.Properties.ToString()}");
3435
logger.LogDebug($"Job Properties:");
3536
foreach (KeyValuePair<string, object> keyValue in config.JobProperties == null ? new Dictionary<string, object>() : config.JobProperties)
3637
{
@@ -50,6 +51,9 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
5051
string sudoImpersonatedUser = properties.SudoImpersonatedUser == null || string.IsNullOrEmpty(properties.SudoImpersonatedUser.Value) ?
5152
ApplicationSettings.DefaultSudoImpersonatedUser :
5253
properties.SudoImpersonatedUser.Value;
54+
bool removeRootCertificate = properties.RemoveRootCertificate == null || string.IsNullOrEmpty(properties.RemoveRootCertificate.Value) ?
55+
false :
56+
Convert.ToBoolean(properties.RemoveRootCertificate.Value);
5357

5458
certificateStore = new RemoteCertificateStore(config.CertificateStoreDetails.ClientMachine, userName, userPassword, config.CertificateStoreDetails.StorePath, storePassword, config.JobProperties);
5559
certificateStore.Initialize(sudoImpersonatedUser);
@@ -67,8 +71,8 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
6771
else
6872
throw new RemoteFileException($"Certificate store {config.CertificateStoreDetails.StorePath} does not exist on server {config.CertificateStoreDetails.ClientMachine}.");
6973
}
70-
certificateStore.LoadCertificateStore(certificateStoreSerializer, config.CertificateStoreDetails.Properties, false);
71-
certificateStore.AddCertificate((config.JobCertificate.Alias ?? new X509Certificate2(Convert.FromBase64String(config.JobCertificate.Contents), config.JobCertificate.PrivateKeyPassword, X509KeyStorageFlags.EphemeralKeySet).Thumbprint), config.JobCertificate.Contents, config.Overwrite, config.JobCertificate.PrivateKeyPassword);
74+
certificateStore.LoadCertificateStore(certificateStoreSerializer, false);
75+
certificateStore.AddCertificate((config.JobCertificate.Alias ?? new X509Certificate2(Convert.FromBase64String(config.JobCertificate.Contents), config.JobCertificate.PrivateKeyPassword, X509KeyStorageFlags.EphemeralKeySet).Thumbprint), config.JobCertificate.Contents, config.Overwrite, config.JobCertificate.PrivateKeyPassword, removeRootCertificate);
7276
certificateStore.SaveCertificateStore(certificateStoreSerializer.SerializeRemoteCertificateStore(certificateStore.GetCertificateStore(), storePathFile.Path, storePathFile.File, storePassword, certificateStore.RemoteHandler));
7377

7478
logger.LogDebug($"END add Operation for {config.CertificateStoreDetails.StorePath} on {config.CertificateStoreDetails.ClientMachine}.");
@@ -82,7 +86,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
8286
}
8387
else
8488
{
85-
certificateStore.LoadCertificateStore(certificateStoreSerializer, config.CertificateStoreDetails.Properties, false);
89+
certificateStore.LoadCertificateStore(certificateStoreSerializer, false);
8690
certificateStore.DeleteCertificateByAlias(config.JobCertificate.Alias);
8791
certificateStore.SaveCertificateStore(certificateStoreSerializer.SerializeRemoteCertificateStore(certificateStore.GetCertificateStore(), storePathFile.Path, storePathFile.File, storePassword, certificateStore.RemoteHandler));
8892
}

RemoteFile/ReenrollmentBase.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ public JobResult ProcessJobToDo(ReenrollmentJobConfiguration config, SubmitReenr
6868
string sudoImpersonatedUser = properties.SudoImpersonatedUser == null || string.IsNullOrEmpty(properties.SudoImpersonatedUser.Value) ?
6969
ApplicationSettings.DefaultSudoImpersonatedUser :
7070
properties.SudoImpersonatedUser.Value;
71+
bool removeRootCertificate = properties.RemoveRootCertificate == null || string.IsNullOrEmpty(properties.RemoveRootCertificate.Value) ?
72+
false :
73+
Convert.ToBoolean(properties.RemoveRootCertificate.Value);
7174
bool createCSROnDevice = properties.CreateCSROnDevice == null || string.IsNullOrEmpty(properties.CreateCSROnDevice.Value) ?
7275
ApplicationSettings.CreateCSROnDevice :
7376
Convert.ToBoolean(properties.CreateCSROnDevice.Value);
@@ -118,8 +121,8 @@ public JobResult ProcessJobToDo(ReenrollmentJobConfiguration config, SubmitReenr
118121
cert = keyTypeEnum == SupportedKeyTypeEnum.RSA ? cert.CopyWithPrivateKey((RSA)alg) : cert.CopyWithPrivateKey((ECDsa)alg);
119122

120123
// save certificate
121-
certificateStore.LoadCertificateStore(certificateStoreSerializer, config.CertificateStoreDetails.Properties, false);
122-
certificateStore.AddCertificate((alias ?? cert.Thumbprint), Convert.ToBase64String(cert.Export(X509ContentType.Pfx)), overwrite, null);
124+
certificateStore.LoadCertificateStore(certificateStoreSerializer, false);
125+
certificateStore.AddCertificate((alias ?? cert.Thumbprint), Convert.ToBase64String(cert.Export(X509ContentType.Pfx)), overwrite, null, removeRootCertificate);
123126
certificateStore.SaveCertificateStore(certificateStoreSerializer.SerializeRemoteCertificateStore(certificateStore.GetCertificateStore(), storePathFile.Path, storePathFile.File, storePassword, certificateStore.RemoteHandler));
124127

125128
logger.LogDebug($"END add Operation for {config.CertificateStoreDetails.StorePath} on {config.CertificateStoreDetails.ClientMachine}.");

RemoteFile/RemoteCertificateStore.cs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ internal RemoteCertificateStore(string server, string serverId, string serverPas
103103
logger.MethodExit(LogLevel.Debug);
104104
}
105105

106-
internal void LoadCertificateStore(ICertificateStoreSerializer certificateStoreSerializer, string storeProperties, bool isInventory)
106+
internal void LoadCertificateStore(ICertificateStoreSerializer certificateStoreSerializer, bool isInventory)
107107
{
108108
logger.MethodEntry(LogLevel.Debug);
109109

@@ -242,7 +242,7 @@ internal void CreateCertificateStore(ICertificateStoreSerializer certificateStor
242242
logger.MethodExit(LogLevel.Debug);
243243
}
244244

245-
internal void AddCertificate(string alias, string certificateEntry, bool overwrite, string pfxPassword)
245+
internal void AddCertificate(string alias, string certificateEntry, bool overwrite, string pfxPassword, bool removeRootCertificate)
246246
{
247247
logger.MethodEntry(LogLevel.Debug);
248248

@@ -251,7 +251,9 @@ internal void AddCertificate(string alias, string certificateEntry, bool overwri
251251
Pkcs12StoreBuilder storeBuilder = new Pkcs12StoreBuilder();
252252
Pkcs12Store certs = storeBuilder.Build();
253253

254-
byte[] newCertBytes = Convert.FromBase64String(certificateEntry);
254+
byte[] newCertBytes = removeRootCertificate && !string.IsNullOrEmpty(pfxPassword) ?
255+
RemoveRootCertificate(Convert.FromBase64String(certificateEntry), pfxPassword) :
256+
Convert.FromBase64String(certificateEntry);
255257

256258
Pkcs12Store newEntry = storeBuilder.Build();
257259

@@ -454,11 +456,38 @@ internal void Initialize(string sudoImpersonatedUser)
454456
else
455457
RemoteHandler = new WinRMHandler(Server, ServerId, ServerPassword, treatAsLocal);
456458

457-
RemoteHandler.Initialize();
458-
459459
logger.MethodExit(LogLevel.Debug);
460460
}
461461

462+
private byte[] RemoveRootCertificate(byte[] binCert, string password)
463+
{
464+
Pkcs12StoreBuilder storeBuilder = new Pkcs12StoreBuilder();
465+
Pkcs12Store store = storeBuilder.Build();
466+
Pkcs12Store store2 = storeBuilder.Build();
467+
468+
byte[] rtnCert = new byte[1];
469+
470+
using (MemoryStream ms = new MemoryStream(binCert))
471+
{
472+
store.Load(ms, password.ToCharArray());
473+
}
474+
475+
foreach (string alias in store.Aliases)
476+
{
477+
X509CertificateEntry[] chain = store.GetCertificateChain(alias);
478+
chain = chain.Where(p => p.Certificate.SubjectDN.ToString() != p.Certificate.IssuerDN.ToString()).ToArray();
479+
store2.SetKeyEntry(alias, store.GetKey(alias), chain);
480+
481+
using (MemoryStream ms = new MemoryStream())
482+
{
483+
store2.Save(ms, password.ToCharArray(), new SecureRandom());
484+
rtnCert = ms.ToArray();
485+
}
486+
}
487+
488+
return rtnCert;
489+
}
490+
462491
private bool AreValuesSafeRegex(string[] values)
463492
{
464493
bool valueIsSafe = true;

RemoteFile/RemoteHandlers/BaseRemoteHandler.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@ public static void AreLinuxPermissionsValid(string permissions)
3333
throw new RemoteFileException($"Invalid format for Linux file permissions. This value must be exactly 3 digits long with each digit between 0-7 but found {permissions} instead.");
3434
}
3535

36-
public abstract void Initialize();
37-
3836
public abstract void Terminate();
3937

4038
public abstract string RunCommand(string commandText, object[] arguments, bool withSudo, string[] passwordsToMaskInLog);

RemoteFile/RemoteHandlers/IRemoteHandler.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ namespace Keyfactor.Extensions.Orchestrator.RemoteFile.RemoteHandlers
1313
/// </summary>
1414
interface IRemoteHandler
1515
{
16-
void Initialize();
17-
1816
void Terminate();
1917

2018
string RunCommand(string commandText, object[] arguments, bool withSudo, string[] passwordsToMaskInLog);

RemoteFile/RemoteHandlers/LinuxLocalHandler.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,6 @@ class LinuxLocalHandler : BaseRemoteHandler
2727
private Command BaseCommand { get; set; }
2828

2929
internal LinuxLocalHandler()
30-
{
31-
_logger.MethodEntry(LogLevel.Debug);
32-
_logger.MethodExit(LogLevel.Debug);
33-
}
34-
35-
public override void Initialize()
3630
{
3731
_logger.MethodEntry(LogLevel.Debug);
3832

RemoteFile/RemoteHandlers/SSHHandler.cs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
using Keyfactor.Logging;
1919
using Keyfactor.PKI.PrivateKeys;
2020
using Keyfactor.PKI.PEM;
21+
using static Microsoft.ApplicationInsights.MetricDimensionNames.TelemetryContext;
22+
using Renci.SshNet.Common;
23+
using Org.BouncyCastle.Bcpg;
2124

2225
namespace Keyfactor.Extensions.Orchestrator.RemoteFile.RemoteHandlers
2326
{
@@ -26,6 +29,8 @@ class SSHHandler : BaseRemoteHandler
2629
private ConnectionInfo Connection { get; set; }
2730
private string SudoImpersonatedUser { get; set; }
2831
private bool IsStoreServerLinux { get; set; }
32+
private string UserId { get; set; }
33+
private string Password { get; set; }
2934
private SshClient sshClient;
3035

3136
internal SSHHandler(string server, string serverLogin, string serverPassword, bool isStoreServerLinux, string sudoImpersonatedUser)
@@ -35,11 +40,14 @@ internal SSHHandler(string server, string serverLogin, string serverPassword, bo
3540
Server = server;
3641
SudoImpersonatedUser = sudoImpersonatedUser;
3742
IsStoreServerLinux = isStoreServerLinux;
43+
UserId = serverLogin;
44+
Password = serverPassword;
3845

39-
List<AuthenticationMethod> authenticationMethods = new List<AuthenticationMethod>();
4046
if (serverPassword.Length < PASSWORD_LENGTH_MAX)
4147
{
42-
authenticationMethods.Add(new PasswordAuthenticationMethod(serverLogin, serverPassword));
48+
KeyboardInteractiveAuthenticationMethod keyboardAuthentication = new KeyboardInteractiveAuthenticationMethod(UserId);
49+
keyboardAuthentication.AuthenticationPrompt += KeyboardAuthentication_AuthenticationPrompt;
50+
Connection = new ConnectionInfo(server, serverLogin, new PasswordAuthenticationMethod(serverLogin, serverPassword), keyboardAuthentication);
4351
}
4452
else
4553
{
@@ -60,18 +68,9 @@ internal SSHHandler(string server, string serverLogin, string serverPassword, bo
6068
}
6169
}
6270

63-
authenticationMethods.Add(new PrivateKeyAuthenticationMethod(serverLogin, privateKeyFile));
71+
Connection = new ConnectionInfo(server, serverLogin, new PrivateKeyAuthenticationMethod(serverLogin, privateKeyFile));
6472
}
6573

66-
Connection = new ConnectionInfo(server, serverLogin, authenticationMethods.ToArray());
67-
68-
_logger.MethodExit(LogLevel.Debug);
69-
}
70-
71-
public override void Initialize()
72-
{
73-
_logger.MethodEntry(LogLevel.Debug);
74-
7574
try
7675
{
7776
sshClient = new SshClient(Connection);
@@ -382,6 +381,17 @@ public override void RemoveCertificateFile(string path, string fileName)
382381
RunCommand($"rm {path}{fileName}", null, ApplicationSettings.UseSudo, null);
383382
}
384383

384+
private void KeyboardAuthentication_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
385+
{
386+
_logger.MethodEntry(LogLevel.Debug);
387+
foreach (AuthenticationPrompt prompt in e.Prompts)
388+
{
389+
if (prompt.Request.StartsWith("Password"))
390+
prompt.Response = Password;
391+
}
392+
_logger.MethodExit(LogLevel.Debug);
393+
}
394+
385395
private void SplitStorePathFile(string pathFileName, out string path, out string fileName)
386396
{
387397
_logger.MethodEntry(LogLevel.Debug);

0 commit comments

Comments
 (0)