diff --git a/Libraries/Opc.Ua.Gds.Server.Common/ApplicationsNodeManager.cs b/Libraries/Opc.Ua.Gds.Server.Common/ApplicationsNodeManager.cs index bd3ca1332..6227c1b4f 100644 --- a/Libraries/Opc.Ua.Gds.Server.Common/ApplicationsNodeManager.cs +++ b/Libraries/Opc.Ua.Gds.Server.Common/ApplicationsNodeManager.cs @@ -346,7 +346,19 @@ public override void CreateAddressSpace(IDictionary> 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 }; } @@ -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; @@ -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); @@ -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) @@ -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; diff --git a/Libraries/Opc.Ua.Gds.Server.Common/CertificateGroup.cs b/Libraries/Opc.Ua.Gds.Server.Common/CertificateGroup.cs index 1f1b6553c..1557d95eb 100644 --- a/Libraries/Opc.Ua.Gds.Server.Common/CertificateGroup.cs +++ b/Libraries/Opc.Ua.Gds.Server.Common/CertificateGroup.cs @@ -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 @@ -152,14 +152,24 @@ public virtual async Task 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") { @@ -173,7 +183,13 @@ public virtual async Task 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); } } @@ -289,14 +305,29 @@ public virtual async Task 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) @@ -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 @@ -470,6 +515,46 @@ public static async Task RevokeCertificateAsync( #endregion #region Private Methods +#if ECC_SUPPORT + /// + /// GetTheEccCurve of the CertificateGroups CertificateType + /// + /// + /// returns false if RSA CertificateType, true if a ECCurve can be found, else throws Exception + /// + private bool TryGetECCCurve(out ECCurve curve) + { + curve = default; + if (IsRSACertificateType()) + { + return false; + } + ECCurve? tempCurve = EccUtils.GetCurveFromCertificateTypeId(CertificateType); + + if (tempCurve == null) + { + throw new ServiceResultException(StatusCodes.BadNotSupported, $"The certificate type {CertificateType} is not supported."); + } + + curve = tempCurve.Value; + + return true; + } +#endif + /// + /// Checks if the Certificate Group is for RSA Certificates + /// + /// True if the CertificateType of the Certificate Group is an RSA Certificate Type + 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; + } + /// /// Updates the certificate authority certificate and CRL in the provided CertificateStore /// diff --git a/Libraries/Opc.Ua.Gds.Server.Common/Opc.Ua.Gds.Server.Common.csproj b/Libraries/Opc.Ua.Gds.Server.Common/Opc.Ua.Gds.Server.Common.csproj index 7750c96a6..2c9586ae6 100644 --- a/Libraries/Opc.Ua.Gds.Server.Common/Opc.Ua.Gds.Server.Common.csproj +++ b/Libraries/Opc.Ua.Gds.Server.Common/Opc.Ua.Gds.Server.Common.csproj @@ -21,6 +21,17 @@ $(DefineConstants);SIGNASSEMBLY + + + + + + + $(DefineConstants);ECC_SUPPORT + + + + diff --git a/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs b/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs index 0c9339ba3..6ccb50f44 100644 --- a/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs +++ b/Libraries/Opc.Ua.Server/Configuration/ConfigurationNodeManager.cs @@ -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);