Skip to content

Commit

Permalink
[Test] Add ECC Certificates to various tests (#2961)
Browse files Browse the repository at this point in the history
* Generate ECC Certificates in Client Tests

* Add more tests

* reenable check of certificate

* fix tests

* Dont use random in test

* fix unsupported certificate types

* fix Hash algorithm for nistP384 curve on linux as the friendly name is ECDSA_P384 instead of nistP384

* use friendly name by default and add special case only for linux while taking care of nullability

* disable special casing for finding correct certificates

* fix tests on net 462

* update Certificate Identifier
  • Loading branch information
romanett authored Feb 11, 2025
1 parent cf82847 commit 7cbeee7
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 94 deletions.
8 changes: 6 additions & 2 deletions Libraries/Opc.Ua.Gds.Server.Common/CertificateGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ [Optional] string trustedIssuerCertificatesStorePath
{
if (!Utils.IsSupportedCertificateType(certificateType))
{
throw new NotImplementedException($"Unsupported certificate type {certificateType}");
Utils.LogError("Certificate type {0} specified for Certificate Group is not supported on this platform", certificateType);
continue;
}

CertificateTypes.Add(certificateType);
Expand Down Expand Up @@ -356,7 +357,10 @@ public virtual async Task<X509Certificate2> SigningRequestAsync(

#if ECC_SUPPORT
certificate = TryGetECCCurve(certificateType, out ECCurve curve) ?
builder.SetIssuer(signingKey).SetECDsaPublicKey(info.SubjectPublicKeyInfo.GetEncoded()).CreateForECDsa() :
builder
.SetIssuer(signingKey)
.SetECDsaPublicKey(info.SubjectPublicKeyInfo.GetEncoded())
.CreateForECDsa() :
builder.SetHashAlgorithm(X509Utils.GetRSAHashAlgorithmName(Configuration.DefaultCertificateHashSize))
.SetIssuer(signingKey)
.SetRSAPublicKey(info.SubjectPublicKeyInfo.GetEncoded())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,9 @@ public virtual ICertificateBuilderIssuer SetIssuer(X509Certificate2 issuerCertif
private void SetHashAlgorithmSize(ECCurve curve)
{
if (curve.Oid.FriendlyName.CompareTo(ECCurve.NamedCurves.nistP384.Oid.FriendlyName) == 0 ||
(curve.Oid.FriendlyName.CompareTo(ECCurve.NamedCurves.brainpoolP384r1.Oid.FriendlyName) == 0))
curve.Oid.FriendlyName.CompareTo(ECCurve.NamedCurves.brainpoolP384r1.Oid.FriendlyName) == 0 ||
// special case for linux where friendly name could be ECDSA_P384 instead of nistP384
(curve.Oid?.Value != null && curve.Oid.Value.CompareTo(ECCurve.NamedCurves.nistP384.Oid.Value) == 0))
{
SetHashAlgorithm(HashAlgorithmName.SHA384);
}
Expand Down Expand Up @@ -388,7 +390,7 @@ protected virtual void NewSerialNumber()
/// </summary>
private protected ECCurve? m_curve;
#endif
#endregion
#endregion

#region Private Fields
private X509Certificate2 m_issuerCAKeyCert;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ private ServiceResult UpdateCertificate(
// identify the existing certificate to be updated
// it should be of the same type and same subject name as the new certificate
CertificateIdentifier existingCertIdentifier = certificateGroup.ApplicationCertificates.FirstOrDefault(cert =>
X509Utils.CompareDistinguishedName(cert.Certificate.Subject, newCert.Subject) &&
X509Utils.CompareDistinguishedName(cert.SubjectName, newCert.Subject) &&
cert.CertificateType == certificateTypeId);

// if no cert was found search by ApplicationUri
Expand Down Expand Up @@ -566,6 +566,8 @@ private ServiceResult UpdateCertificate(
var certOnly = X509CertificateLoader.LoadCertificate(updateCertificate.CertificateWithPrivateKey.RawData);
updateCertificate.CertificateWithPrivateKey.Dispose();
updateCertificate.CertificateWithPrivateKey = certOnly;
//update certificate identifier with new certificate
existingCertIdentifier.Find(m_configuration.ApplicationUri).GetAwaiter().GetResult();
}

ICertificateStore issuerStore = certificateGroup.IssuerStore.OpenStore();
Expand Down Expand Up @@ -800,7 +802,7 @@ private ServiceResult GetCertificates(
}

certificateTypeIds = certificateGroup.CertificateTypes;
certificates = certificateGroup.ApplicationCertificates.Select(s => s.Certificate.RawData).ToArray();
certificates = certificateGroup.ApplicationCertificates.Select(s => s.Certificate?.RawData).ToArray();

return ServiceResult.Good;
}
Expand Down
31 changes: 19 additions & 12 deletions Stack/Opc.Ua.Core/Security/Certificates/CertificateIdentifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,10 @@ public static NodeId GetCertificateType(X509Certificate2 certificate)
/// <param name="certificateType">The NodeId of the certificate type.</param>
public static bool ValidateCertificateType(X509Certificate2 certificate, NodeId certificateType)
{
if (certificateType == null)
{
return true;
}
switch (certificate.SignatureAlgorithm.Value)
{
case Oids.ECDsaWithSha1:
Expand All @@ -594,18 +598,21 @@ public static bool ValidateCertificateType(X509Certificate2 certificate, NodeId
}


// special cases
if (certType == ObjectTypeIds.EccNistP384ApplicationCertificateType &&
certificateType == ObjectTypeIds.EccNistP256ApplicationCertificateType)
{
return true;
}

if (certType == ObjectTypeIds.EccBrainpoolP384r1ApplicationCertificateType &&
certificateType == ObjectTypeIds.EccBrainpoolP256r1ApplicationCertificateType)
{
return true;
}
// not needed: An end entity Certificate shall use P-256.
// http://opcfoundation.org/UA/SecurityPolicy#ECC_nistP256
//if (certType == ObjectTypeIds.EccNistP384ApplicationCertificateType &&
// certificateType == ObjectTypeIds.EccNistP256ApplicationCertificateType)
//{
// return true;
//}

// not needed: An end entity Certificate shall use P256r1.
// http://opcfoundation.org/UA/SecurityPolicy#ECC_brainpoolP256r1
//if (certType == ObjectTypeIds.EccBrainpoolP384r1ApplicationCertificateType &&
// certificateType == ObjectTypeIds.EccBrainpoolP256r1ApplicationCertificateType)
//{
// return true;
//}

break;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,9 @@ public virtual async Task UpdateCertificateAsync(SecurityConfiguration securityC

try
{

m_applicationCertificates.Clear();
foreach (var applicationCertificate in securityConfiguration.ApplicationCertificates)
{
m_applicationCertificates.RemoveAll(cert => Utils.IsEqual(cert.RawData, applicationCertificate.RawData));
applicationCertificate.DisposeCertificate();
}

Expand Down
16 changes: 15 additions & 1 deletion Tests/Opc.Ua.Gds.Tests/ClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using Opc.Ua.Gds.Server;
using Assert = NUnit.Framework.Legacy.ClassicAssert;


Expand Down Expand Up @@ -108,6 +109,14 @@ protected async Task OneTimeSetUp()
m_invalidRegistrationOk = false;
m_goodNewKeyPairRequestOk = false;
m_gdsRegisteredTestClient = false;

//get supported CertificateTypes from GDS
m_supportedCertificateTypes = m_server.Config.ParseExtension<GlobalDiscoveryServerConfiguration>().CertificateGroups
.Where(cg => cg.Id == "Default")
.SelectMany(cg => cg.CertificateTypes)
.Select(s => typeof(Ua.ObjectTypeIds).GetField(s).GetValue(null) as NodeId)
.Where(n => n != null && Utils.IsSupportedCertificateType(n))
.ToList();
}

/// <summary>
Expand Down Expand Up @@ -652,8 +661,12 @@ public void StartGoodNewKeyPairRequests()
{
AssertIgnoreTestWithoutGoodRegistration();
ConnectGDS(true);
int certificateTypeIndex = 0;
foreach (var application in m_goodApplicationTestSet)
{
application.CertificateTypeId = m_supportedCertificateTypes[certificateTypeIndex];
certificateTypeIndex = (certificateTypeIndex + 1) % m_supportedCertificateTypes.Count;

Assert.Null(application.CertificateRequestId);
NodeId requestId = m_gdsClient.GDSClient.StartNewKeyPairRequest(
application.ApplicationRecord.ApplicationId,
Expand Down Expand Up @@ -875,7 +888,7 @@ out byte[][] issuerCertificates

}


[Test, Order(540)]
public void GetGoodCertificates()
{
Expand Down Expand Up @@ -1531,6 +1544,7 @@ private int GoodServersOnNetworkCount()
private bool m_goodRegistrationOk;
private bool m_invalidRegistrationOk;
private bool m_gdsRegisteredTestClient;
private List<NodeId> m_supportedCertificateTypes;
private bool m_goodNewKeyPairRequestOk;
private string m_storeType;
#endregion
Expand Down
8 changes: 7 additions & 1 deletion Tests/Opc.Ua.Gds.Tests/GlobalDiscoveryTestServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,13 @@ private static async Task<ApplicationConfiguration> Load(ApplicationInstance app
{
new CertificateGroupConfiguration() {
Id = "Default",
CertificateType = "RsaSha256ApplicationCertificateType",
CertificateTypes = new StringCollection() {
"RsaSha256ApplicationCertificateType",
"EccNistP256ApplicationCertificateType",
"EccNistP384ApplicationCertificateType",
"EccBrainpoolP256r1ApplicationCertificateType",
"EccBrainpoolP384r1ApplicationCertificateType"
},
SubjectName = "CN=GDS Test CA, O=OPC Foundation",
BaseStorePath = Path.Combine(gdsRoot, "CA", "default"),
DefaultCertificateHashSize = 256,
Expand Down
26 changes: 26 additions & 0 deletions Tests/Opc.Ua.Gds.Tests/Opc.Ua.GlobalDiscoveryTestServer.Config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,30 @@
<SubjectName>CN=Global Discovery Test Server, O=OPC Foundation, DC=localhost</SubjectName>
<CertificateTypeString>RsaSha256</CertificateTypeString>
</CertificateIdentifier>
<CertificateIdentifier>
<StoreType>Directory</StoreType>
<StorePath>%LocalApplicationData%/OPC/GDS/own</StorePath>
<SubjectName>CN=Global Discovery Test Server, O=OPC Foundation, DC=localhost</SubjectName>
<CertificateTypeString>NistP256</CertificateTypeString>
</CertificateIdentifier>
<CertificateIdentifier>
<StoreType>Directory</StoreType>
<StorePath>%LocalApplicationData%/OPC/GDS/own</StorePath>
<SubjectName>CN=Global Discovery Test Server, O=OPC Foundation, DC=localhost</SubjectName>
<CertificateTypeString>NistP384</CertificateTypeString>
</CertificateIdentifier>
<CertificateIdentifier>
<StoreType>Directory</StoreType>
<StorePath>%LocalApplicationData%/OPC/GDS/own</StorePath>
<SubjectName>CN=Global Discovery Test Server, O=OPC Foundation, DC=localhost</SubjectName>
<CertificateTypeString>BrainpoolP256r1</CertificateTypeString>
</CertificateIdentifier>
<CertificateIdentifier>
<StoreType>Directory</StoreType>
<StorePath>%LocalApplicationData%/OPC/GDS/own</StorePath>
<SubjectName>CN=Global Discovery Test Server, O=OPC Foundation, DC=localhost</SubjectName>
<CertificateTypeString>BrainpoolP384r1</CertificateTypeString>
</CertificateIdentifier>
</ApplicationCertificates>

<TrustedIssuerCertificates>
Expand Down Expand Up @@ -143,6 +167,8 @@
<ua:String>RsaSha256ApplicationCertificateType</ua:String>
<ua:String>EccNistP256ApplicationCertificateType</ua:String>
<ua:String>EccNistP384ApplicationCertificateType</ua:String>
<ua:String>EccBrainpoolP256r1ApplicationCertificateType</ua:String>
<ua:String>EccBrainpoolP384r1ApplicationCertificateType</ua:String>
</CertificateTypes>
<SubjectName>CN=GDS Test CA, O=OPC Foundation</SubjectName>
<BaseStorePath>%LocalApplicationData%/OPC/GDS/CA/default</BaseStorePath>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,30 @@
<SubjectName>CN=Global Discovery Test Server, O=OPC Foundation, DC=localhost</SubjectName>
<CertificateTypeString>RsaSha256</CertificateTypeString>
</CertificateIdentifier>
<CertificateIdentifier>
<StoreType>X509Store</StoreType>
<StorePath>CurrentUser\UA_Test_GDS_Server_own</StorePath>
<SubjectName>CN=Global Discovery Test Server, O=OPC Foundation, DC=localhost</SubjectName>
<CertificateTypeString>NistP256</CertificateTypeString>
</CertificateIdentifier>
<CertificateIdentifier>
<StoreType>X509Store</StoreType>
<StorePath>CurrentUser\UA_Test_GDS_Server_own</StorePath>
<SubjectName>CN=Global Discovery Test Server, O=OPC Foundation, DC=localhost</SubjectName>
<CertificateTypeString>NistP384</CertificateTypeString>
</CertificateIdentifier>
<CertificateIdentifier>
<StoreType>X509Store</StoreType>
<StorePath>CurrentUser\UA_Test_GDS_Server_own</StorePath>
<SubjectName>CN=Global Discovery Test Server, O=OPC Foundation, DC=localhost</SubjectName>
<CertificateTypeString>BrainpoolP256r1</CertificateTypeString>
</CertificateIdentifier>
<CertificateIdentifier>
<StoreType>X509Store</StoreType>
<StorePath>CurrentUser\UA_Test_GDS_Server_own</StorePath>
<SubjectName>CN=Global Discovery Test Server, O=OPC Foundation, DC=localhost</SubjectName>
<CertificateTypeString>BrainpoolP384r1</CertificateTypeString>
</CertificateIdentifier>
</ApplicationCertificates>

<TrustedIssuerCertificates>
Expand Down Expand Up @@ -143,6 +167,8 @@
<ua:String>RsaSha256ApplicationCertificateType</ua:String>
<ua:String>EccNistP256ApplicationCertificateType</ua:String>
<ua:String>EccNistP384ApplicationCertificateType</ua:String>
<ua:String>EccBrainpoolP256r1ApplicationCertificateType</ua:String>
<ua:String>EccBrainpoolP384r1ApplicationCertificateType</ua:String>
</CertificateTypes>
<SubjectName>CN=GDS Test CA, O=OPC Foundation</SubjectName>
<BaseStorePath>%LocalApplicationData%/OPC/GDS/CA/default</BaseStorePath>
Expand Down
Loading

0 comments on commit 7cbeee7

Please sign in to comment.