Skip to content

Commit c903af1

Browse files
committed
Add jwks to trustlist
1 parent b5820b8 commit c903af1

File tree

9 files changed

+429
-218
lines changed

9 files changed

+429
-218
lines changed

examples/TokenX5C/Program.cs

Lines changed: 283 additions & 177 deletions
Large diffs are not rendered by default.

src/AasSecurity/GlobalSecurityVariables.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public static class GlobalSecurityVariables
2323
internal static List<X509Certificate2> ServerCertificates = new();
2424
internal static List<string> ServerCertFileNames = new();
2525
internal static List<string> ServerDomain = new();
26+
internal static List<string> ServerJwksUrl = new();
27+
internal static List<string> ServerKid = new();
2628
internal static List<SecurityRight> SecurityRights = new();
2729
internal static Dictionary<string, string> SecurityUsernamePassword = new();
2830
internal static Property ConditionSM = null;

src/AasSecurity/SecurityHelper.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ private static void ParseSecurityMetamodel()
8080
domain = "";
8181
if (GlobalSecurityVariables.ServerCertFileNames != null)
8282
{
83-
for (int i = 0; i < GlobalSecurityVariables.ServerCertFileNames.Count; i++)
83+
for (var i = 0; i < GlobalSecurityVariables.ServerCertFileNames.Count; i++)
8484
{
8585
if (Path.GetFileName(GlobalSecurityVariables.ServerCertFileNames[i]) == serverName + ".cer")
8686
{
@@ -92,5 +92,23 @@ private static void ParseSecurityMetamodel()
9292

9393
return null;
9494
}
95+
96+
internal static string? FindServerJwksUrl(string kid, out string domain)
97+
{
98+
domain = "";
99+
if (GlobalSecurityVariables.ServerKid != null)
100+
{
101+
for (var i = 0; i < GlobalSecurityVariables.ServerKid.Count; i++)
102+
{
103+
if (GlobalSecurityVariables.ServerKid[i] == kid)
104+
{
105+
domain = GlobalSecurityVariables.ServerDomain[i];
106+
return GlobalSecurityVariables.ServerJwksUrl[i];
107+
}
108+
}
109+
}
110+
111+
return null;
112+
}
95113
}
96114
}

src/AasSecurity/SecurityService.cs

Lines changed: 66 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,37 @@
1111
* SPDX-License-Identifier: Apache-2.0
1212
********************************************************************************/
1313

14-
using AasSecurity.Models;
15-
using AasSecurity.Exceptions;
16-
using AasxServer;
17-
using AasxServerStandardBib.Logging;
18-
using AasxServerStandardBib.Services;
19-
using Extensions;
20-
using Jose;
21-
using Microsoft.AspNetCore.Authentication;
22-
using Microsoft.AspNetCore.Http;
23-
using Microsoft.Extensions.Logging;
24-
using Microsoft.IdentityModel.Tokens;
2514
using System.Collections.Specialized;
15+
using System.Data;
2616
using System.IdentityModel.Tokens.Jwt;
17+
using System.Linq.Dynamic.Core;
18+
using System.Linq.Expressions;
19+
using System.Net;
20+
using System.Net.Http.Headers;
2721
using System.Security.Claims;
2822
using System.Security.Cryptography;
2923
using System.Security.Cryptography.X509Certificates;
3024
using System.Text;
25+
using System.Text.Json;
3126
using System.Web;
32-
using File = AasCore.Aas3_0.File;
27+
using AasSecurity.Exceptions;
28+
using AasSecurity.Models;
29+
using AasxServer;
30+
using AasxServerStandardBib.Logging;
31+
using AasxServerStandardBib.Services;
3332
using Contracts;
34-
using System.Linq.Expressions;
33+
using Extensions;
3534
using Irony.Parsing;
36-
using System.Linq.Dynamic.Core;
35+
using Jose;
36+
using Microsoft.AspNetCore.Authentication;
37+
using Microsoft.AspNetCore.Http;
38+
using Microsoft.Extensions.Logging;
3739
using Microsoft.IdentityModel.JsonWebTokens;
38-
using static QRCoder.PayloadGenerator;
39-
using System.Data;
40+
using Microsoft.IdentityModel.Tokens;
4041
using Namotion.Reflection;
42+
using Newtonsoft.Json.Linq;
43+
using static QRCoder.PayloadGenerator;
44+
using File = AasCore.Aas3_0.File;
4145

