Skip to content

Commit 7bbe95d

Browse files
arbuusmrts
authored andcommitted
Adds support of organisation certificate
Added organisation certificate data extraction and appropriate tests. Signed-off-by: Erkki Arus <[email protected]>
1 parent f494b36 commit 7bbe95d

File tree

5 files changed

+116
-10
lines changed

5 files changed

+116
-10
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
namespace WebEid.Security.Tests.Certificate
2+
{
3+
using System.Security.Cryptography.X509Certificates;
4+
using NUnit.Framework;
5+
using WebEid.Security.Exceptions;
6+
using WebEid.Security.Tests.TestUtils;
7+
using WebEid.Security.Util;
8+
9+
public class CertificateTests
10+
{
11+
[Test]
12+
public void WhenOrganizationCertificateThenSubjectCNAndIdCodeAndCountryCodeExtractionSucceeds()
13+
{
14+
X509Certificate organizationCert = Certificates.GetOrganizationCert();
15+
Assert.That(organizationCert.GetSubjectCn(), Is.EqualTo("Testijad.ee isikutuvastus"));
16+
Assert.That(organizationCert.GetSubjectIdCode(), Is.EqualTo("12276279"));
17+
Assert.That(organizationCert.GetSubjectCountryCode(), Is.EqualTo("EE"));
18+
}
19+
20+
[Test]
21+
public void WhenOrganizationCertificateThenSubjectGivenNameAndSurnameExtractionFails()
22+
{
23+
X509Certificate organizationCert = Certificates.GetOrganizationCert();
24+
Assert.Throws<CertificateEncodingException>(() => organizationCert.GetSubjectGivenName());
25+
Assert.Throws<CertificateEncodingException>(() => organizationCert.GetSubjectSurname());
26+
}
27+
}
28+
}

src/WebEid.Security.Tests/TestUtils/Certificates.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ internal static class Certificates
3232

