Skip to content

Commit

Permalink
GDS ready for ECC Step1
Browse files Browse the repository at this point in the history
  • Loading branch information
romanett committed Nov 3, 2024
1 parent 88878e6 commit 780c78f
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 45 deletions.
36 changes: 30 additions & 6 deletions Libraries/Opc.Ua.Gds.Server.Common/ApplicationsNodeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,19 @@ public override void CreateAddressSpace(IDictionary<NodeId, IList<IReference>> e
{ Ua.ObjectTypeIds.UserCredentialCertificateType, nameof(Ua.ObjectTypeIds.UserCredentialCertificateType) },
{ Ua.ObjectTypeIds.ApplicationCertificateType, nameof(Ua.ObjectTypeIds.ApplicationCertificateType) },
{ Ua.ObjectTypeIds.RsaMinApplicationCertificateType, nameof(Ua.ObjectTypeIds.RsaMinApplicationCertificateType) },
{ Ua.ObjectTypeIds.RsaSha256ApplicationCertificateType, nameof(Ua.ObjectTypeIds.RsaSha256ApplicationCertificateType) }
{ Ua.ObjectTypeIds.RsaSha256ApplicationCertificateType, nameof(Ua.ObjectTypeIds.RsaSha256ApplicationCertificateType) },
// ECC / V1.05
#if ECC_SUPPORT
{ Ua.ObjectTypeIds.EccApplicationCertificateType, nameof(Ua.ObjectTypeIds.EccApplicationCertificateType) },
{ Ua.ObjectTypeIds.EccNistP256ApplicationCertificateType, nameof(Ua.ObjectTypeIds.EccNistP256ApplicationCertificateType) },
{ Ua.ObjectTypeIds.EccNistP384ApplicationCertificateType, nameof(Ua.ObjectTypeIds.EccNistP384ApplicationCertificateType) },
{ Ua.ObjectTypeIds.EccBrainpoolP256r1ApplicationCertificateType, nameof(Ua.ObjectTypeIds.EccBrainpoolP256r1ApplicationCertificateType) },
{ Ua.ObjectTypeIds.EccBrainpoolP384r1ApplicationCertificateType, nameof(Ua.ObjectTypeIds.EccBrainpoolP384r1ApplicationCertificateType) },
#if CURVE25519
{ Ua.ObjectTypeIds.EccCurve25519ApplicationCertificateType, nameof(Ua.ObjectTypeIds.EccCurve25519ApplicationCertificateType) },
{ Ua.ObjectTypeIds.EccCurve448ApplicationCertificateType, nameof(Ua.ObjectTypeIds.EccCurve448ApplicationCertificateType) },
#endif
#endif
};

}
Expand Down Expand Up @@ -414,7 +426,7 @@ protected override NodeState AddBehaviourToPredefinedNode(ISystemContext context
activeNode.CheckRevocationStatus.OnCall = new CheckRevocationStatusMethodStateMethodCallHandler(OnCheckRevocationStatus);
activeNode.GetCertificates.OnCall = new GetCertificatesMethodStateMethodCallHandler(OnGetCertificates);

activeNode.CertificateGroups.DefaultApplicationGroup.CertificateTypes.Value = new NodeId[] { Opc.Ua.ObjectTypeIds.RsaSha256ApplicationCertificateType };
activeNode.CertificateGroups.DefaultApplicationGroup.CertificateTypes.Value = new NodeId[] { Ua.ObjectTypeIds.ApplicationCertificateType };
activeNode.CertificateGroups.DefaultApplicationGroup.TrustList.LastUpdateTime.Value = DateTime.UtcNow;
activeNode.CertificateGroups.DefaultApplicationGroup.TrustList.Writable.Value = false;
activeNode.CertificateGroups.DefaultApplicationGroup.TrustList.UserWritable.Value = false;
Expand Down Expand Up @@ -966,7 +978,7 @@ private ServiceResult OnStartNewKeyPairRequest(
object[] inputArguments = new object[] { applicationId, certificateGroupId, certificateTypeId, subjectName, domainNames, privateKeyFormat, privateKeyPassword };
Server.ReportCertificateRequestedAuditEvent(context, objectId, method, inputArguments, certificateGroupId, certificateTypeId);

AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.CertificateAuthorityAdminOrSelfAdmin, applicationId); ;
AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.CertificateAuthorityAdminOrSelfAdmin, applicationId);

var application = m_database.GetApplication(applicationId);

