Skip to content

Commit b946b16

Browse files
Implement per-instance cert multi-format export
1 parent 77893ca commit b946b16

File tree

6 files changed

+233
-118
lines changed

6 files changed

+233
-118
lines changed

src/Certify.Core/Management/CertifyManager/CertifyManager.ManagedCertificates.cs

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22
using System.Collections.Concurrent;
33
using System.Collections.Generic;
44
using System.Diagnostics;
5+
using System.IO;
56
using System.Linq;
67
using System.Threading.Tasks;
7-
using Certify.Models.Hub;
88
using Certify.Models;
9+
using Certify.Models.Config;
10+
using Certify.Models.Hub;
911
using Certify.Models.Providers;
1012
using Certify.Models.Reporting;
1113
using Certify.Models.Shared;
12-
using Certify.Models.Config;
14+
using Certify.Shared.Core.Utils.PKI;
1315

1416
namespace Certify.Management
1517
{
@@ -531,6 +533,87 @@ public async Task<List<ActionStep>> GeneratePreview(ManagedCertificate item)
531533
return await new PreviewManager().GeneratePreview(item, serverProvider, this, _credentialsManager);
532534
}
533535

536+
/// <summary>
537+
/// Prepare an export of a managed certificate in the given format (if certificate present)
538+
/// </summary>
539+
/// <param name="item"></param>
540+
/// <returns></returns>
541+
public async Task<ActionResult<byte[]>> ExportCertificate(string managedCertId, string format)
542+
{
543+
var item = await GetManagedCertificate(managedCertId);
544+
545+
if (string.IsNullOrEmpty(item?.CertificatePath) || !File.Exists(item?.CertificatePath))
546+
{
547+
return new ActionResult<byte[]>("Source certificate file is not present. Export cannot continue.", false);
548+
}
549+
550+
if (string.IsNullOrWhiteSpace(format))
551+
{
552+
format = "pfx";
553+
}
554+
555+
try
556+
{
557+
var pfxData = File.ReadAllBytes(item.CertificatePath);
558+
559+
var certPwd = "";
560+
561+
// if credential used for private key, check if we can decrypt that (unless we exporting PFX which is just a file copy)
562+
if (!string.IsNullOrWhiteSpace(item.CertificatePasswordCredentialId) && format != "pfx")
563+
{
564+
var cred = await GetCredentialsManager().GetUnlockedCredentialsDictionary(item.CertificatePasswordCredentialId);
565+
if (cred != null)
566+
{
567+
certPwd = cred["password"];
568+
}
569+
else
570+
{
571+
return new ActionResult<byte[]>($"Export - the credentials for this export could not be unlocked or were not accessible {item.CertificatePasswordCredentialId}.", false);
572+
}
573+
}
574+
575+
byte[] result = [];
576+
577+
if (format == "pfx")
578+
{
579+
result = pfxData;
580+
}
581+
else if (format == "pem_key")
582+
{
583+
result = CertUtils.GetCertComponentsAsPEMBytes(pfxData, certPwd, ExportFlags.PrivateKey);
584+
}
585+
else if (format == "pem_fullchain")
586+
{
587+
result = CertUtils.GetCertComponentsAsPEMBytes(pfxData, certPwd, ExportFlags.EndEntityCertificate | ExportFlags.IntermediateCertificates);
588+
}
589+
else if (format == "pem_fullchain_key")
590+
{
591+
result = CertUtils.GetCertComponentsAsPEMBytes(pfxData, certPwd, ExportFlags.PrivateKey | ExportFlags.EndEntityCertificate | ExportFlags.IntermediateCertificates);
592+
}
593+
else if (format == "pem_fullchain_root")
594+
{
595+
result = CertUtils.GetCertComponentsAsPEMBytes(pfxData, certPwd, ExportFlags.EndEntityCertificate | ExportFlags.IntermediateCertificates | ExportFlags.RootCertificate);
596+
}
597+
else if (format == "pem_fullchain_root_key")
598+
{
599+
result = CertUtils.GetCertComponentsAsPEMBytes(pfxData, certPwd, ExportFlags.PrivateKey | ExportFlags.EndEntityCertificate | ExportFlags.IntermediateCertificates | ExportFlags.RootCertificate);
600+
}
601+
602+
if (result.Length == 0)
603+
{
604+
return new ActionResult<byte[]>($"Export - no files where selected for export or export could not be applied for source certificate.", false);
605+
}
606+
else
607+
{
608+
return new ActionResult<byte[]> { Result = result, IsSuccess = true };
609+
}
610+
}
611+
catch (Exception exp)
612+
{
613+
return new ActionResult<byte[]>($"Export - {exp}", false);
614+
}
615+
}
616+
534617
public async Task<List<DnsZone>> GetDnsProviderZones(string providerTypeId, string credentialId)
535618
{
536619
var dnsHelper = new Core.Management.Challenges.DnsChallengeHelper(_credentialsManager);

src/Certify.Core/Management/CertifyManager/CertifyManager.ManagementHub.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ private async Task<InstanceCommandResult> _managementServerClient_OnGetCommandRe
142142

143143
val = await GeneratePreview(managedCert);
144144
}
145+
else if (arg.CommandType == ManagementHubCommands.ExportCertificate)
146+
{
147+
var args = JsonSerializer.Deserialize<KeyValuePair<string, string>[]>(arg.Value);
148+
var managedCertIdArg = args.FirstOrDefault(a => a.Key == "managedCertId");
149+
var format = args.FirstOrDefault(a => a.Key == "format");
150+
val = await ExportCertificate(managedCertIdArg.Value, format.Value);
151+
}
145152
else if (arg.CommandType == ManagementHubCommands.UpdateManagedItem)
146153
{
147154
// update a single managed item

src/Certify.Models/Hub/ManagementHubMessages.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class ManagementHubCommands
1919
public const string GetManagedItem = "GetManagedItem";
2020
public const string GetManagedItemLog = "GetManagedItemLog";
2121
public const string GetManagedItemRenewalPreview = "GetManagedItemRenewalPreview";
22+
public const string ExportCertificate = "ExportCertificate";
2223

2324
public const string UpdateManagedItem = "UpdateManagedItem";
2425
public const string RemoveManagedItem = "RemoveManagedItem";

0 commit comments

Comments
 (0)