3333
private const string JaakKristjanEsteid2018Cert = "MIIEAzCCA2WgAwIBAgIQOWkBWXNDJm1byFd3XsWkvjAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTE4MTAxODA5NTA0N1oXDTIzMTAxNzIxNTk1OVowfzELMAkGA1UEBhMCRUUxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEQMA4GA1UEBAwHSsOVRU9SRzEWMBQGA1UEKgwNSkFBSy1LUklTVEpBTjEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAR5k1lXzvSeI9O/1s1pZvjhEW8nItJoG0EBFxmLEY6S7ki1vF2Q3TEDx6dNztI1Xtx96cs8r4zYTwdiQoDg7k3diUuR9nTWGxQEMO1FDo4Y9fAmiPGWT++GuOVoZQY3XxijggHDMIIBvzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIDiDBHBgNVHSAEQDA+MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAIBgYEAI96AQIwHwYDVR0RBBgwFoEUMzgwMDEwODU3MThAZWVzdGkuZWUwHQYDVR0OBBYEFOQsvTQJEBVMMSmhyZX5bibYJubAMGEGCCsGAQUFBwEDBFUwUzBRBgYEAI5GAQUwRzBFFj9odHRwczovL3NrLmVlL2VuL3JlcG9zaXRvcnkvY29uZGl0aW9ucy1mb3ItdXNlLW9mLWNlcnRpZmljYXRlcy8TAkVOMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBiwAwgYcCQgH1UsmMdtLZti51Fq2QR4wUkAwpsnhsBV2HQqUXFYBJ7EXnLCkaXjdZKkHpABfM0QEx7UUhaI4i53jiJ7E1Y7WOAAJBDX4z61pniHJapI1bkMIiJQ/ti7ha8fdJSMSpAds5CyHIyHkQzWlVy86f9mA7Eu3oRO/1q+eFUzDbNN3Vvy7gQWQ=";
3434
private const string MariliisEsteid2015Cert = "MIIFwjCCA6qgAwIBAgIQY+LgQ6n0BURZ048wIEiYHjANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEXMBUGA1UEYQwOTlRSRUUtMTA3NDcwMTMxHzAdBgNVBAMMFlRFU1Qgb2YgRVNURUlELVNLIDIwMTUwHhcNMTcxMDAzMTMyMjU2WhcNMjIxMDAyMjA1OTU5WjCBnjELMAkGA1UEBhMCRUUxDzANBgNVBAoMBkVTVEVJRDEaMBgGA1UECwwRZGlnaXRhbCBzaWduYXR1cmUxJjAkBgNVBAMMHU3DhE5OSUssTUFSSS1MSUlTLDYxNzEwMDMwMTYzMRAwDgYDVQQEDAdNw4ROTklLMRIwEAYDVQQqDAlNQVJJLUxJSVMxFDASBgNVBAUTCzYxNzEwMDMwMTYzMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE+nNdtmZ2Ve3XXtjBEGwpvVrDIg7slPfLlyHbCBFMXevfqW5KsXIOy6E2A+Yof+/cqRlY4IhsX2Ka9SsJSo8/EekasFasLFPw9ZBE3MG0nn5zaatg45VSjnPinMmrzFzxo4IB2jCCAdYwCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCBkAwgYsGA1UdIASBgzCBgDBzBgkrBgEEAc4fAwEwZjAvBggrBgEFBQcCARYjaHR0cHM6Ly93d3cuc2suZWUvcmVwb3NpdG9vcml1bS9DUFMwMwYIKwYBBQUHAgIwJwwlQWludWx0IHRlc3RpbWlzZWtzLiBPbmx5IGZvciB0ZXN0aW5nLjAJBgcEAIvsQAECMB0GA1UdDgQWBBTiw6M0uow+u6sfhgJAWCSvtkB/ejAiBggrBgEFBQcBAwQWMBQwCAYGBACORgEBMAgGBgQAjkYBBDAfBgNVHSMEGDAWgBRJwPJEOWXVm0Y7DThgg7HWLSiGpjCBgwYIKwYBBQUHAQEEdzB1MCwGCCsGAQUFBzABhiBodHRwOi8vYWlhLmRlbW8uc2suZWUvZXN0ZWlkMjAxNTBFBggrBgEFBQcwAoY5aHR0cHM6Ly9zay5lZS91cGxvYWQvZmlsZXMvVEVTVF9vZl9FU1RFSUQtU0tfMjAxNS5kZXIuY3J0MEEGA1UdHwQ6MDgwNqA0oDKGMGh0dHA6Ly93d3cuc2suZWUvY3Jscy9lc3RlaWQvdGVzdF9lc3RlaWQyMDE1LmNybDANBgkqhkiG9w0BAQsFAAOCAgEAEWBdwmzo/yRncJXKvrE+A1G6yQaBNarKectI5uk18BewYEA4QkhmIwOCwD83jBDB9JF+kuODMHsnvz2mfhwaB/uJIPwfBDQ5JCMBdHPsxLN9nzW/UUzqv2UDMwFkibHCcfV5lTBcmOd7FagUHTUm+8gRlWbDiVl5yPochdJgGYPV+fs/jc5ttHaBvBon0z9LbI4qi0VXdRmV0iogErh8JF5yfGkbfGRaMkWkNYQtQ68i/hPe6MaUxL2/MMt4YTyXtVghmc3ZKZIyp4j0+jlK4vL+d4gaE+TvoQvh6HrmP145FqlMDurATWdB069+hdDLO5fI6AYkc79D5XPKwQ/f1MBufLtBYtOJmtpLT+tdBt/EqOEIO/0FeHcXZlFioNMuxBBeTE/QcDtJ2jxTcg8jNOoepS0wjuxBon9iI1710SR53DLGSWdL52lPoBFacnyPQI1htXVUkJ8icMQKYe3BLt1Ha2cvsA4n4IpjqVROX4mzoPL1hg/aJlD+W2uI2ppYRUNY5FX7C0R+AYzMpOahQ7STQfUxtEnKW98e1I33LWwpjJW9q4htsZeXs4Zatf9ssfUW0VA49tnI28kkN2D8aw1NgWfzVlnJKkEj0qa3ewLZK577j8MexAetT/7leH6mqewr9ewC/tKbYjhufieXx6RPcRC4OZsxtii7ih8TqRg=";
35+
private const string OrganizationCert = "MIIF2zCCA8OgAwIBAgIQJs4xyGoNzixjYmV9gUjYljANBgkqhkiG9w0BAQsFADCBjjELMAkGA1UEBhMCRUUxIjAgBgNVBAoMGUFTIFNlcnRpZml0c2VlcmltaXNrZXNrdXMxITAfBgNVBAsMGFNlcnRpZml0c2VlcmltaXN0ZWVudXNlZDEXMBUGA1UEYQwOTlRSRUUtMTA3NDcwMTMxHzAdBgNVBAMMFlRFU1Qgb2YgS0xBU1MzLVNLIDIwMTYwHhcNMjIxMTAyMTI0MTA0WhcNMjUxMjAxMTI0MTA0WjB7MREwDwYDVQQFEwgxMjI3NjI3OTERMA8GA1UECAwISGFyanVtYWExEDAOBgNVBAcMB1RhbGxpbm4xCzAJBgNVBAYTAkVFMRAwDgYDVQQKDAdUVFQgT8OcMSIwIAYDVQQDDBlUZXN0aWphZC5lZSBpc2lrdXR1dmFzdHVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzSV4zydk5WY2AuUJ50lNpH3q2C+WH0dE/wqq4nFqpNYkyzFNHecFDFlU0YcpPrhFKDZfJtaAP/drvmdqaVdAcCGIPnXhZ+01pCvmlebe7//kQXaZ6ZHS3EAtwy0EBsVVOMapw1kC58YYymlJhTrdzDFrqjdgv1t1Ph9Gkg/PhaHvqGtKp3IY+v33EwxEV3nPIhZHHC/d0YnzVaN5QiSHbU+mRt8+d2vHPNPNY3qVDh8MPOrJIDeIHp9oSS1+FF4crnvfxmg99d7zemsSstR8/SXedYuvWZb6iSybAjhucp21uF0tcqJ2k6+ZH/976AEy0IC8r4tgf7r70hhYu6KOOQIDAQABo4IBRTCCAUEwCQYDVR0TBAIwADBUBgNVHSAETTBLMDIGCysGAQQBzh8HAQIGMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL2NwczAIBgYEAI96AQEwCwYJKwYBBAHOHwkDMBMGA1UdJQQMMAoGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFC4bj7sBLzT42jAEi1zB8lwl49j3MA4GA1UdDwEB/wQEAwIEsDAdBgNVHQ4EFgQUbNSRZSddDUofhxlpoSVEunofez8weQYIKwYBBQUHAQEEbTBrMC0GCCsGAQUFBzABhiFodHRwOi8vYWlhLmRlbW8uc2suZWUva2xhc3MzLTIwMTYwOgYIKwYBBQUHMAKGLmh0dHBzOi8vYy5zay5lZS9URVNUX29mX0tMQVNTMy1TS18yMDE2LmRlci5jcnQwDQYJKoZIhvcNAQELBQADggIBAE8Z/GIEfPWGMe1fHYqCQ2v3zSOuIzyeEId595wrknl7IcLY8ogG10oDUw6rDWQ6jMBS5PINUG+WpH6Wo8qxkPY5Dz4WQvBB2qnuJTH3Bvm/PFpsD1Jk7dOF35P4kfX63NnsCkccRxwlhjFE56WdxDOwhC+neF5FP4hvYvbIIK73DVxRg6yBe4i/Y/g5MOXKrzpHvRzMTURqR3lF0dAgIwMNluik4so/B2DIXMYHi6jZVJlwdQriyL7HI4/Ub3QwyTrbfJtXkwWINsMaCFG+Ccjae3TVRFDJvIIE/gQd4wEh+PK0RJBYfOnAypFEKyH+giID7LIAnO90MY6mNl1QSLQWrdlqMxv+fDdEi/JwGLZyHzEOxKs9C4S8zngwCiDFBHMtJcL9A1vq512yBz5aXYwlqcmjcQDegLT6s6otu+AXO8ZOdqsA+/ak7BEl0FUWlsc8yLKa4cuLiV68iArfl+VFVIZ+jgdMplwUuf5c2QN5f0gPZZxkiAXQ8D8qssW1yI+dLCuPXPwyMENGxWTzyodcSdkpZsdIyOg7/o+WK3RczvMjjT8X8F4XKo8JPjZBYyGBx5XkqhwVrX3SjEmRPFdcvy+glYRoTslgM2fsj5fSNxCIsq1fQN8yVjYnxk8/X53AsorcpWpLMHxtoxT+YvNZzryY00QjS5kgUQBNmFaU";
3536

