Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented certificate template export and partially import #75

Merged
merged 12 commits into from
May 17, 2024
Merged
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
52 changes: 35 additions & 17 deletions src/SysadminsLV.PKI.Win/CertificateTemplates/CertificateTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Interop.CERTENROLLLib;
using SysadminsLV.PKI.CertificateTemplates;
using SysadminsLV.PKI.Management.ActiveDirectory;
using SysadminsLV.PKI.Security.AccessControl;
using SysadminsLV.PKI.Utils;
Expand All @@ -15,12 +15,12 @@ namespace PKI.CertificateTemplates;
/// Represents a certificate template object.
/// </summary>
public class CertificateTemplate {
Int32 major, minor, flags;
Int32 major, minor;
CertificateTemplateFlags flags;
static readonly String _baseDsPath = $"CN=Certificate Templates, CN=Public Key Services, CN=Services,{DsUtils.ConfigContext}";

internal CertificateTemplate(IX509CertificateTemplate template) {
internal CertificateTemplate(IAdcsCertificateTemplate template) {
initializeFromCom(template);
Settings = new CertificateTemplateSettings(template);
}
/// <param name="findType">
/// Specifies certificate template search type. The search type can be either:
Expand Down Expand Up @@ -60,7 +60,7 @@ internal CertificateTemplate(IX509CertificateTemplate template) {
/// <summary>
/// This flag indicates whether clients can perform autoenrollment for the specified template.
/// </summary>
public Boolean AutoenrollmentAllowed => SchemaVersion > 1 && (flags & (Int32)CertificateTemplateFlags.Autoenrollment) != 0;
public Boolean AutoenrollmentAllowed => SchemaVersion > 1 && (flags & CertificateTemplateFlags.Autoenrollment) != 0;

/// <summary>
/// Gets certificate template's object identifier. Object identifiers are used to uniquely identify certificate template. While
Expand Down Expand Up @@ -194,7 +194,7 @@ void initializeFromDs(String ldapPath) {
DsUtils.PropPkiKeyUsage,
DsUtils.PropPkiKeyUsageCng
);
flags = props.GetDsScalarValue<Int32>(DsUtils.PropFlags);
flags = props.GetDsScalarValue<CertificateTemplateFlags>(DsUtils.PropFlags);
Name = props.GetDsScalarValue<String>(DsUtils.PropCN);
DistinguishedName = ldapPath.Replace("LDAP://", null); // we have to use ldapPath, because it is fully escaped and re-usable. DN is not.
DisplayName = props.GetDsScalarValue<String>(DsUtils.PropDisplayName);
Expand All @@ -203,7 +203,7 @@ void initializeFromDs(String ldapPath) {
SchemaVersion = props.GetDsScalarValue<Int32>(DsUtils.PropPkiSchemaVersion);
OID = new Oid(props.GetDsScalarValue<String>(DsUtils.PropCertTemplateOid));
LastWriteTime = props.GetDsScalarValue<DateTime>(DsUtils.PropWhenChanged);
Settings = new CertificateTemplateSettings(props);
Settings = new CertificateTemplateSettings(props, this);

setClientSupport(props.GetDsScalarValue<PrivateKeyFlags>(DsUtils.PropPkiPKeyFlags));
setServerSupport(props.GetDsScalarValue<PrivateKeyFlags>(DsUtils.PropPkiPKeyFlags));
Expand Down Expand Up @@ -256,17 +256,18 @@ String getServerSupportLegacy() {
_ => "Unknown"
};
}
void initializeFromCom(IX509CertificateTemplate template) {
Name = (String)template.Property[EnrollmentTemplateProperty.TemplatePropCommonName];
DisplayName = (String)template.Property[EnrollmentTemplateProperty.TemplatePropFriendlyName];
OID = new Oid(((IObjectId)template.Property[EnrollmentTemplateProperty.TemplatePropOID]).Value);
void initializeFromCom(IAdcsCertificateTemplate template) {
Name = template.CommonName;
DisplayName = template.DisplayName;
OID = new Oid(template.Oid, DisplayName);
// we use Convert.ToInt32, because COM variants can be either signed or unsigned integer based on a platform.
major = Convert.ToInt32(template.Property[EnrollmentTemplateProperty.TemplatePropMajorRevision]);
minor = Convert.ToInt32(template.Property[EnrollmentTemplateProperty.TemplatePropMinorRevision]);
SchemaVersion = Convert.ToInt32(template.Property[EnrollmentTemplateProperty.TemplatePropSchemaVersion]);
Settings = new CertificateTemplateSettings(template);
setClientSupport((PrivateKeyFlags)Convert.ToInt32(template.Property[EnrollmentTemplateProperty.TemplatePropPrivateKeyFlags]));
setServerSupport((PrivateKeyFlags)Convert.ToInt32(template.Property[EnrollmentTemplateProperty.TemplatePropPrivateKeyFlags]));
major = template.MajorVersion;
minor = template.MinorVersion;
flags = template.Flags;
SchemaVersion = template.SchemaVersion;
Settings = new CertificateTemplateSettings(template, this);
setClientSupport(template.CryptPrivateKeyFlags);
setServerSupport(template.CryptPrivateKeyFlags);
}

/// <summary>
Expand Down Expand Up @@ -333,6 +334,23 @@ public CertTemplateSecurityDescriptor GetSecurityDescriptor() {
return new CertTemplateSecurityDescriptor(this);
}
/// <summary>
/// Gets template major version. Major version is used by autoenrollment component to determine if certificate
/// needs to be renewed prior to scheduled renewal period.
/// </summary>
/// <returns>Template major version.</returns>
public Int32 GetMajorVersion() {
return major;
}
/// <summary>
/// Gets template minor version. Minor version is increased with every template setting change
/// (excluding ACL, common and display names) and used by autoenrollment component to determine whether to use
/// new or renewal request during re-enrollment.
/// </summary>
/// <returns>Template minor version.</returns>
public Int32 GetMinorVersion() {
return minor;
}
/// <summary>
/// Gets certificate template textual representation.
/// </summary>
/// <returns>Certificate template textual representation.</returns>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#nullable enable
using System;
using System.Collections.Generic;
using PKI.CertificateTemplates;

namespace SysadminsLV.PKI.CertificateTemplates;

/// <summary>
/// Represents a collection of AD CS Certificate Templates.
/// </summary>
public class CertificateTemplateCollection : BasicCollection<CertificateTemplate> {

/// <summary>
/// Initializes a new instance of <strong>Certificate Template</strong> class.
/// </summary>
public CertificateTemplateCollection() { }
/// <summary>
/// Initializes a new instance of <strong>Certificate Template</strong> class from existing collection.
/// </summary>
/// <param name="collection">Existing collection of certificate templates.</param>
public CertificateTemplateCollection(IEnumerable<CertificateTemplate> collection) : base(collection) { }

/// <summary>
/// Exports current collection into a specified serialized format.
/// </summary>
/// <param name="format">Specifies the format to export current collection into.</param>
/// <returns>Serialized string.</returns>
/// <exception cref="ArgumentOutOfRangeException"><strong>format</strong> value is not recognized.</exception>
public String Export(CertificateTemplateExportFormat format) {
return format switch {
CertificateTemplateExportFormat.XCep => new CertificateTemplateXCepFormatter().Serialize(this),
_ => throw new ArgumentOutOfRangeException(nameof(format), format, null)
};
}
/// <summary>
/// Exports current collection using specified formatter that implements <see cref="ICertificateTemplateFormatter"/> interface.
/// </summary>
/// <param name="formatter">Formatter.</param>
/// <returns>Serialized string.</returns>
public String Export(ICertificateTemplateFormatter formatter) {
return formatter.Serialize(this);
}

/// <summary>
/// Imports serialized certificate templates using specified serializer format into current list. If successful, imported templates
/// will overwrite all templates in current collection.
/// </summary>
/// <param name="serializedString">Serialized string.</param>
/// <param name="format">Serialized string format.</param>
public void Import(String serializedString, CertificateTemplateExportFormat format) {
CertificateTemplateCollection collection = format switch {
CertificateTemplateExportFormat.XCep => new CertificateTemplateXCepFormatter().Deserialize(serializedString),
_ => throw new ArgumentOutOfRangeException(nameof(format), format, null)
};

Clear();
AddRange(collection);
}
/// <summary>
/// Imports serialized certificate templates using specified formatter into current list. If successful, imported templates
/// will overwrite all templates in current collection.
/// </summary>
/// <param name="serializedString">Serialized string.</param>
/// <param name="formatter">Formatter.</param>
public void Import(String serializedString, ICertificateTemplateFormatter formatter) {
CertificateTemplateCollection collection = formatter.Deserialize(serializedString);

Clear();
AddRange(collection);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#nullable enable
namespace SysadminsLV.PKI.CertificateTemplates;

/// <summary>
/// Represents supported built-in certificate template export/serialization formats.
/// </summary>
public enum CertificateTemplateExportFormat {
/// <summary>
/// Represents <see href="https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-xcep/08ec4475-32c2-457d-8c27-5a176660a210">[MS-XCEP]</see>
/// compatible certificate template format.
/// </summary>
XCep = 0
}
Loading