Skip to content

Commit 9df9fa7

Browse files
committed
Load and verify trustlist.xml from server
1 parent b6117d9 commit 9df9fa7

5 files changed

Lines changed: 558 additions & 439 deletions

File tree

src/AasSecurity/SecuritySettingsForServerParser.cs

Lines changed: 114 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,39 @@
1313

1414
using System;
1515
using System.Buffers.Text;
16+
using System.Net;
17+
using System.Security.Cryptography;
1618
using System.Security.Cryptography.X509Certificates;
19+
using System.Text.Json;
1720
using System.Xml;
1821
using System.Xml.Linq;
1922
using AasSecurity.Models;
2023
using AasxServer;
2124
using AdminShellNS;
2225
using IdentityModel;
26+
using Namotion.Reflection;
2327

2428
namespace AasSecurity
2529
{
2630
internal static class SecuritySettingsForServerParser
27-
{
28-
//private static ILogger _logger = ApplicationLogging.CreateLogger("SecuritySettingsForServerParser");
31+
{
32+
//private static ILogger _logger = ApplicationLogging.CreateLogger("SecuritySettingsForServerParser");
33+
34+
/// <summary>
35+
/// Import the RSA key from a pem file
36+
/// </summary>
37+
/// <param name="pemFile">public key provided as pem file</param>
38+
/// <returns>RSA key object instance</returns>
39+
private static RSA ReadPemFile(string pemFile)
40+
{
41+
string privateKeyPem = System.IO.File.ReadAllText(pemFile);
42+
43+
RSA rsa = RSA.Create();
44+
rsa.ImportFromPem(privateKeyPem.AsSpan());
45+
46+
47+
return rsa;
48+
}
2949

3050
internal static void ParseSecuritySettingsForServer(AdminShellPackageEnv env, ISubmodel submodel)
3151
{
@@ -35,35 +55,35 @@ internal static void ParseSecuritySettingsForServer(AdminShellPackageEnv env, IS
3555
{
3656
switch (submodelElement.IdShort!.ToLower())
3757
{
38-
case "authenticationserver":
39-
{
40-
if (submodelElement is SubmodelElementCollection authServer)
41-
{
42-
ParseAuthenticationServer(env, authServer);
43-
}
44-
}
45-
break;
46-
case "rolemapping":
47-
{
48-
if (submodelElement is SubmodelElementCollection roleMappings)
49-
{
50-
ParseRoleMappings(roleMappings);
51-
}
52-
}
53-
break;
54-
case "basicauth":
55-
{
56-
if (submodelElement is SubmodelElementCollection basicAuth)
57-
{
58-
ParseBasicAuth(basicAuth);
59-
}
60-
break;
61-
}
62-
default:
63-
{
64-
//_logger.LogError($"Unhandled submodel element {submodelElement.IdShort} while parsing SecuritySettingsForServer.");
65-
break;
66-
}
58+
case "authenticationserver":
59+
{
60+
if (submodelElement is SubmodelElementCollection authServer)
61+
{
62+
ParseAuthenticationServer(env, authServer);
63+
}
64+
}
65+
break;
66+
case "rolemapping":
67+
{
68+
if (submodelElement is SubmodelElementCollection roleMappings)
69+
{
70+
ParseRoleMappings(roleMappings);
71+
}
72+
}
73+
break;
74+
case "basicauth":
75+
{
76+
if (submodelElement is SubmodelElementCollection basicAuth)
77+
{
78+
ParseBasicAuth(basicAuth);
79+
}
80+
break;
81+
}
82+
default:
83+
{
84+
//_logger.LogError($"Unhandled submodel element {submodelElement.IdShort} while parsing SecuritySettingsForServer.");
85+
break;
86+
}
6787
}
6888
}
6989
}
@@ -83,8 +103,10 @@ private static void ParseBasicAuth(SubmodelElementCollection basicAuth)
83103
}
84104
}
85105

86-
private static void ParseAuthenticationServer(AdminShellPackageEnv env, SubmodelElementCollection? authServer)
106+
private async static void ParseAuthenticationServer(AdminShellPackageEnv env, SubmodelElementCollection? authServer)
87107
{
108+
var trustListOnServer = System.Environment.GetEnvironmentVariable("TRUST_LIST");
109+
88110
if (System.IO.File.Exists("trustlist.txt"))
89111
{
90112
Console.WriteLine("Read trustlist.txt");
@@ -156,13 +178,69 @@ private static void ParseAuthenticationServer(AdminShellPackageEnv env, Submodel
156178
}
157179
}
158180
}
159-
if (System.IO.File.Exists("trustlist.xml"))
181+
182+
XDocument doc = null;
183+
184+
// Create a new XML document.
185+
XmlDocument xmlDoc = new()
160186
{
161-
Console.WriteLine("Read trustlist.xml");
187+
// Load an XML file into the XmlDocument object.
188+
PreserveWhitespace = true
189+
};
190+
191+
if (!string.IsNullOrEmpty(trustListOnServer))
192+
{
193+
var handlerExchange = new HttpClientHandler { DefaultProxyCredentials = CredentialCache.DefaultCredentials };
194+
using var client = new HttpClient(handlerExchange);
195+
196+
try
197+
{
198+
var response = await client.GetAsync(trustListOnServer);
199+
response.EnsureSuccessStatusCode();
200+
201+
var content = await response.Content.ReadAsStreamAsync();
202+
203+
xmlDoc.Load(content);
204+
205+
content.Position = 0;
206+
207+
doc = XDocument.Load(content);
208+
}
209+
catch (Exception ex)
210+
{
211+
Console.WriteLine($"Error: {ex.Message}");
212+
}
213+
}
162214

215+
var localTrustListPath = "trustlist.xml";
163216

217+
if (doc == null && System.IO.File.Exists(localTrustListPath))
218+
{
164219
// Load the XML
165-
var doc = XDocument.Load("trustlist.xml");
220+
doc = XDocument.Load(localTrustListPath);
221+
xmlDoc.Load(localTrustListPath);
222+
}
223+
224+
var localPemFilePath = "publickey.pem";
225+
226+
if (doc != null)
227+
{
228+
if (System.IO.File.Exists(localPemFilePath))
229+
{
230+
RSA rsa = ReadPemFile(localPemFilePath);
231+
232+
bool sigValidation = TrustListVerifier.VerifyXmlSignature(xmlDoc, rsa);
233+
234+
if (sigValidation)
235+
{
236+
Console.WriteLine("Signature could get validated");
237+
}
238+
else
239+
{
240+
Console.WriteLine("Signature validation failed");
241+
return;
242+
}
243+
}
166244

167245
// Default ETSI namespace present in the document
168246
XNamespace ns = "http://uri.etsi.org/02231/v2#"; // <- critical
@@ -245,6 +323,7 @@ private static void ParseAuthenticationServer(AdminShellPackageEnv env, Submodel
245323
}
246324
}
247325
}
326+
248327
if (authServer == null || authServer.Value == null)
249328
{
250329
return;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
namespace AasSecurity
2+
{
3+
using System;
4+
using System.Security.Cryptography;
5+
using System.Security.Cryptography.Xml;
6+
using System.Xml;
7+
8+
internal class TrustListVerifier
9+
{
10+
// Verify the signature of an XML file against an asymmetric
11+
// algorithm and return the result.
12+
public static bool VerifyXmlSignature(XmlDocument xmlDoc, RSA key)
13+
{
14+
// Check arguments.
15+
if (xmlDoc == null)
16+
throw new ArgumentException(null, nameof(xmlDoc));
17+
if (key == null)
18+
throw new ArgumentException(null, nameof(key));
19+
20+
// Create a new SignedXml object and pass it
21+
// the XML document class.
22+
SignedXml signedXml = new(xmlDoc);
23+
24+
// Find the "Signature" node and create a new
25+
// XmlNodeList object.
26+
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Signature");
27+
28+
// Throw an exception if no signature was found.
29+
if (nodeList.Count <= 0)
30+
{
31+
throw new CryptographicException("Verification failed: No Signature was found in the document.");
32+
}
33+
34+
// This example only supports one signature for
35+
// the entire XML document. Throw an exception
36+
// if more than one signature was found.
37+
if (nodeList.Count >= 2)
38+
{
39+
throw new CryptographicException("Verification failed: More that one signature was found for the document.");
40+
}
41+
42+
// Load the first <signature> node.
43+
signedXml.LoadXml((XmlElement?)nodeList[0]);
44+
45+
// Check the signature and return the result.
46+
var isSigned = signedXml.CheckSignature(key);
47+
return isSigned;
48+
}
49+
}
50+
}

src/AasxServerBlazor/Properties/launchSettings.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"AASX_MQTT": "1",
2222
"TOKENEXCHANGE1": "https://iam-security-training.com/consumer/sts",
2323
"TOKENEXCHANGE2": "https://iam-security-training.com/provider/sts",
24-
"TOKENEXCHANGE_AUDIENCE": "fa3st"
24+
"TOKENEXCHANGE_AUDIENCE": "fa3st",
25+
"TRUST_LIST": "https://iam-security-training.com/trustlist/trustlist.xml"
2526
},
2627
"applicationUrl": "http://localhost:5001",
2728
"jsWebView2Debugging": true
@@ -35,4 +36,4 @@
3536
"sslPort": 0
3637
}
3738
}
38-
}
39+
}