3637
private static X509Certificate testEsteid2018Ca;
3738
private static X509Certificate testEsteid2015Ca;
3839

3940
private static X509Certificate2 jaakKristjanEsteid2018Cert;
4041
private static X509Certificate mariliisEsteid2015Cert;
4142
private static X509Certificate testSkOcspResponder2020;
43+
private static X509Certificate organizationCert;
4244

4345
private static void LoadCertificates()
4446
{
@@ -95,5 +97,11 @@ public static X509Certificate GetMariliisEsteid2015Cert()
9597
}
9698
return mariliisEsteid2015Cert;
9799
}
100+
101+
public static X509Certificate GetOrganizationCert()
102+
{
103+
organizationCert ??= CertificateLoader.LoadCertificateFromBase64String(OrganizationCert);
104+
return organizationCert;
105+
}
98106
}
99107
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
namespace WebEid.Security.Exceptions
2+
{
3+
using System;
4+
using System.Runtime.Serialization;
5+
6+
/// <summary>
7+
///
8+
/// </summary>
9+
[Serializable]
10+
public class CertificateEncodingException : AuthTokenException
11+
{
12+
/// <summary>
13+
/// Object identifier that was not found
14+
/// </summary>
15+
public string OID { get; }
16+
17+
/// <summary>
18+
/// Default constructor.
19+
/// </summary>
20+
/// <remarks>
21+
/// Constructs the instance of the class and sets the message of the exception to
22+
/// "X500 name RDNs empty or first element is null".
23+
/// </remarks>
24+
public CertificateEncodingException() : base("X500 name RDNs empty or first element is null")
25+
{
26+
OID = String.Empty;
27+
}
28+
29+
/// <summary>
30+
/// Constructs the instance from given Object Identifier that was not found.
31+
/// </summary>
32+
/// <param name="oid">The Object Identifier that was not found.</param>
33+
public CertificateEncodingException(string oid) : base($"Object Identifier {oid} was not found")
34+
{
35+
OID = oid;
36+
}
37+
38+
39+
/// <summary>
40+
/// Constructs the instance from given Object Identifier, message and existing <see cref="Exception"/> object.
41+
/// </summary>
42+
/// <param name="oid">The Object Identifier that was not found.</param>
43+
/// <param name="msg">Message of the exception.</param>
44+
/// <param name="innerException">The exception that is the cause of the current exception.</param>
45+
public CertificateEncodingException(string oid, string msg, Exception innerException) : base(msg, innerException)
46+
{
47+
OID = oid;
48+
}
49+
50+
/// <summary>
51+
/// Constructs the instance with serialized data.
52+
/// </summary>
53+
/// <param name="info">The <see cref="SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
54+
/// <param name="context">The <see cref="StreamingContext"/> that contains contextual information about the source or destination.</param>
55+
protected CertificateEncodingException(SerializationInfo info, StreamingContext context) : base(info, context)
56+
{
57+
}
58+
}
59+
}

