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

Extend ServerConfiguration & GDS Server for ECC #2817

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
40 changes: 7 additions & 33 deletions Libraries/Opc.Ua.Configuration/ApplicationInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -908,44 +908,18 @@
#if !ECC_SUPPORT
throw new ServiceResultException(StatusCodes.BadConfigurationError, "The Ecc certificate type is not supported.");
#else
ECCurve curve = default(ECCurve);
if (id.CertificateType == ObjectTypeIds.EccApplicationCertificateType ||
id.CertificateType == ObjectTypeIds.EccNistP256ApplicationCertificateType)
{
curve = ECCurve.NamedCurves.nistP256;
}
else if (id.CertificateType == ObjectTypeIds.EccNistP384ApplicationCertificateType)
{
curve = ECCurve.NamedCurves.nistP384;
}
else if (id.CertificateType == ObjectTypeIds.EccBrainpoolP256r1ApplicationCertificateType)
{
curve = ECCurve.NamedCurves.brainpoolP256r1;
}
else if (id.CertificateType == ObjectTypeIds.EccBrainpoolP384r1ApplicationCertificateType)
{
curve = ECCurve.NamedCurves.brainpoolP384r1;
}
#if CURVE25519
else if (id.CertificateType == ObjectTypeIds.EccCurve25519ApplicationCertificateType)
{
curve = default(ECCurve);
}
else if (id.CertificateType == ObjectTypeIds.EccCurve448ApplicationCertificateType)
{
curve = default(ECCurve);
}
#endif
else
ECCurve? curve = EccUtils.GetCurveFromCertificateTypeId(id.CertificateType);

if(curve == null)
{
throw new ServiceResultException(StatusCodes.BadConfigurationError, "The ECC certificate type is not supported.");
throw new ServiceResultException(StatusCodes.BadConfigurationError, "The Ecc certificate type is not supported.");

Check warning on line 915 in Libraries/Opc.Ua.Configuration/ApplicationInstance.cs

View check run for this annotation

Codecov / codecov/patch

Libraries/Opc.Ua.Configuration/ApplicationInstance.cs#L915

Added line #L915 was not covered by tests
}

id.Certificate = builder
.SetECCurve(curve)
.SetECCurve(curve.Value)
.CreateForECDsa();

Utils.LogCertificate("Certificate created for {0}.", id.Certificate, curve.Oid.FriendlyName);
Utils.LogCertificate("Certificate created for {0}.", id.Certificate, curve.Value.Oid.FriendlyName);
#endif
}

Expand Down Expand Up @@ -1160,7 +1134,7 @@
return false;
}
}
#endregion
#endregion

#region Private Fields
private string m_applicationName;
Expand Down
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 @@
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 @@
{
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 @@
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 @@
}

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 @@
#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
Loading
Loading