Expand Down Expand Up @@ -1166,7 +1178,7 @@ private ServiceResult OnFinishRequest(
signedCertificate = null;
issuerCertificates = null;
privateKey = null;
AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.CertificateAuthorityAdminOrSelfAdmin, applicationId); ;
AuthorizationHelper.HasAuthorization(context, AuthorizationHelper.CertificateAuthorityAdminOrSelfAdmin, applicationId);

var application = m_database.GetApplication(applicationId);
if (application == null)
Expand Down Expand Up @@ -1547,8 +1559,20 @@ protected void SetCertificateGroupNodes(ICertificateGroup certificateGroup)
certificateGroup.DefaultTrustList = (TrustListState)FindPredefinedNode(ExpandedNodeId.ToNodeId(Opc.Ua.Gds.ObjectIds.Directory_CertificateGroups_DefaultUserTokenGroup_TrustList, Server.NamespaceUris), typeof(TrustListState));
}
else if (Utils.IsEqual(certificateType, Opc.Ua.ObjectTypeIds.ApplicationCertificateType) ||
Utils.IsEqual(certificateType, Opc.Ua.ObjectTypeIds.RsaMinApplicationCertificateType) ||
Utils.IsEqual(certificateType, Opc.Ua.ObjectTypeIds.RsaSha256ApplicationCertificateType)
Utils.IsEqual(certificateType, Opc.Ua.ObjectTypeIds.RsaMinApplicationCertificateType) ||
Utils.IsEqual(certificateType, Opc.Ua.ObjectTypeIds.RsaSha256ApplicationCertificateType)

#if ECC_SUPPORT
|| Utils.IsEqual(certificateType, Ua.ObjectTypeIds.EccApplicationCertificateType)
|| Utils.IsEqual(certificateType, Ua.ObjectTypeIds.EccNistP256ApplicationCertificateType)
|| Utils.IsEqual(certificateType, Ua.ObjectTypeIds.EccNistP384ApplicationCertificateType)
|| Utils.IsEqual(certificateType, Ua.ObjectTypeIds.EccBrainpoolP256r1ApplicationCertificateType)
|| Utils.IsEqual(certificateType, Ua.ObjectTypeIds.EccBrainpoolP384r1ApplicationCertificateType)
#if CURVE25519
|| Utils.IsEqual(certificateType, Ua.ObjectTypeIds.EccCurve25519ApplicationCertificateType)
|| Utils.IsEqual(certificateType, Ua.ObjectTypeIds.EccCurve448ApplicationCertificateType)
#endif
#endif
)
{
certificateGroup.Id = m_defaultApplicationGroupId;
Expand Down
161 changes: 123 additions & 38 deletions Libraries/Opc.Ua.Gds.Server.Common/CertificateGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using Opc.Ua.Security;
using Opc.Ua.Security.Certificates;

namespace Opc.Ua.Gds.Server
Expand Down Expand Up @@ -152,14 +152,24 @@ public virtual async Task<X509Certificate2KeyPair> NewKeyPairRequestAsync(
if (application.ApplicationNames == null) throw new ArgumentNullException(nameof(application.ApplicationNames));

using (var signingKey = await LoadSigningKeyAsync(Certificate, string.Empty).ConfigureAwait(false))
using (var certificate = CertificateFactory.CreateCertificate(
application.ApplicationUri,
application.ApplicationNames.Count > 0 ? application.ApplicationNames[0].Text : "ApplicationName",
subjectName,
domainNames)
.SetIssuer(signingKey)
.CreateForRSA())
{
X509Certificate2 certificate;

ICertificateBuilderIssuer builder = CertificateFactory.CreateCertificate(
application.ApplicationUri,
application.ApplicationNames.Count > 0 ? application.ApplicationNames[0].Text : "ApplicationName",
subjectName,
domainNames)
.SetIssuer(signingKey);
#if ECC_SUPPORT
certificate = TryGetECCCurve(out ECCurve curve) ?
builder.SetECCurve(curve).CreateForECDsa() :
builder.CreateForRSA();
#else
certificate = builder
.CreateForRSA();
#endif

byte[] privateKey;
if (privateKeyFormat == "PFX")
{
Expand All @@ -173,7 +183,13 @@ public virtual async Task<X509Certificate2KeyPair> NewKeyPairRequestAsync(
{
throw new ServiceResultException(StatusCodes.BadInvalidArgument, "Invalid private key format");
}
return new X509Certificate2KeyPair(new X509Certificate2(certificate.RawData), privateKeyFormat, privateKey);


var publicKey = new X509Certificate2(certificate.RawData);

certificate?.Dispose();

return new X509Certificate2KeyPair(publicKey, privateKeyFormat, privateKey);
}
}

Expand Down Expand Up @@ -289,14 +305,29 @@ public virtual async Task<X509Certificate2> SigningRequestAsync(
using (var signingKey = await LoadSigningKeyAsync(Certificate, string.Empty).ConfigureAwait(false))
{
X500DistinguishedName subjectName = new X500DistinguishedName(info.Subject.GetEncoded());
return CertificateBuilder.Create(subjectName)

X509Certificate2 certificate;

ICertificateBuilder builder = CertificateBuilder.Create(subjectName)
.AddExtension(new X509SubjectAltNameExtension(application.ApplicationUri, domainNames))
.SetNotBefore(yesterday)
.SetLifeTime(Configuration.DefaultCertificateLifetime)
.SetHashAlgorithm(X509Utils.GetRSAHashAlgorithmName(Configuration.DefaultCertificateHashSize))
.SetIssuer(signingKey)
.SetRSAPublicKey(info.SubjectPublicKeyInfo.GetEncoded())
.CreateForRSA();
.SetLifeTime(Configuration.DefaultCertificateLifetime);

#if ECC_SUPPORT
certificate = TryGetECCCurve(out ECCurve curve) ?
builder.SetIssuer(signingKey).SetECDsaPublicKey(info.SubjectPublicKeyInfo.GetEncoded()).CreateForECDsa() :
builder.SetHashAlgorithm(X509Utils.GetRSAHashAlgorithmName(Configuration.DefaultCertificateHashSize))
.SetIssuer(signingKey)
.SetRSAPublicKey(info.SubjectPublicKeyInfo.GetEncoded())
.CreateForRSA();
#else
certificate = builder.SetHashAlgorithm(X509Utils.GetRSAHashAlgorithmName(Configuration.DefaultCertificateHashSize))
.SetIssuer(signingKey)
.SetRSAPublicKey(info.SubjectPublicKeyInfo.GetEncoded())
.CreateForRSA();
#endif

return certificate;
}
}
catch (Exception ex)
Expand Down Expand Up @@ -325,37 +356,51 @@ string subjectName
}

DateTime yesterday = DateTime.Today.AddDays(-1);
using (X509Certificate2 newCertificate = await CertificateFactory.CreateCertificate(subjectName)
X509Certificate2 certificate;

ICertificateBuilder builder = CertificateFactory.CreateCertificate(subjectName)
.SetNotBefore(yesterday)
.SetLifeTime(Configuration.CACertificateLifetime)
.SetHashAlgorithm(X509Utils.GetRSAHashAlgorithmName(Configuration.CACertificateHashSize))
.SetCAConstraint()
.SetRSAKeySize(Configuration.CACertificateKeySize)
.CreateForRSA()
.AddToStoreAsync(AuthoritiesStore).ConfigureAwait(false))
{
.SetCAConstraint();

// save only public key
Certificate = new X509Certificate2(newCertificate.RawData);
#if ECC_SUPPORT
certificate = TryGetECCCurve(out ECCurve curve) ?
builder.SetECCurve(curve).CreateForECDsa() :
builder.SetHashAlgorithm(X509Utils.GetRSAHashAlgorithmName(Configuration.CACertificateHashSize))
.SetRSAKeySize(Configuration.CACertificateKeySize)
.CreateForRSA();
#else
certificate = builder.SetHashAlgorithm(X509Utils.GetRSAHashAlgorithmName(Configuration.CACertificateHashSize))
.SetRSAKeySize(Configuration.CACertificateKeySize)
.CreateForRSA();
#endif

// initialize revocation list
X509CRL crl = await RevokeCertificateAsync(AuthoritiesStore, newCertificate, null).ConfigureAwait(false);
await certificate.AddToStoreAsync(AuthoritiesStore).ConfigureAwait(false);

//Update TrustedList Store
if (crl != null)
{
// TODO: make CA trust selectable
var certificateStoreIdentifier = new CertificateStoreIdentifier(Configuration.TrustedListPath);
await UpdateAuthorityCertInCertificateStore(certificateStoreIdentifier).ConfigureAwait(false);
// save only public key
Certificate = new X509Certificate2(certificate.RawData);

// Update TrustedIssuerCertificates Store
if (IssuerCertificatesStore != null)
{
await UpdateAuthorityCertInCertificateStore(IssuerCertificatesStore).ConfigureAwait(false);
}
// initialize revocation list
X509CRL crl = await RevokeCertificateAsync(AuthoritiesStore, certificate, null).ConfigureAwait(false);

//Update TrustedList Store
if (crl != null)
{
// TODO: make CA trust selectable
var certificateStoreIdentifier = new CertificateStoreIdentifier(Configuration.TrustedListPath);
await UpdateAuthorityCertInCertificateStore(certificateStoreIdentifier).ConfigureAwait(false);

// Update TrustedIssuerCertificates Store
if (IssuerCertificatesStore != null)
{
await UpdateAuthorityCertInCertificateStore(IssuerCertificatesStore).ConfigureAwait(false);
}
return Certificate;
}

certificate?.Dispose();

return Certificate;

}

#endregion
Expand Down Expand Up @@ -470,6 +515,46 @@ public static async Task<X509CRL> RevokeCertificateAsync(
#endregion

#region Private Methods
#if ECC_SUPPORT
/// <summary>
/// GetTheEccCurve of the CertificateGroups CertificateType
/// </summary>
/// <param name="curve"></param>
/// <returns>returns false if RSA CertificateType, true if a ECCurve can be found, else throws Exception</returns>
/// <exception cref="ServiceResultException"></exception>
private bool TryGetECCCurve(out ECCurve curve)
{
curve = default;
if (IsRSACertificateType())
{
return false;
}
ECCurve? tempCurve = EccUtils.GetCurveFromCertificateTypeId(CertificateType);

Check warning on line 532 in Libraries/Opc.Ua.Gds.Server.Common/CertificateGroup.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/CertificateGroup.cs#L532

Added line #L532 was not covered by tests

if (tempCurve == null)
{
throw new ServiceResultException(StatusCodes.BadNotSupported, $"The certificate type {CertificateType} is not supported.");

Check warning on line 536 in Libraries/Opc.Ua.Gds.Server.Common/CertificateGroup.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/CertificateGroup.cs#L536

Added line #L536 was not covered by tests
}

curve = tempCurve.Value;

Check warning on line 539 in Libraries/Opc.Ua.Gds.Server.Common/CertificateGroup.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/CertificateGroup.cs#L539

Added line #L539 was not covered by tests

return true;

Check warning on line 541 in Libraries/Opc.Ua.Gds.Server.Common/CertificateGroup.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Gds.Server.Common/CertificateGroup.cs#L541

Added line #L541 was not covered by tests
}
#endif
/// <summary>
/// Checks if the Certificate Group is for RSA Certificates
/// </summary>
/// <returns>True if the CertificateType of the Certificate Group is an RSA Certificate Type</returns>
private bool IsRSACertificateType()
{
return CertificateType == null ||
CertificateType == Opc.Ua.ObjectTypeIds.ApplicationCertificateType ||
CertificateType == Opc.Ua.ObjectTypeIds.HttpsCertificateType ||
CertificateType == Opc.Ua.ObjectTypeIds.UserCredentialCertificateType ||
CertificateType == Opc.Ua.ObjectTypeIds.RsaMinApplicationCertificateType ||
CertificateType == Opc.Ua.ObjectTypeIds.RsaSha256ApplicationCertificateType;
}

/// <summary>
/// Updates the certificate authority certificate and CRL in the provided CertificateStore
/// </summary>
Expand Down
11 changes: 11 additions & 0 deletions Libraries/Opc.Ua.Gds.Server.Common/Opc.Ua.Gds.Server.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@
<DefineConstants>$(DefineConstants);SIGNASSEMBLY</DefineConstants>
</PropertyGroup>

<!-- select ECC support -->
<Choose>
<When Condition="'$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'netstandard2.0'">
</When>
<Otherwise>
<PropertyGroup>
<DefineConstants>$(DefineConstants);ECC_SUPPORT</DefineConstants>
</PropertyGroup>
</Otherwise>
</Choose>

<ItemGroup>
<EmbeddedResource Include="Model\Opc.Ua.Gds.PredefinedNodes.uanodes" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ ApplicationConfiguration configuration
defaultApplicationGroup.CertificateTypes = defaultApplicationGroup.CertificateTypes.Concat(new NodeId[] { cert.CertificateType }).ToArray();
defaultApplicationGroup.ApplicationCertificates.Add(cert);

if(cert.CertificateType == ObjectTypeIds.HttpsCertificateType)
if (cert.CertificateType == ObjectTypeIds.HttpsCertificateType)
{
defaultHttpsGroup.CertificateTypes = defaultHttpsGroup.CertificateTypes.Concat(new NodeId[] { cert.CertificateType }).ToArray();
defaultHttpsGroup.ApplicationCertificates.Add(cert);

Check warning on line 120 in Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs#L119-L120

Added lines #L119 - L120 were not covered by tests
Expand Down

0 comments on commit 780c78f

Please sign in to comment.