Skip to content

Commit d6ebae1

Browse files
* use dual build action release candidate * target completed 3.1.1 workflow Co-authored-by: Lee Fine <[email protected]>
1 parent 331bb26 commit d6ebae1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2118
-1814
lines changed

.github/workflows/keyfactor-starter-workflow.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ on:
1111

1212
jobs:
1313
call-starter-workflow:
14-
uses: keyfactor/actions/.github/workflows/starter.yml@v2
14+
uses: keyfactor/actions/.github/workflows/starter.yml@3.1.1
1515
secrets:
1616
token: ${{ secrets.V2BUILDTOKEN}}
1717
APPROVE_README_PUSH: ${{ secrets.APPROVE_README_PUSH}}
1818
gpg_key: ${{ secrets.KF_GPG_PRIVATE_KEY }}
1919
gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }}
20+
scan_token: ${{ secrets.SAST_TOKEN }}

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
v2.9.0
2+
- Modify Discovery on Linux servers to filter out ignored folders when searching using the Find command rather than eliminating them after. This was done to eliminate permissions errors.
3+
- Deprecated isRSAPrivateKey custom property on RFPEM certificate store type. Integration now reads the existing private key to determin if it is formatted as PKCS#1 or PKCS#8 and, on renewal, keeps the format the same. For new PEM certificate stores/certificates, PKCS#8 will always be used. PLEASE NOTE, for existing certificate stores that already have isRSAPrivateKey defined, this setting will be ignored.
4+
- Modified RFPkcs12 store type to handle single store certificate stores with no friendly name/alias
5+
- Modify to create 2 builds - one for .net6 and one for .net8
6+
- Update README to new DocTool format
7+
18
v2.8.1
29
- Fixed issue were sensitive information could be exposed at debug logging level
310

README.md

+1,139-343
Large diffs are not rendered by default.

RemoteFile/Discovery.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
using Microsoft.Extensions.Logging;
1818
using Newtonsoft.Json;
19+
using static Keyfactor.PKI.PKIConstants.Microsoft;
1920

