Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
v4.2.0
- Deprecate legacy encryption. From this release forward, RequiresLegacyEncryption will be ignored.
- Modification to suppor SSH certificate authentication for Linux managed servers. If a certificate is used, the private key must be in OpenSSH format and the public key must be in OpenSSH format or PEM format.
- Modify Management-Create job for RFKDB to create all 4 IBM key dabase files - kdb, rdb, sth, and crl. Actual certificate management, however, remains centered on the kdb file. The other 3 files are
created to ensure that the kdb file can be used by IBM applications that require all 4 files to be present, such as IBM MQ.

v4.1.0
- Add custom field to select legacy encryption for certificate stores
- Improve error message when attempting a management or ODKG job and Ignore Private Key on Inventory is selected.
Expand Down
142 changes: 63 additions & 79 deletions README.md

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions RemoteFile/ICustomFileCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2021 Keyfactor
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
// and limitations under the License.

using Org.BouncyCastle.Pkcs;
using Keyfactor.Extensions.Orchestrator.RemoteFile.RemoteHandlers;
using Keyfactor.Extensions.Orchestrator.RemoteFile.Models;
using System.Collections.Generic;

namespace Keyfactor.Extensions.Orchestrator.RemoteFile
{
interface ICustomFileCreator
{
void CreateEmptyStoreFile(string storePath, string storePassword, string linuxFilePermissions, string linuxFileOwner, IRemoteHandler remoteHandler);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,24 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
// and limitations under the License.

using System;
using System.Collections.Generic;
using System.IO;

using Keyfactor.Logging;
using Keyfactor.Extensions.Orchestrator.RemoteFile.RemoteHandlers;
using Keyfactor.Extensions.Orchestrator.RemoteFile.Models;

using Keyfactor.Extensions.Orchestrator.RemoteFile.RemoteHandlers;
using Keyfactor.Logging;
using Microsoft.Extensions.Logging;

using Newtonsoft.Json;
using Org.BouncyCastle.Pkcs;
using Renci.SshNet;
using System;
using System.Collections.Generic;
using System.IO;

namespace Keyfactor.Extensions.Orchestrator.RemoteFile.KDB
{
class KDBCertificateStoreSerializer : ICertificateStoreSerializer
class KDBCertificateStoreSerializer : ICertificateStoreSerializer, ICustomFileCreator
{
private ILogger logger;

public KDBCertificateStoreSerializer(string storeProperties)
public KDBCertificateStoreSerializer(string storeProperties)
{
logger = LogHandler.GetClassLogger(this.GetType());
}
Expand All @@ -44,7 +43,7 @@ public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, s

remoteHandler.UploadCertificateFile(storePath, tempStoreFile, storeContentBytes);

string command = $"{bashCommand}gskcapicmd -keydb -convert -db \"{storePath}{tempStoreFile}\" -pw \"{storePassword}\" -type kdb -new_db \"{storePath}{tempCertFile}\" -new_pw \"{storePassword}\" -new_format p12";
string command = $"{bashCommand}gskcapicmd -keydb -convert -db \"{storePath}{tempStoreFile}\" -pw \"{storePassword}\" -new_db \"{storePath}{tempCertFile}\" -new_pw \"{storePassword}\" -new_format p12";

try
{
Expand Down Expand Up @@ -86,7 +85,7 @@ public List<SerializedStoreInfo> SerializeRemoteCertificateStore(Pkcs12Store cer
string tempStoreFile = Guid.NewGuid().ToString().Replace("-", string.Empty) + ".kdb";
string tempCertFile = Guid.NewGuid().ToString().Replace("-", string.Empty) + ".p12";

string command = $"{bashCommand}gskcapicmd -keydb -convert -db \"{storePath}{tempCertFile}\" -pw \"{storePassword}\" -type p12 -new_db \"{storePath}{tempStoreFile}\" -new_pw \"{storePassword}\" -new_format kdb";
string command = $"{bashCommand}gskcapicmd -keydb -convert -db \"{storePath}{tempCertFile}\" -pw \"{storePassword}\" -type p12 -new_db \"{storePath}{tempStoreFile}\" -new_pw \"{storePassword}\" -new_format cms";

try
{
Expand Down Expand Up @@ -123,5 +122,65 @@ public string GetPrivateKeyPath()
{
return null;
}

public void CreateEmptyStoreFile(string storePath, string storePassword, string linuxFilePermissions, string linuxFileOwner, IRemoteHandler remoteHandler)
{
logger.MethodEntry(LogLevel.Debug);

int extIdx = storePath.LastIndexOf('.');
if (extIdx == -1)
throw new Exception("Store path must include a file name with an extension.");

string pathDelim = storePath.Substring(0, 1) == "/" ? "/" : "\\";
int fileNameIdx = storePath.LastIndexOf(pathDelim) + 1;
if (fileNameIdx == -1)
throw new Exception("Invalid format for store path.");

string bashCommand = storePath.Substring(0, 1) == "/" ? "bash " : string.Empty;
if (storePath.Substring(0, 1) == "|")
storePath = "/" + storePath.Substring(1);

string path = storePath.Substring(0, fileNameIdx);
string fileName = storePath.Substring(fileNameIdx, extIdx - fileNameIdx);

string tempStoreFile = Guid.NewGuid().ToString().Replace("-", string.Empty);

string command = $"{bashCommand}gskcapicmd -keydb -create -db \"{path}{tempStoreFile + ".kdb"}\" -pw \"{storePassword}\" -type cms -stash";

try
{
remoteHandler.RunCommand(command, null, ApplicationSettings.UseSudo, new string[] { storePassword });

remoteHandler.CreateEmptyStoreFile(path + fileName + ".kdb", linuxFilePermissions, linuxFileOwner);
remoteHandler.CreateEmptyStoreFile(path + fileName + ".rdb", linuxFilePermissions, linuxFileOwner);
remoteHandler.CreateEmptyStoreFile(path + fileName + ".crl", linuxFilePermissions, linuxFileOwner);
remoteHandler.CreateEmptyStoreFile(path + fileName + ".sth", linuxFilePermissions, linuxFileOwner);

remoteHandler.RunCommand($"cp {path}{tempStoreFile}.kdb {path}{fileName}.kdb", null, ApplicationSettings.UseSudo, null);
remoteHandler.RunCommand($"cp {path}{tempStoreFile}.rdb {path}{fileName}.rdb", null, ApplicationSettings.UseSudo, null);
remoteHandler.RunCommand($"cp {path}{tempStoreFile}.crl {path}{fileName}.crl", null, ApplicationSettings.UseSudo, null);
remoteHandler.RunCommand($"cp {path}{tempStoreFile}.sth {path}{fileName}.sth", null, ApplicationSettings.UseSudo, null);
}
catch (Exception ex)
{
if (ex.Message.Contains("cannot execute binary file", StringComparison.InvariantCultureIgnoreCase) && storePath.Substring(0, 1) == "/")
{
storePath = "|" + storePath.Substring(1);
CreateEmptyStoreFile(storePath, storePassword, linuxFilePermissions, linuxFileOwner, remoteHandler);
}
else
throw;
}
finally
{
try { remoteHandler.RemoveCertificateFile(path, tempStoreFile + ".kdb"); } catch (Exception) { };
try { remoteHandler.RemoveCertificateFile(path, tempStoreFile + ".sth"); } catch (Exception) { };
try { remoteHandler.RemoveCertificateFile(path, tempStoreFile + ".rdb"); } catch (Exception) { };
try { remoteHandler.RemoveCertificateFile(path, tempStoreFile + ".crl"); } catch (Exception) { };
}


logger.MethodExit(LogLevel.Debug);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContents, strin
public List<SerializedStoreInfo> SerializeRemoteCertificateStore(Pkcs12Store certificateStore, string storePath, string storeFileName, string storePassword, IRemoteHandler remoteHandler)
{
Pkcs12StoreBuilder storeBuilder = new Pkcs12StoreBuilder();
storeBuilder.SetCertAlgorithm(PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc);
//storeBuilder.SetCertAlgorithm(PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc);
storeBuilder.SetKeyAlgorithm(NistObjectIdentifiers.IdAes256Cbc, PkcsObjectIdentifiers.IdHmacWithSha256);
storeBuilder.SetUseDerEncoding(true);

Expand Down Expand Up @@ -95,7 +95,7 @@ public string GetPrivateKeyPath()
private Pkcs12Store ConvertAliases(Pkcs12Store workingStore, bool useThumbprintAsAlias)
{
Pkcs12StoreBuilder storeBuilder = new Pkcs12StoreBuilder();
storeBuilder.SetCertAlgorithm(PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc);
//storeBuilder.SetCertAlgorithm(PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc);
storeBuilder.SetKeyAlgorithm(NistObjectIdentifiers.IdAes256Cbc, PkcsObjectIdentifiers.IdHmacWithSha256);
storeBuilder.SetUseDerEncoding(true);

Expand Down
4 changes: 2 additions & 2 deletions RemoteFile/ManagementBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
}
certificateStore.LoadCertificateStore(certificateStoreSerializer, false);
certificateStore.AddCertificate(config.JobCertificate.Alias ?? GetThumbprint(config.JobCertificate, logger), config.JobCertificate.Contents, config.Overwrite, config.JobCertificate.PrivateKeyPassword, RemoveRootCertificate);
certificateStore.SaveCertificateStore(certificateStoreSerializer.SerializeRemoteCertificateStore(certificateStore.GetCertificateStore(RequiresLegacyEncryption), storePathFile.Path, storePathFile.File, StorePassword, certificateStore.RemoteHandler));
certificateStore.SaveCertificateStore(certificateStoreSerializer.SerializeRemoteCertificateStore(certificateStore.GetCertificateStore(), storePathFile.Path, storePathFile.File, StorePassword, certificateStore.RemoteHandler));

try
{
Expand Down Expand Up @@ -83,7 +83,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
{
certificateStore.LoadCertificateStore(certificateStoreSerializer, false);
certificateStore.DeleteCertificateByAlias(config.JobCertificate.Alias);
certificateStore.SaveCertificateStore(certificateStoreSerializer.SerializeRemoteCertificateStore(certificateStore.GetCertificateStore(RequiresLegacyEncryption), storePathFile.Path, storePathFile.File, StorePassword, certificateStore.RemoteHandler));
certificateStore.SaveCertificateStore(certificateStoreSerializer.SerializeRemoteCertificateStore(certificateStore.GetCertificateStore(), storePathFile.Path, storePathFile.File, StorePassword, certificateStore.RemoteHandler));
}
logger.LogDebug($"END Delete Operation for {config.CertificateStoreDetails.StorePath} on {config.CertificateStoreDetails.ClientMachine}.");
break;
Expand Down
2 changes: 1 addition & 1 deletion RemoteFile/ReenrollmentBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public JobResult ProcessJob(ReenrollmentJobConfiguration config, SubmitReenrollm

// save certificate
certificateStore.AddCertificate(config.Alias ?? cert.Thumbprint, Convert.ToBase64String(cert.Export(X509ContentType.Pfx, "password")), config.Overwrite, "password", RemoveRootCertificate);
certificateStore.SaveCertificateStore(certificateStoreSerializer.SerializeRemoteCertificateStore(certificateStore.GetCertificateStore(RequiresLegacyEncryption), storePathFile.Path, storePathFile.File, StorePassword, certificateStore.RemoteHandler));
certificateStore.SaveCertificateStore(certificateStoreSerializer.SerializeRemoteCertificateStore(certificateStore.GetCertificateStore(), storePathFile.Path, storePathFile.File, StorePassword, certificateStore.RemoteHandler));

try
{
Expand Down
53 changes: 12 additions & 41 deletions RemoteFile/RemoteCertificateStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,38 +125,11 @@ internal void LoadCertificateStore(ICertificateStoreSerializer certificateStoreS
logger.MethodExit(LogLevel.Debug);
}

internal Pkcs12Store GetCertificateStore(bool requiresLegacyEncryption)
internal Pkcs12Store GetCertificateStore()
{
logger.MethodEntry(LogLevel.Debug);
logger.MethodExit(LogLevel.Debug);

if (requiresLegacyEncryption)
{
Pkcs12StoreBuilder builder = new Pkcs12StoreBuilder();
builder.SetKeyAlgorithm(PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc);
builder.SetCertAlgorithm(PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc);

Pkcs12Store tempStore = builder.Build();

foreach (string alias in CertificateStore.Aliases)
{
if (CertificateStore.IsKeyEntry(alias))
{
var keyEntry = CertificateStore.GetKey(alias);
var certChain = CertificateStore.GetCertificateChain(alias);

tempStore.SetKeyEntry(alias, keyEntry, certChain);
}
else if (CertificateStore.IsCertificateEntry(alias))
{
var certEntry = CertificateStore.GetCertificate(alias);
tempStore.SetCertificateEntry(alias, certEntry);
}
}

CertificateStore = tempStore;
}

return CertificateStore;
}

Expand Down Expand Up @@ -264,19 +237,17 @@ internal void CreateCertificateStore(ICertificateStoreSerializer certificateStor
ApplicationSettings.DefaultOwnerOnStoreCreation :
propertiesCollection.LinuxFileOwnerOnStoreCreation.Value;

RemoteHandler.CreateEmptyStoreFile(storePath, linuxFilePermissions, linuxFileOwner);
string privateKeyPath = certificateStoreSerializer.GetPrivateKeyPath();
if (!string.IsNullOrEmpty(privateKeyPath))
RemoteHandler.CreateEmptyStoreFile(privateKeyPath, linuxFilePermissions, linuxFileOwner);

logger.MethodExit(LogLevel.Debug);
}

internal void CreateCertificateStore(ICertificateStoreSerializer certificateStoreSerializer, string storePath, string linuxFilePermissions, string linuxFileOwner)
{
logger.MethodEntry(LogLevel.Debug);


if (certificateStoreSerializer is ICustomFileCreator)
{
((ICustomFileCreator)certificateStoreSerializer).CreateEmptyStoreFile(storePath, StorePassword, linuxFilePermissions, linuxFileOwner, RemoteHandler);
}
else
{
RemoteHandler.CreateEmptyStoreFile(storePath, linuxFilePermissions, linuxFileOwner);
string privateKeyPath = certificateStoreSerializer.GetPrivateKeyPath();
if (!string.IsNullOrEmpty(privateKeyPath))
RemoteHandler.CreateEmptyStoreFile(privateKeyPath, linuxFilePermissions, linuxFileOwner);
}

logger.MethodExit(LogLevel.Debug);
}
Expand Down
2 changes: 1 addition & 1 deletion RemoteFile/RemoteFile.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<PackageReference Include="Keyfactor.Orchestrators.IOrchestratorJobExtensions" Version="1.0.0" />
<PackageReference Include="Keyfactor.PKI" Version="8.1.1" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.4.13" />
<PackageReference Include="SSH.NET" Version="2024.0.0" />
<PackageReference Include="SSH.NET" Version="2025.1.0" />

<None Update="manifest.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
Expand Down
5 changes: 0 additions & 5 deletions RemoteFile/RemoteFileJobTypeBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public abstract class RemoteFileJobTypeBase
internal bool CreateCSROnDevice { get; set; }
internal bool UseShellCommands { get; set; }
internal string PostJobApplicationRestart { get; set; }
internal bool RequiresLegacyEncryption { get; set; }
internal string KeyType { get; set; }
internal int KeySize { get; set; }
internal string SubjectText { get; set; }
Expand Down Expand Up @@ -81,10 +80,6 @@ internal void SetJobProperties(JobConfiguration config, CertificateStore certifi
null :
properties.PostJobApplicationRestart;

RequiresLegacyEncryption = properties.RequiresLegacyEncryption == null || string.IsNullOrEmpty(properties.RequiresLegacyEncryption.Value) ?
false :
properties.RequiresLegacyEncryption;

if (config.JobProperties != null)
{
KeyType = !config.JobProperties.ContainsKey("keyType") || config.JobProperties["keyType"] == null || string.IsNullOrEmpty(config.JobProperties["keyType"].ToString()) ? string.Empty : config.JobProperties["keyType"].ToString();
Expand Down
20 changes: 11 additions & 9 deletions RemoteFile/RemoteHandlers/SSHHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
// and limitations under the License.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
Expand All @@ -19,9 +18,7 @@
using Keyfactor.Logging;
using Keyfactor.PKI.PrivateKeys;
using Keyfactor.PKI.PEM;
using static Microsoft.ApplicationInsights.MetricDimensionNames.TelemetryContext;
using Renci.SshNet.Common;
using Org.BouncyCastle.Bcpg;

namespace Keyfactor.Extensions.Orchestrator.RemoteFile.RemoteHandlers
{
Expand Down Expand Up @@ -57,18 +54,23 @@
{
PrivateKeyFile privateKeyFile;

string privateKey = string.Empty;

string[] sshSecret = serverPassword.Split(new[] { "|||" }, 2, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
try
{
using (MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(FormatPrivateKey(serverPassword))))
{
privateKeyFile = new PrivateKeyFile(ms);
}
privateKey = FormatPrivateKey(sshSecret[0]);
}
catch (Exception)
{
using (MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(ConvertToPKCS1(serverPassword))))
privateKey = ConvertToPKCS1(sshSecret[0]);
}

using (MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(privateKey)))
{
using (MemoryStream ms2 = new MemoryStream(Encoding.ASCII.GetBytes(sshSecret.Length == 1 ? string.Empty : sshSecret[1])))
{
privateKeyFile = new PrivateKeyFile(ms);
privateKeyFile = new PrivateKeyFile(ms, null, ms2.Length == 0 ? null : ms2);
}
}

Expand Down Expand Up @@ -185,7 +187,7 @@
client.Upload(stream, FormatFTPPath(uploadPath, false));
}
}
catch (Exception ex)

Check warning on line 190 in RemoteFile/RemoteHandlers/SSHHandler.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

The variable 'ex' is declared but never used

Check warning on line 190 in RemoteFile/RemoteHandlers/SSHHandler.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

The variable 'ex' is declared but never used

Check warning on line 190 in RemoteFile/RemoteHandlers/SSHHandler.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

The variable 'ex' is declared but never used

Check warning on line 190 in RemoteFile/RemoteHandlers/SSHHandler.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

The variable 'ex' is declared but never used

Check warning on line 190 in RemoteFile/RemoteHandlers/SSHHandler.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

The variable 'ex' is declared but never used

Check warning on line 190 in RemoteFile/RemoteHandlers/SSHHandler.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

The variable 'ex' is declared but never used

Check warning on line 190 in RemoteFile/RemoteHandlers/SSHHandler.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

The variable 'ex' is declared but never used

Check warning on line 190 in RemoteFile/RemoteHandlers/SSHHandler.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

The variable 'ex' is declared but never used
{
scpError = true;
_logger.LogDebug($"SCP upload failed. Attempting with SFTP protocol...");
Expand Down
Loading
Loading