Skip to content

Commit 2e85553

Browse files
utilizing the Keyfactor.PKI libraries for cert processing
1 parent 9e9bbf6 commit 2e85553

File tree

2 files changed

+58
-46
lines changed

2 files changed

+58
-46
lines changed

alteon-orchestrator/Jobs/Management.cs

Lines changed: 57 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
using System;
16+
using System.Collections.Generic;
1617
using System.IO;
1718
using System.Linq;
1819
using System.Security.Cryptography.X509Certificates;
@@ -21,6 +22,7 @@
2122
using Keyfactor.Logging;
2223
using Keyfactor.Orchestrators.Common.Enums;
2324
using Keyfactor.Orchestrators.Extensions;
25+
using Keyfactor.PKI.X509;
2426
using Microsoft.Extensions.Logging;
2527
using Org.BouncyCastle.Crypto;
2628
using Org.BouncyCastle.OpenSsl;
@@ -66,16 +68,16 @@ protected virtual async Task<JobResult> PerformAddition(string alias, string pfx
6668
/// if there is a cert and key, import as "pair"
6769
/// if there is no key, and the issuer and subject match, import as "clca" (trusted CA)
6870
/// if there is no key, and the issuer and subject are different, import as "inca" (intermediate CA)
69-
71+
7072
byte[] bytes;
7173
X509Certificate2 x509;
72-
string pemCert, privateKeyString;
74+
string pemCert, pemKey;
7375

7476
try
7577
{
7678
bytes = Convert.FromBase64String(entryContents);
7779
x509 = new X509Certificate2(bytes, pfxPassword, X509KeyStorageFlags.Exportable);
78-
(pemCert, privateKeyString) = GetPemFromPfx(bytes, pfxPassword.ToCharArray());
80+
(pemCert, pemKey) = GetPemFromPfx(bytes, pfxPassword);
7981
}
8082
catch (Exception ex)
8183
{
@@ -117,7 +119,7 @@ protected virtual async Task<JobResult> PerformAddition(string alias, string pfx
117119
// add key and cert separately.
118120
// this needs to be done in the following order: key, then cert (per Alteon support)
119121
logger.LogTrace($"adding key and then certificate for certificate with alias {alias}");
120-
await aClient.AddCertificate(alias, pfxPassword, Pemify(privateKeyString), AlteonCertTypes.KEY_ONLY);
122+
await aClient.AddCertificate(alias, pfxPassword, pemKey, AlteonCertTypes.KEY_ONLY);
121123
await aClient.AddCertificate(alias, pfxPassword, pemCert, AlteonCertTypes.CERT_ONLY);
122124
}
123125
else
@@ -158,7 +160,7 @@ protected virtual async Task<JobResult> PerformRemoval(string alias, long jobHis
158160
try
159161
{
160162
await aClient.RemoveCertificate(alias);
161-
complete.Result = OrchestratorJobStatusJobResult.Success;
163+
complete.Result = OrchestratorJobStatusJobResult.Success;
162164
}
163165

164166
catch (Exception ex)
@@ -169,61 +171,70 @@ protected virtual async Task<JobResult> PerformRemoval(string alias, long jobHis
169171
return complete;
170172
}
171173

172-
private (string, string) GetPemFromPfx(byte[] pfxBytes, char[] pfxPassword)
174+
private (string, string) GetPemFromPfx(byte[] pfxBytes, string pfxPassword)
173175
{
174176
try
175177
{
176-
logger.LogDebug("Entering GetPemFromPfx(byte[] pfxBytes, char[] pfxPassword)");
177-
var p = new Pkcs12Store(new MemoryStream(pfxBytes), pfxPassword);
178-
179-
// Extract private key
180-
var memoryStream = new MemoryStream();
181-
TextWriter streamWriter = new StreamWriter(memoryStream);
182-
var pemWriter = new PemWriter(streamWriter);
183-
184-
var alias = p.Aliases.Cast<string>().SingleOrDefault(a => p.IsKeyEntry(a));
185-
logger.LogTrace($"alias: {alias}");
186-
187-
var publicKey = p.GetCertificate(alias).Certificate.GetPublicKey();
188-
if (p.GetKey(alias) == null) throw new Exception($"Unable to get the key for alias: {alias}");
189-
var privateKey = p.GetKey(alias).Key;
190-
var keyPair = new AsymmetricCipherKeyPair(publicKey, privateKey);
191-
192-
pemWriter.WriteObject(keyPair.Private);
193-
streamWriter.Flush();
194-
var privateKeyString = Encoding.ASCII.GetString(memoryStream.GetBuffer()).Trim().Replace("\r", "")
195-
.Replace("\0", "");
196-
memoryStream.Close();
197-
streamWriter.Close();
198-
199-
// Extract server certificate
200-
var certStart = "-----BEGIN CERTIFICATE-----\n";
201-
var certEnd = "\n-----END CERTIFICATE-----";
202-
203-
string Pemify(string ss)
178+
logger.MethodEntry();
179+
180+
CertificateCollectionConverter converter = CertificateCollectionConverterFactory.FromDER(pfxBytes, pfxPassword);
181+
string pfxPem = converter.ToPEM(pfxPassword);
182+
List<X509Certificate2> clist = converter.ToX509Certificate2List(pfxPassword);
183+
StringBuilder certPemBuilder = new StringBuilder();
184+
185+
//reordering of certificate chain necessary because of BouncyCastle bug. Being fixed in a later release
186+
if (clist.Count > 1)
187+
clist = ReorderPEMLIst(clist);
188+
189+
logger.LogTrace("Building certificate PEM");
190+
foreach (X509Certificate2 cert in clist)
204191
{
205-
return ss.Length <= 64 ? ss : ss.Substring(0, 64) + "\n" + Pemify(ss.Substring(64));
192+
certPemBuilder.AppendLine("-----BEGIN CERTIFICATE-----");
193+
certPemBuilder.AppendLine(
194+
Convert.ToBase64String(cert.RawData, Base64FormattingOptions.InsertLineBreaks));
195+
certPemBuilder.AppendLine("-----END CERTIFICATE-----");
206196
}
207197

208-
var certPem =
209-
certStart + Pemify(Convert.ToBase64String(p.GetCertificate(alias).Certificate.GetEncoded())) +
210-
certEnd;
211-
logger.LogTrace($"certPem: {certPem}");
212-
logger.LogDebug("Exiting GetPemFromPfx(byte[] pfxBytes, char[] pfxPassword)");
213-
return (certPem, privateKeyString);
198+
logger.LogTrace("Building the key PEM");
199+
byte[] pkBytes = PKI.PrivateKeys.PrivateKeyConverterFactory.FromPKCS12(pfxBytes, pfxPassword.ToString()).ToPkcs8BlobUnencrypted();
200+
StringBuilder keyPemBuilder = new StringBuilder();
201+
keyPemBuilder.AppendLine("-----BEGIN PRIVATE KEY-----");
202+
keyPemBuilder.AppendLine(
203+
Convert.ToBase64String(pkBytes, Base64FormattingOptions.InsertLineBreaks));
204+
keyPemBuilder.AppendLine("-----END PRIVATE KEY-----");
205+
206+
logger.LogTrace($"certPem: {certPemBuilder}");
207+
logger.MethodExit();
208+
return (certPemBuilder.ToString(), keyPemBuilder.ToString());
214209
}
215210
catch (Exception e)
216211
{
217212
logger.LogError(
218-
$"Error Occurred in GetPemFromPfx(byte[] pfxBytes, char[] pfxPassword): {LogHandler.FlattenException(e)}");
213+
$"Error Occurred in GetPemFromPfx(byte[] pfxBytes, string pfxPassword): {LogHandler.FlattenException(e)}");
219214
throw;
220215
}
221216
}
222-
223-
private string Pemify(string ss)
217+
private List<X509Certificate2> ReorderPEMLIst(List<X509Certificate2> certList)
224218
{
225-
return ss.Length <= 64 ? ss : ss.Substring(0, 64) + "\n" + Pemify(ss.Substring(64));
226-
}
219+
List<X509Certificate2> rtnList = new List<X509Certificate2>();
220+
X509Certificate2 root = certList.FirstOrDefault(p => p.IssuerName.RawData.SequenceEqual(p.SubjectName.RawData));
221+
if (root == null || string.IsNullOrEmpty(root.SerialNumber))
222+
throw new Exception("Invalid certificate chain. No root CA certificate found.");
227223

224+
rtnList.Add(root);
225+
226+
X509Certificate2 parentCert = root;
227+
for (int i = 1; i < certList.Count; i++)
228+
{
229+
X509Certificate2 childCert = certList.FirstOrDefault(p => p.IssuerName.RawData.SequenceEqual(parentCert.SubjectName.RawData) && !p.IssuerName.RawData.SequenceEqual(p.SubjectName.RawData));
230+
if (root == null || string.IsNullOrEmpty(root.SerialNumber))
231+
throw new Exception("Invalid certificate chain. End entity or issuing CA certificate not found.");
232+
233+
rtnList.Insert(0, childCert);
234+
parentCert = childCert;
235+
}
236+
237+
return rtnList;
238+
}
228239
}
229240
}

alteon-orchestrator/alteon-orchestrator.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<PackageReference Include="Keyfactor.Orchestrators.Common" Version="3.1.2" />
1919
<PackageReference Include="Keyfactor.Orchestrators.IOrchestratorJobExtensions" Version="0.6.0" />
2020
<PackageReference Include="Keyfactor.Orchestrators.IOrchestratorRegistrationUpdater" Version="1.0.3" />
21+
<PackageReference Include="Keyfactor.PKI" Version="5.5.0" />
2122
<PackageReference Include="NLog" Version="5.0.1" />
2223
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" />
2324
<PackageReference Include="RestSharp" Version="112.0.0" />

0 commit comments

Comments
 (0)