src/WebEid.Security/Util/X509CertificateExtensions.cs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ namespace WebEid.Security.Util
3232
using Org.BouncyCastle.Asn1;
3333
using Org.BouncyCastle.Asn1.X509;
3434
using Org.BouncyCastle.Security;
35+
using Org.BouncyCastle.Security.Certificates;
3536

3637
public static class X509CertificateExtensions
3738
{
@@ -58,11 +59,11 @@ public static void ValidateCertificateExpiry(this Org.BouncyCastle.X509.X509Cert
5859
}
5960
catch (Org.BouncyCastle.Security.Certificates.CertificateNotYetValidException e)
6061
{
61-
throw new CertificateNotYetValidException(subject, e);
62+
throw new Exceptions.CertificateNotYetValidException(subject, e);
6263
}
6364
catch (Org.BouncyCastle.Security.Certificates.CertificateExpiredException e)
6465
{
65-
throw new CertificateExpiredException(subject, e);
66+
throw new Exceptions.CertificateExpiredException(subject, e);
6667
}
6768
}
6869

@@ -132,24 +133,32 @@ public static X509Certificate2 ParseCertificate(string certificateInBase64, stri
132133
}
133134

134135
public static string GetSubjectCn(this X509Certificate certificate) =>
135-
certificate.GetSubjectCnFieldValue(X509Name.CN);
136+
certificate.GetSubjectFieldValue(X509Name.CN);
136137