src/AasxServerBlazor/publickey.pem

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0jYueisuycatlWB0Atvn
3+
t/fwQM+u5VnZS2uNVsC1Kij/S87ZdCSH/VqoQUVVhvb+9XqMnaoTTMVr+tjaUcuw
4+
xCl3Yc+3svKLe+PMJH7+bUzu/qHjJVf8vE6emE8XjixvE78egMsmeXHs05EMnnTO
5+
mK3rVdbRpO153u57shYFZ4NN95XApc70BNRGb7AwxL+Iw5aFroofV8SSlNrfUm0j
6+
FSq3TWzVtqEGtjc8/N6/Q7WKzvy/fG3HWxwaqJpLcLnl8jQsBjZc5QhKHdXJf4cs
7+
f7iNZytqQnAK9IhIOX/MOSR8W+j2ward1jyK0yE/bwPHY8jpW676CewTdpdlaqU1
8+
7uIFFD+y5L/PoRnb9EFufstSfNIBNTa6lwk462kmOfb4aTujRSEc+yGlO1z4AReZ
9+
QgubH4aQs7MJZaKGeXjNyr1Yla/9i1M3eMuxFBT4YVizhwV/khi9LpMrmVpj0WMd
10+
p9vosDzTU1gqT4rflUl1JeBWekcrOUBPhlDmkx/FPFiejrYa/F22WpqllVNnS25v
11+
plHhN0/GTYjhMfg9+ekQhvXyjAiyBxk7Q/nd23QipEJMhrb3Z5FU9NfiPMiVSUrX
12+
Mo9mclOHnfgv2p/Hge35SYYWn7xnJYt124nRTDLyYQIR8WcCeETyFExjjAkYro+E
13+
GA0qT0iq5MnbYI/7k4bRx3sCAwEAAQ==
14+
-----END PUBLIC KEY-----

0 commit comments

Comments
 (0)