4246
namespace AasSecurity
4347
{
@@ -427,7 +431,8 @@ private string HandleBearerToken(string? bearerToken, ref string user, ref bool
427431
.First(c => c.Type == "tid").Value;
428432

429433
var jwksUrl = $"https://login.microsoftonline.com/{tenantId}/discovery/v2.0/keys";
430-
using var httpClient = new HttpClient();
434+
var clientHandler = new HttpClientHandler { DefaultProxyCredentials = CredentialCache.DefaultCredentials };
435+
using var httpClient = new HttpClient(clientHandler);
431436
var jwksJson = httpClient.GetStringAsync(jwksUrl).Result;
432437
var jwks = new JsonWebKeySet(jwksJson);
433438
var signingKeys = jwks.GetSigningKeys();
@@ -458,6 +463,50 @@ private string HandleBearerToken(string? bearerToken, ref string user, ref bool
458463
}
459464
else
460465
{
466+
if (jwtSecurityToken.Header.TryGetValue("kid", out _))
467+
{
468+
user = "";
469+
var jwksUrl = "";
470+
var kid = jwtSecurityToken.Header["kid"].ToString();
471+
if (kid != null)
472+
{
473+
jwksUrl = SecurityHelper.FindServerJwksUrl(kid, out domain);
474+
}
475+
if (jwksUrl != "")
476+
{
477+
var clientHandler = new HttpClientHandler { DefaultProxyCredentials = CredentialCache.DefaultCredentials };
478+
using var httpClient = new HttpClient(clientHandler);
479+
var jwksJson = httpClient.GetStringAsync(jwksUrl + "/jwks").Result;
480+
var jwks = new JsonWebKeySet(jwksJson);
481+
var signingKeys = jwks.GetSigningKeys();
482+
483+
var tokenHandler = new JwtSecurityTokenHandler();
484+
var validationParameters = new TokenValidationParameters
485+
{
486+
ValidateIssuer = false,
487+
ValidateAudience = false,
488+
ValidateLifetime = true,
489+
ValidateIssuerSigningKey = true,
490+
IssuerSigningKeys = signingKeys
491+
};
492+
493+
try
494+
{
495+
var principal = tokenHandler.ValidateToken(bearerToken, validationParameters, out var validatedToken);
496+
497+
user = jwtSecurityToken.Claims.First(c => c.Type == "userName").Value;
498+
if (!string.IsNullOrEmpty(user))
499+
{
500+
return "";
501+
}
502+
user = "";
503+
}
504+
catch (Exception ex)
505+
{
506+
}
507+
}
508+
}
509+
461510
var serverName = jwtSecurityToken.Claims.First(c => c.Type == "serverName").Value;
462511
if (!string.IsNullOrEmpty(serverName))
463512
{

src/AasSecurity/SecuritySettingsForServerParser.cs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@
1111
* SPDX-License-Identifier: Apache-2.0
1212
********************************************************************************/
1313

14-
using AasSecurity.Models;
15-
using AasxServer;
16-
using AdminShellNS;
1714
using System;
1815
using System.Buffers.Text;
1916
using System.Security.Cryptography.X509Certificates;
17+
using AasSecurity.Models;
18+
using AasxServer;
19+
using AdminShellNS;
20+
using IdentityModel;
2021

2122
namespace AasSecurity
2223
{
@@ -91,6 +92,8 @@ private static void ParseAuthenticationServer(AdminShellPackageEnv env, Submodel
9192
var domain = "";
9293
var base64 = "";
9394
var insideBas64 = false;
95+
var jwks = "";
96+
var kid = "";
9497
foreach (var line in lines)
9598
{
9699
if (line == "" || line.StartsWith("# "))
@@ -104,12 +107,29 @@ private static void ParseAuthenticationServer(AdminShellPackageEnv env, Submodel
104107
serverName = split[1];
105108
Console.WriteLine(" serverName: " + serverName);
106109
}
107-
if (line.Contains("domain: "))
110+
else if (line.Contains("domain: "))
108111
{
109112
var split = line.Split(": ");
110113
domain = split[1];
111114
Console.WriteLine(" domain: " + domain);
112115
}
116+
else if (line.Contains("jwks: "))
117+
{
118+
var split = line.Split(": ");
119+
jwks = split[1];
120+
Console.WriteLine(" jwks: " + jwks);
121+
}
122+
else if (line.Contains("kid: "))
123+
{
124+
var split = line.Split(": ");
125+
kid = split[1];
126+
Console.WriteLine(" kid: " + kid);
127+
GlobalSecurityVariables.ServerCertificates.Add(null);
128+
GlobalSecurityVariables.ServerCertFileNames.Add("");
129+
GlobalSecurityVariables.ServerDomain.Add(domain);
130+
GlobalSecurityVariables.ServerJwksUrl.Add(jwks);
131+
GlobalSecurityVariables.ServerKid.Add(kid);
132+
}
113133
else if (line.Contains("BEGIN CERTIFICATE"))
114134
{
115135
insideBas64 = true;
@@ -124,6 +144,8 @@ private static void ParseAuthenticationServer(AdminShellPackageEnv env, Submodel
124144
GlobalSecurityVariables.ServerCertificates.Add(x509);
125145
GlobalSecurityVariables.ServerCertFileNames.Add(serverName + ".cer");
126146
GlobalSecurityVariables.ServerDomain.Add(domain);
147+
GlobalSecurityVariables.ServerJwksUrl.Add("");
148+
GlobalSecurityVariables.ServerKid.Add("");
127149
}
128150
else if (insideBas64)
129151
{

src/AasxServerBlazor/Pages/Pcf2.razor

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -602,9 +602,9 @@
602602
}
603603
}
604604

605-
@code {
606-
607-
// void toggle(int clickIndex)
605+
@code {
606+
607+
// void toggle(int clickIndex)
608608
void toggle(CfpNode n)
609609
{
610610
if (closedNode.Contains(n.aas.Id))
@@ -642,19 +642,24 @@
642642
var idEncoded = Base64UrlEncoder.Encode(sm.Id);
643643
if (sm.Extensions != null)
644644
{
645-
endpoint = sm.Extensions[ 0 ].Value;
646-
var s1 = endpoint.Split("/shells/");
647-
if (s1.Length == 2)
648-
{
649-
var s2 = s1[ 1 ].Split("/submodels/");
650-
if (s2.Length == 2)
645+
endpoint = sm.Extensions[0].Value;
646+
if (endpoint.Contains("/shells/"))
647+
{
648+
var s1 = endpoint.Split("/shells/");
649+
if (s1.Length == 2)
651650
{
652-
idEncoded = s2[ 1 ].Replace("/submodel/", "");
653-
;
654-
endpoint = s1[ 0 ] + "/submodels/" + idEncoded;
651+
var s2 = s1[1].Split("/submodels/");
652+
if (s2.Length == 2)
653+
{
654+
idEncoded = s2[1].Replace("/submodel/", "");
655+
endpoint = s1[0] + "/submodels/" + idEncoded;
656+
}
655657
}
658+
}
659+
else
660+
{
661+
;
656662
}
657-
// endpoint = endpoint.Replace("/submodel/", "");
658663
}
659664
else
660665
{
@@ -709,7 +714,7 @@
709714
if (f.Value != "")
710715
{
711716
string[] split = f.Value.Split(new Char[] {'/'});
712-
if (split.Length == 2 || (split.Length > 1 && (split[ 1 ].ToLower() == "aasx" || split[ 1 ].ToLower() == "tmp")))
717+
if (split.Length == 1 || split.Length == 2 || (split.Length > 1 && (split[1].ToLower() == "aasx" || split[1].ToLower() == "tmp")))
713718
{
714719
split = f.Value.Split(new Char[] {'.'});
715720
switch (split.Last().ToLower())
@@ -768,6 +773,10 @@
768773

769774
break;
770775
}
776+
}
777+
else
778+
{
779+
;
771780
}
772781
}
773782

src/AasxServerDB/EntityFrameworkPersistenceService.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -398,13 +398,14 @@ public async Task<DbRequestResult> DoDbOperation(DbRequest dbRequest)
398398
case DbRequestOp.ReadSubmodelById:
399399
found = IsSubmodelPresent(db, securityCondition, aasIdentifier, submodelIdentifier, true, false, out _, out ISubmodel submodel);
400400

401+
var aasText = aasIdentifier == null ? "" : $" in Asset Administration Shell with id {aasIdentifier}";
401402
if (found)
402403
{
403-
scopedLogger.LogDebug($"Submodel with id {submodelIdentifier} in Asset Administration Shell with id {aasIdentifier} found.");
404+
scopedLogger.LogDebug($"Submodel with id {submodelIdentifier}{aasText} found.");
404405
}
405406
else
406407
{
407-
throw new NotFoundException($"Submodel with id {submodelIdentifier} in Asset Administration Shell with id {aasIdentifier} not found.");
408+
throw new NotFoundException($"Submodel with id {submodelIdentifier}{aasText} not found.");
408409
}
409410

410411
if (dbRequest.Context.Params.IsSigned)
@@ -1406,7 +1407,8 @@ private string ReadFileByPath(AasContext db, Dictionary<string, string>? securit
14061407
}
14071408
else
14081409
{
1409-
throw new NotFoundException($"Submodel with id {submodelIdentifier} in Asset Administration Shell with id {aasIdentifier} not found.");
1410+
var aasText = aasIdentifier == null ? "" : $" in Asset Administration Shell with id {aasIdentifier}";
1411+
throw new NotFoundException($"Submodel with id {submodelIdentifier}{aasText} not found.");
14101412
}
14111413
}
14121414

src/AasxServerDB/Query.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2073,7 +2073,7 @@ private static string ChangeINSTRToLIKE(string rawSQL)
20732073
}
20742074
i++;
20752075
// result = result + $"{param[0]} LIKE \'%{paramS[1]}%\' {splitParam[i]}";
2076-
result += $"{param[0]} LIKE '%' || {paramSNotEmpty} || '%' {splitParam[i]}";
2076+
result += $"{param[0]} LIKE '%' || '{paramSNotEmpty}' || '%' {splitParam[i]}";
20772077
}
20782078
return result;
20792079
}

src/Contracts/QueryParserJSON.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,10 @@ public static string ReplaceField(string mode, string value, string smeValue)
603603
value = value.Replace("$aas#", "aas.");
604604
value = value.Replace("$sm#", "sm.");
605605
value = value.Replace("$sme#", "sme.");
606-
value = value.Replace("sm.Id", "sm.Identifier");
606+
if (value == "sm.Id")
607+
{
608+
value = value.Replace("sm.Id", "sm.Identifier");
609+
}
607610
switch (mode)
608611
{
609612
case "all":

0 commit comments

Comments
 (0)