137138
public static string GetSubjectIdCode(this X509Certificate certificate) =>
138-
certificate.GetSubjectCnFieldValue(X509Name.SerialNumber);
139+
certificate.GetSubjectFieldValue(X509Name.SerialNumber);
139140

140141
public static string GetSubjectGivenName(this X509Certificate certificate) =>
141-
certificate.GetSubjectCnFieldValue(X509Name.GivenName);
142+
certificate.GetSubjectFieldValue(X509Name.GivenName);
142143

143144
public static string GetSubjectSurname(this X509Certificate certificate) =>
144-
certificate.GetSubjectCnFieldValue(X509Name.Surname);
145+
certificate.GetSubjectFieldValue(X509Name.Surname);
145146

146147
public static string GetSubjectCountryCode(this X509Certificate certificate) =>
147-
certificate.GetSubjectCnFieldValue(X509Name.C);
148+
certificate.GetSubjectFieldValue(X509Name.C);
148149

149-
private static string GetSubjectCnFieldValue(this X509Certificate certificate, DerObjectIdentifier oid)
150+
private static string GetSubjectFieldValue(this X509Certificate certificate, DerObjectIdentifier oid)
150151
{
151152
var bcCertificate = DotNetUtilities.FromX509Certificate(certificate);
152-
return bcCertificate.SubjectDN.GetValueList(oid)[0].ToString();
153+
var valueList = bcCertificate.SubjectDN.GetValueList(oid);
154+
if (valueList.Count == 0 || valueList[0] is null)
155+
{
156+
throw new Exceptions.CertificateEncodingException(oid.ToString());
157+
}
158+
else
159+
{
160+
return valueList[0].ToString();
161+
}
153162
}
154163

155164
public static AsymmetricAlgorithm GetAsymmetricPublicKey(this X509Certificate2 certificate2) =>

src/WebEid.Security/WebEid.Security.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
<TargetFramework>netstandard2.1</TargetFramework>
88
<RootNamespace>WebEid.Security</RootNamespace>
99
<AssemblyName>WebEid.Security</AssemblyName>
10+
<GenerateDocumentationFile>True</GenerateDocumentationFile>
1011
<PublisherName>RIA</PublisherName>
1112
<Authors>RIA</Authors>
12-
<Copyright>RIA</Copyright>
13+
<Copyright>Copyright © Republic of Estonia Information System Authority 2023</Copyright>
14+
<Version>1.1.1</Version>
1315
</PropertyGroup>
1416

1517
<ItemGroup>

0 commit comments

Comments
 (0)