2021
namespace Keyfactor.Extensions.Orchestrator.RemoteFile
2122
{
@@ -67,7 +68,8 @@ public JobResult ProcessJob(DiscoveryJobConfiguration config, SubmitDiscoveryUpd
6768
if (filesTosearch.Length == 0)
6869
filesTosearch = new string[] { "*" };
6970

70-
locations = certificateStore.FindStores(directoriesToSearch, extensionsToSearch, filesTosearch, includeSymLinks);
71+
locations = certificateStore.FindStores(directoriesToSearch, extensionsToSearch, filesTosearch, ignoredDirs, includeSymLinks);
72+
7173
foreach (string ignoredDir in ignoredDirs)
7274
{
7375
locations = locations.Where(p => !p.StartsWith(ignoredDir) && !p.ToLower().StartsWith("find:")).ToList();

RemoteFile/ImplementedStoreTypes/KDB/KDBCertificateStoreSerializer.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, s
5151
byte[] storeBytes = remoteHandler.DownloadCertificateFile($"{storePath}{tempCertFile}");
5252
store.Load(new MemoryStream(storeBytes), string.IsNullOrEmpty(storePassword) ? new char[0] : storePassword.ToCharArray());
5353
}
54-
catch (Exception ex)
54+
catch (Exception)
5555
{
56-
throw ex;
56+
throw;
5757
}
5858
finally
5959
{
@@ -90,9 +90,9 @@ public List<SerializedStoreInfo> SerializeRemoteCertificateStore(Pkcs12Store cer
9090
storeInfo.Add(new SerializedStoreInfo() { Contents = storeContents, FilePath = storePath+storeFileName });
9191
return storeInfo;
9292
}
93-
catch (Exception ex)
93+
catch (Exception)
9494
{
95-
throw ex;
95+
throw;
9696
}
9797
finally
9898
{

RemoteFile/ImplementedStoreTypes/OraWlt/OraWltCertificateStoreSerializer.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, s
5959
JKSCertificateStoreSerializer serializer = new JKSCertificateStoreSerializer(String.Empty);
6060
store = serializer.DeserializeRemoteCertificateStore(storeBytes, $"{WorkFolder}{tempStoreFileJKS}", storePassword, remoteHandler, isInventory);
6161
}
62-
catch (Exception ex)
62+
catch (Exception)
6363
{
64-
throw ex;
64+
throw;
6565
}
6666
finally
6767
{
@@ -101,9 +101,9 @@ public List<SerializedStoreInfo> SerializeRemoteCertificateStore(Pkcs12Store cer
101101
storeInfo.Add(new SerializedStoreInfo() { Contents = storeContents, FilePath = storePath+storeFileName });
102102
return storeInfo;
103103
}
104-
catch (Exception ex)
104+
catch (Exception)
105105
{
106-
throw ex;
106+
throw;
107107
}
108108
finally
109109
{

RemoteFile/ImplementedStoreTypes/PEM/PEMCertificateStoreSerializer.cs

+31-13
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ class PEMCertificateStoreSerializer : ICertificateStoreSerializer
3939
private bool IsTrustStore { get; set; }
4040
private bool IncludesChain { get; set; }
4141
private string SeparatePrivateKeyFilePath { get; set; }
42-
private bool IsRSAPrivateKey { get; set; }
4342
private bool IgnorePrivateKeyOnInventory { get; set; }
4443

4544
private ILogger logger;
@@ -53,9 +52,6 @@ public PEMCertificateStoreSerializer(string storeProperties)
5352
public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, string storePath, string storePassword, IRemoteHandler remoteHandler, bool isInventory)
5453
{
5554
logger.MethodEntry(LogLevel.Debug);
56-
57-
if (IsRSAPrivateKey && !string.IsNullOrEmpty(storePassword))
58-
throw new RemoteFileException($"Certificate store with an RSA Private Key cannot contain a store password. Invalid store format not supported.");
5955

6056
Pkcs12StoreBuilder storeBuilder = new Pkcs12StoreBuilder();
6157
Pkcs12Store store = storeBuilder.Build();
@@ -72,7 +68,12 @@ public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContentBytes, s
7268
}
7369
else
7470
{
75-
AsymmetricKeyEntry keyEntry = GetPrivateKey(storeContents, storePassword ?? string.Empty, remoteHandler);
71+
bool isRSAPrivateKey = false;
72+
AsymmetricKeyEntry keyEntry = GetPrivateKey(storeContents, storePassword ?? string.Empty, remoteHandler, out isRSAPrivateKey);
73+
74+
if (isRSAPrivateKey && !string.IsNullOrEmpty(storePassword))
75+
throw new RemoteFileException($"Certificate store with an RSA Private Key cannot contain a store password. Invalid store format not supported.");
76+
7677
store.SetKeyEntry(CertificateConverterFactory.FromBouncyCastleCertificate(certificates[0].Certificate).ToX509Certificate2().Thumbprint, keyEntry, certificates);
7778
}
7879

@@ -93,9 +94,6 @@ public List<SerializedStoreInfo> SerializeRemoteCertificateStore(Pkcs12Store cer
9394
{
9495
logger.MethodEntry(LogLevel.Debug);
9596

96-
if (IsRSAPrivateKey && !string.IsNullOrEmpty(storePassword))
97-
throw new RemoteFileException($"Certificate store with an RSA Private Key cannot contain a store password. Invalid store format not supported.");
98-
9997
string pemString = string.Empty;
10098
string keyString = string.Empty;
10199
List<SerializedStoreInfo> storeInfo = new List<SerializedStoreInfo>();
@@ -113,6 +111,17 @@ public List<SerializedStoreInfo> SerializeRemoteCertificateStore(Pkcs12Store cer
113111
}
114112
else
115113
{
114+
string storeContents = Encoding.ASCII.GetString(remoteHandler.DownloadCertificateFile(storePath + storeFileName));
115+
bool isRSAPrivateKey = false;
116+
try
117+
{
118+
GetPrivateKey(storeContents, storePassword, remoteHandler, out isRSAPrivateKey);
119+
}
120+
catch (RemoteFileException) { }
121+
122+
if (isRSAPrivateKey && !string.IsNullOrEmpty(storePassword))
123+
throw new RemoteFileException($"Certificate store with an RSA Private Key cannot contain a store password. Invalid store format not supported.");
124+
116125
bool keyEntryProcessed = false;
117126
foreach (string alias in certificateStore.Aliases)
118127
{
@@ -131,7 +140,7 @@ public List<SerializedStoreInfo> SerializeRemoteCertificateStore(Pkcs12Store cer
131140
X509CertificateEntry[] certEntries = certificateStore.GetCertificateChain(alias);
132141
AsymmetricKeyParameter publicKey = certEntries[0].Certificate.GetPublicKey();
133142

134-
if (IsRSAPrivateKey)
143+
if (isRSAPrivateKey)
135144
{
136145
TextWriter textWriter = new StringWriter();
137146
PemWriter pemWriter = new PemWriter(textWriter);
@@ -185,7 +194,6 @@ private void LoadCustomProperties(string storeProperties)
185194
IsTrustStore = properties.IsTrustStore == null || string.IsNullOrEmpty(properties.IsTrustStore.Value) ? false : bool.Parse(properties.IsTrustStore.Value);
186195
IncludesChain = properties.IncludesChain == null || string.IsNullOrEmpty(properties.IncludesChain.Value) ? false : bool.Parse(properties.IncludesChain.Value);
187196
SeparatePrivateKeyFilePath = properties.SeparatePrivateKeyFilePath == null || string.IsNullOrEmpty(properties.SeparatePrivateKeyFilePath.Value) ? String.Empty : properties.SeparatePrivateKeyFilePath.Value;
188-
IsRSAPrivateKey = properties.IsRSAPrivateKey == null || string.IsNullOrEmpty(properties.IsRSAPrivateKey.Value) ? false : bool.Parse(properties.IsRSAPrivateKey.Value);
189197
IgnorePrivateKeyOnInventory = properties.IgnorePrivateKeyOnInventory == null || string.IsNullOrEmpty(properties.IgnorePrivateKeyOnInventory.Value) ? false : bool.Parse(properties.IgnorePrivateKeyOnInventory.Value);
190198

191199
logger.MethodExit(LogLevel.Debug);
@@ -222,7 +230,7 @@ private X509CertificateEntry[] GetCertificates(string certificates)
222230
return certificateEntries.ToArray();
223231
}
224232

225-
private AsymmetricKeyEntry GetPrivateKey(string storeContents, string storePassword, IRemoteHandler remoteHandler)
233+
private AsymmetricKeyEntry GetPrivateKey(string storeContents, string storePassword, IRemoteHandler remoteHandler, out bool isRSA)
226234
{
227235
logger.MethodEntry(LogLevel.Debug);
228236

@@ -231,8 +239,18 @@ private AsymmetricKeyEntry GetPrivateKey(string storeContents, string storePassw
231239
storeContents = Encoding.ASCII.GetString(remoteHandler.DownloadCertificateFile(SeparatePrivateKeyFilePath));
232240
}
233241

242+
isRSA = false;
243+
foreach (string begDelim in PrivateKeyDelimetersPkcs1)
244+
{
245+
if (storeContents.Contains(begDelim))
246+
{
247+
isRSA = true;
248+
break;
249+
}
250+
}
251+
234252
string privateKey = string.Empty;
235-
foreach (string begDelim in IsRSAPrivateKey ? PrivateKeyDelimetersPkcs1 : PrivateKeyDelimetersPkcs8)
253+
foreach (string begDelim in isRSA ? PrivateKeyDelimetersPkcs1 : PrivateKeyDelimetersPkcs8)
236254
{
237255
string endDelim = begDelim.Replace("BEGIN", "END");
238256

@@ -252,7 +270,7 @@ private AsymmetricKeyEntry GetPrivateKey(string storeContents, string storePassw
252270
throw new RemoteFileException("Invalid private key: No private key or invalid private key format found.");
253271

254272
PrivateKeyConverter c;
255-
if (IsRSAPrivateKey)
273+
if (isRSA)
256274
{
257275
RSA rsa = RSA.Create();
258276
int bytesRead;

RemoteFile/ImplementedStoreTypes/PKCS12/PKCS12CertificateStoreSerializer.cs

+62-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
66
// and limitations under the License.
77

8+
using System;
89
using System.IO;
910
using System.Collections.Generic;
1011
using Keyfactor.Extensions.Orchestrator.RemoteFile.RemoteHandlers;
@@ -13,12 +14,15 @@
1314
using Org.BouncyCastle.Pkcs;
1415
using Keyfactor.Logging;
1516
using Microsoft.Extensions.Logging;
17+
using System.Linq;
18+
using Keyfactor.PKI.Extensions;
1619

1720
namespace Keyfactor.Extensions.Orchestrator.RemoteFile.PKCS12
1821
{
1922
class PKCS12CertificateStoreSerializer : ICertificateStoreSerializer
2023
{
2124
private ILogger logger;
25+
private bool HasEmptyAliases { get; set; }
2226

2327
public PKCS12CertificateStoreSerializer(string storeProperties)
2428
{
@@ -28,21 +32,46 @@ public PKCS12CertificateStoreSerializer(string storeProperties)
2832
public Pkcs12Store DeserializeRemoteCertificateStore(byte[] storeContents, string storePath, string storePassword, IRemoteHandler remoteHandler, bool isInventory)
2933
{
3034
Pkcs12StoreBuilder storeBuilder = new Pkcs12StoreBuilder();
31-
Pkcs12Store store = storeBuilder.Build();
35+
Pkcs12Store workingStore = storeBuilder.Build();
36+
Pkcs12Store returnStore = storeBuilder.Build();
3237

3338
using (MemoryStream ms = new MemoryStream(storeContents))
3439
{
35-
store.Load(ms, string.IsNullOrEmpty(storePassword) ? new char[0] : storePassword.ToCharArray());
40+
workingStore.Load(ms, string.IsNullOrEmpty(storePassword) ? new char[0] : storePassword.ToCharArray());
3641
}
3742

38-
return store;
43+
if (workingStore.Aliases.Where(p => string.IsNullOrEmpty(p)).Count() > 0 && workingStore.Aliases.Where(p => !string.IsNullOrEmpty(p)).Count() > 0)
44+
throw new Exception("Certificate store contains entries with both empty and non-empty friendly names. This configuration is not supported in this store type.");
45+
46+
HasEmptyAliases = workingStore.Aliases.Where(p => string.IsNullOrEmpty(p)).Count() > 0;
47+
48+
returnStore = ConvertAliases(workingStore, true);
49+
50+
return returnStore;
3951
}
4052

4153
public List<SerializedStoreInfo> SerializeRemoteCertificateStore(Pkcs12Store certificateStore, string storePath, string storeFileName, string storePassword, IRemoteHandler remoteHandler)
4254
{
55+
Pkcs12StoreBuilder storeBuilder = new Pkcs12StoreBuilder();
56+
Pkcs12Store workingStore = storeBuilder.Build();
57+
58+
foreach (string alias in certificateStore.Aliases)
59+
{
60+
if (certificateStore.IsKeyEntry(alias))
61+
{
62+
workingStore.SetKeyEntry(alias, certificateStore.GetKey(alias), certificateStore.GetCertificateChain(alias));
63+
}
64+
else
65+
{
66+
workingStore.SetCertificateEntry(alias, certificateStore.GetCertificate(alias));
67+
}
68+
}
69+
70+
Pkcs12Store outputCertificateStore = ConvertAliases(workingStore, false);
71+
4372
using (MemoryStream outStream = new MemoryStream())
4473
{
45-
certificateStore.Save(outStream, string.IsNullOrEmpty(storePassword) ? new char[0] : storePassword.ToCharArray(), new Org.BouncyCastle.Security.SecureRandom());
74+
outputCertificateStore.Save(outStream, string.IsNullOrEmpty(storePassword) ? new char[0] : storePassword.ToCharArray(), new Org.BouncyCastle.Security.SecureRandom());
4675

4776
List<SerializedStoreInfo> storeInfo = new List<SerializedStoreInfo>();
4877
storeInfo.Add(new SerializedStoreInfo() { FilePath = storePath+storeFileName, Contents = outStream.ToArray() });
@@ -55,5 +84,34 @@ public string GetPrivateKeyPath()
5584
{
5685
return null;
5786
}
87+
88+
private Pkcs12Store ConvertAliases(Pkcs12Store workingStore, bool useThumbprintAsAlias)
89+
{
90+
Pkcs12StoreBuilder storeBuilder = new Pkcs12StoreBuilder();
91+
Pkcs12Store returnStore = storeBuilder.Build();
92+
93+
if (HasEmptyAliases)
94+
{
95+
foreach (string alias in workingStore.Aliases)
96+
{
97+
if (workingStore.IsKeyEntry(alias))
98+
{
99+
X509CertificateEntry cert = workingStore.GetCertificate(alias);
100+
returnStore.SetKeyEntry(useThumbprintAsAlias ? cert.Certificate.Thumbprint() : string.Empty, workingStore.GetKey(alias), workingStore.GetCertificateChain(alias));
101+
}
102+
else
103+
{
104+
X509CertificateEntry cert = workingStore.GetCertificate(alias);
105+
returnStore.SetCertificateEntry(cert.Certificate.Thumbprint(), cert);
106+
}
107+
}
108+
}
109+
else
110+
{
111+
returnStore = workingStore;
112+
}
113+
114+
return returnStore;
115+
}
58116
}
59117
}

RemoteFile/InventoryBase.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +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($"Job Properties:");
34+
logger.LogDebug($"Job Properties:");
3535
foreach (KeyValuePair<string, object> keyValue in config.JobProperties ?? new Dictionary<string,object>())
3636
{
3737
logger.LogDebug($" {keyValue.Key}: {keyValue.Value}");

RemoteFile/ManagementBase.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
3030
ILogger logger = LogHandler.GetClassLogger(this.GetType());
3131
logger.LogDebug($"Begin {config.Capability} for job id {config.JobId}...");
3232
logger.LogDebug($"Server: {config.CertificateStoreDetails.ClientMachine}");
33-
logger.LogDebug($"Store Path: {config.CertificateStoreDetails.StorePath}");
33+
logger.LogDebug($"Store Path: {config.CertificateStoreDetails.StorePath}");
3434
logger.LogDebug($"Job Properties:");
3535
foreach (KeyValuePair<string, object> keyValue in config.JobProperties == null ? new Dictionary<string, object>() : config.JobProperties)
3636
{

RemoteFile/Models/SerializedStoreInfo.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace Keyfactor.Extensions.Orchestrator.RemoteFile.Models
1111
{
12-
class SerializedStoreInfo : X509Certificate2
12+
internal class SerializedStoreInfo
1313
{
1414
public string FilePath { get; set; }
1515

RemoteFile/RemoteCertificateStore.cs

+9-3
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ internal void Terminate()
137137
logger.MethodExit(LogLevel.Debug);
138138
}
139139

140-
internal List<string> FindStores(string[] paths, string[] extensions, string[] files, bool includeSymLinks)
140+
internal List<string> FindStores(string[] paths, string[] extensions, string[] files, string[] ignoredDirs, bool includeSymLinks)
141141
{
142142
logger.MethodEntry(LogLevel.Debug);
143143

@@ -153,7 +153,7 @@ internal List<string> FindStores(string[] paths, string[] extensions, string[] f
153153
if (DiscoveredStores != null)
154154
return DiscoveredStores;
155155

156-
return ServerType == ServerTypeEnum.Linux ? FindStoresLinux(paths, extensions, files, includeSymLinks) : FindStoresWindows(paths, extensions, files);
156+
return ServerType == ServerTypeEnum.Linux ? FindStoresLinux(paths, extensions, files, ignoredDirs, includeSymLinks) : FindStoresWindows(paths, extensions, files);
157157
}
158158

159159
internal List<X509Certificate2Collection> GetCertificateChains()
@@ -511,14 +511,20 @@ private bool IsValueSafeRegex(string value)
511511
return regex.IsMatch(value);
512512
}
513513

514-
private List<string> FindStoresLinux(string[] paths, string[] extensions, string[] fileNames, bool includeSymLinks)
514+
private List<string> FindStoresLinux(string[] paths, string[] extensions, string[] fileNames, string[] ignoredDirs, bool includeSymLinks)
515515
{
516516
logger.MethodEntry(LogLevel.Debug);
517517

518518
try
519519
{
520520
string concatPaths = string.Join(" ", paths);
521521
string command = $"find {concatPaths} -path /proc -prune -o ";
522+
523+
foreach (string ignoredDir in ignoredDirs)
524+
{
525+
command += $"-path {ignoredDir} -prune -o ";
526+
}
527+
522528
if (!includeSymLinks)
523529
command += " -type f ";
524530

0 commit comments

Comments
 (0)