diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/AuthResponse/Encryption/EncryptedAuthorizationResponse.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/AuthResponse/Encryption/EncryptedAuthorizationResponse.cs index 663bc4e9..cc81cd6f 100644 --- a/src/WalletFramework.Oid4Vc/Oid4Vp/AuthResponse/Encryption/EncryptedAuthorizationResponse.cs +++ b/src/WalletFramework.Oid4Vc/Oid4Vp/AuthResponse/Encryption/EncryptedAuthorizationResponse.cs @@ -17,7 +17,7 @@ public static FormUrlEncodedContent ToFormUrl(this EncryptedAuthorizationRespons { var content = new Dictionary { - { "response", response.ToString() }, + { "response", response.ToString() } }; return new FormUrlEncodedContent(content); diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Extensions/X509CertificateExtensions.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Extensions/X509CertificateExtensions.cs index d08538ea..8ae53b3a 100644 --- a/src/WalletFramework.Oid4Vc/Oid4Vp/Extensions/X509CertificateExtensions.cs +++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Extensions/X509CertificateExtensions.cs @@ -19,52 +19,36 @@ public static bool IsTrustChainValid(this List trustChain) { var leafCert = trustChain.First(); - var rootCerts = - new HashSet( - trustChain - .Skip(1) - .Where(cert => cert.IssuerDN.Equivalent(cert.SubjectDN)) - .Select(cert => new TrustAnchor(cert, null)) - .ToList() - ); - - var intermediateCerts = - new HashSet( - trustChain - .Skip(1) - .Where(cert => !cert.IssuerDN.Equivalent(cert.SubjectDN)) - .Append(leafCert) - ); - - var builderParams = - new PkixBuilderParameters( - rootCerts, - new X509CertStoreSelector - { - Certificate = leafCert - } - ) - { - //TODO: Check if CRLs (Certificate Revocation Lists) are valid - IsRevocationEnabled = false - }; - - builderParams.AddStore( - X509StoreFactory.Create( - "Certificate/Collection", - new X509CollectionStoreParameters(intermediateCerts) - ) - ); + var rootCerts = new HashSet( + trustChain + .Where(cert => cert.IsSelfSigned()) + .Select(cert => new TrustAnchor(cert, null))); + + var intermediateCerts = new HashSet( + trustChain + .Where(cert => !cert.IsSelfSigned()) + .Append(leafCert)); + + var storeSelector = new X509CertStoreSelector { Certificate = leafCert }; + + var builderParams = new PkixBuilderParameters(rootCerts, storeSelector) + { + //TODO: Check if CRLs (Certificate Revocation Lists) are valid + IsRevocationEnabled = false + }; + + var store = X509StoreFactory.Create( + "Certificate/Collection", + new X509CollectionStoreParameters(intermediateCerts)); + builderParams.AddStore(store); // This throws if validation fails - new PkixCertPathValidator() - .Validate( - new PkixCertPathBuilder() - .Build(builderParams) - .CertPath, - builderParams - ); + var path = new PkixCertPathBuilder().Build(builderParams).CertPath; + new PkixCertPathValidator().Validate(path, builderParams); return true; } + + internal static bool IsSelfSigned(this X509Certificate certificate) => + certificate.IssuerDN.Equivalent(certificate.SubjectDN); } diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/RequestObject.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/RequestObject.cs index 4f1ac306..649c7994 100644 --- a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/RequestObject.cs +++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/RequestObject.cs @@ -74,9 +74,14 @@ public static class RequestObjectExtensions /// The validated request object. /// Throws when validation fails public static RequestObject ValidateJwt(this RequestObject requestObject) - => ((JwtSecurityToken)requestObject).IsSignatureValid(requestObject.GetLeafCertificate().GetPublicKey()) + { + var jwt = (JwtSecurityToken)requestObject; + var pubKey = requestObject.GetLeafCertificate().GetPublicKey(); + + return jwt.IsSignatureValid(pubKey) ? requestObject : throw new InvalidOperationException("Invalid JWT Signature"); + } /// /// Validates the SAN name of the leaf certificate @@ -85,9 +90,8 @@ public static RequestObject ValidateJwt(this RequestObject requestObject) /// Throws when validation fails public static RequestObject ValidateSanName(this RequestObject requestObject) { - var x509Certificate = new X509Certificate2( - requestObject.GetLeafCertificate().GetEncoded() - ); + var encoded = requestObject.GetLeafCertificate().GetEncoded(); + var x509Certificate = new X509Certificate2(encoded); return GetSanDnsNames(x509Certificate).Any(sanDnsName => requestObject.ClientId.EndsWith(sanDnsName.Split("*").Last())) ? requestObject @@ -121,25 +125,36 @@ private static IEnumerable GetSanDnsNames(X509Certificate2 certificate) /// /// The validated request object /// Throws when validation fails - public static RequestObject ValidateTrustChain(this RequestObject requestObject) => - requestObject - .GetCertificates() - .IsTrustChainValid() - ? requestObject - : throw new InvalidOperationException("Validation of trust chain failed"); - - internal static List GetCertificates(this RequestObject requestObject) => - Parse(((JwtSecurityToken)requestObject).Header.X5c) - .Select( - certAsJToken => - new X509CertificateParser() - .ReadCertificate( - FromBase64String(certAsJToken.ToString()) - ) - ) - .ToList(); + public static RequestObject ValidateTrustChain(this RequestObject requestObject) + { + var certificates = requestObject.GetCertificates(); + if (certificates.Count == 1) + { + if (certificates.First().IsSelfSigned()) + return requestObject; + else + throw new InvalidOperationException("TrustChain must not consist of only one non-self-signed certificate"); + } + else + { + if (certificates.IsTrustChainValid()) + return requestObject; + else + throw new InvalidOperationException("Validation of trust chain failed"); + } + } + + internal static List GetCertificates(this RequestObject requestObject) + { + var x5C = ((JwtSecurityToken)requestObject).Header.X5c; + return Parse(x5C).Select( + certAsJToken => + { + var certBytes = FromBase64String(certAsJToken.ToString()); + return new X509CertificateParser().ReadCertificate(certBytes); + }).ToList(); + } internal static X509Certificate GetLeafCertificate(this RequestObject requestObject) => GetCertificates(requestObject).First(); } - diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Services/Oid4VpClientService.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Services/Oid4VpClientService.cs index ce863637..0bca006a 100644 --- a/src/WalletFramework.Oid4Vc/Oid4Vp/Services/Oid4VpClientService.cs +++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Services/Oid4VpClientService.cs @@ -108,7 +108,6 @@ public Oid4VpClientService( .PresentationDefinition .InputDescriptors; - // TODO: This is only a hack until the encryption response is implemented var mdocNonce = Option.None; var presentationMapTasks = credentials.Select(async credential => @@ -291,8 +290,11 @@ await _oid4VpRecordService.StoreAsync( } //TODO: Refactor this C'' method into current flows (too much duplicate code) - public async Task SendAuthorizationResponseAsync(AuthorizationRequest authorizationRequest, IEnumerable selectedCredentials, - IssuanceSession issuanceSession, CombinedWalletAttestation? clientAttestation = null) + public async Task SendAuthorizationResponseAsync( + AuthorizationRequest authorizationRequest, + IEnumerable selectedCredentials, + IssuanceSession issuanceSession, + CombinedWalletAttestation? clientAttestation = null) { var credentials = selectedCredentials.ToList(); @@ -300,7 +302,6 @@ await _oid4VpRecordService.StoreAsync( .PresentationDefinition .InputDescriptors; - // TODO: This is only a hack until the encryption response is implemented var mdocNonce = Option.None; var presentationMapTasks = credentials.Select(async credential => diff --git a/test/WalletFramework.Oid4Vc.Tests/Oid4Vp/AuthRequest/Samples/AuthRequestSamples.cs b/test/WalletFramework.Oid4Vc.Tests/Oid4Vp/AuthRequest/Samples/AuthRequestSamples.cs new file mode 100644 index 00000000..43bfe627 --- /dev/null +++ b/test/WalletFramework.Oid4Vc.Tests/Oid4Vp/AuthRequest/Samples/AuthRequestSamples.cs @@ -0,0 +1,18 @@ +namespace WalletFramework.Oid4Vc.Tests.Oid4Vp.AuthRequest.Samples; + +public static class AuthRequestSamples +{ + public const string SignedRequestObjectWithRs256AndTrustChain = + "eyJ4NWMiOlsiTUlJRVZ6Q0NBeitnQXdJQkFnSVVPbmRKVVZtRU9hVC9Hb1MwN1hJbktYL2pxd0l3RFFZSktvWklodmNOQVFFTEJRQXdnWUV4Q3pBSkJnTlZCQVlUQWtSRk1ROHdEUVlEVlFRSURBWklaWE56Wlc0eEVqQVFCZ05WQkFjTUNWZHBaWE5pWVdSbGJqRVlNQllHQTFVRUNnd1BSWGhoYlhCc1pTQkRiMjF3WVc1NU1Rc3dDUVlEVlFRTERBSkpWREVtTUNRR0ExVUVBd3dkWkdWdGJ5NWpaWEowYVdacFkyRjBhVzl1TG05d1pXNXBaQzV1WlhRd0hoY05NalF3T1RBeU1UVTBOVFUzV2hjTk1qVXdPVEF5TVRVME5UVTNXakNCZ1RFTE1Ba0dBMVVFQmhNQ1JFVXhEekFOQmdOVkJBZ01Ca2hsYzNObGJqRVNNQkFHQTFVRUJ3d0pWMmxsYzJKaFpHVnVNUmd3RmdZRFZRUUtEQTlGZUdGdGNHeGxJRU52YlhCaGJua3hDekFKQmdOVkJBc01Ba2xVTVNZd0pBWURWUVFEREIxa1pXMXZMbU5sY25ScFptbGpZWFJwYjI0dWIzQmxibWxrTG01bGREQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQU56WGk2cWdEOFdPWnkwUEphcjIvK1M1bmp6OG1BaGY5QWRjZ2pwa2NmbS8yZW9ZNUpHa0JGbzk3L0pFcytkVTUwSnNmV0JNUTNFaTZ5WmFJc0Y2bWdzVjg3aDFheG1XbnNUUUVjVCtjd1FJK2VHVC9EQVEwNEExekxDbDdyaTAyZWozYU93YjRXSzh3VktQZG1VOS83OENlMWVmd2cxSStIc0RQVnFWUkNOUHhGcytCc2NrTlFkcE5reHA4N3Y5SU5acUpmWmlzNDlBWVliUmRzeG5mb3pMK3RHU1psMkppNHhQZnAraDlLNndxbW00Y1BPTFJ1Ty92VGFxd3FXaFhBWlh1dkNWT0lCWFZzUnNZclN4aWZMMVg4UUVwQUZ1MktBWGhKL3lvL05hYTdzcTZzSERPcEZCV2tLM0txYkp2dlpjZ3Z6blJEdmFiMlpDbWFhaWNUc0NBd0VBQWFPQnhEQ0J3VEJxQmdOVkhSRUVZekJoZ2gxa1pXMXZMbU5sY25ScFptbGpZWFJwYjI0dWIzQmxibWxrTG01bGRJSVBkM2QzTG1WNFlXMXdiR1V1WTI5dGdoQnRZV2xzTG1WNFlXMXdiR1V1WTI5dGdoRmhibTkwYUdWeVpHOXRZV2x1TG1OdmJZY0V3S2dCQVljRXdLZ0JBakFUQmdOVkhTVUVEREFLQmdnckJnRUZCUWNEQWpBZEJnTlZIUTRFRmdRVW96UUdBZFZZc0c5cDdkMDI2cDhlTHdCMVRyMHdId1lEVlIwakJCZ3dGb0FVUmNDdUVjNXJPYnAyd1RLVUs5SzE3bC9MT3lZd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFIdHVzNFdLNmZpRUF0Ulhma0FFUkoybzZnWXBNdTlicXN4N3QyN1g3NUQ2MXJsZ2c1SXo0bm9nU29lWE9yNzhFcXdJTEFZRXRwWjN6SWZ4d0tuSXJ1dmdxMmpEN1IvbWhWaFFwS2M1WmFmVDgyZVZWTkZxeWhudnhST29HN0pUamFZbWNkZENQS3NZYVdxVEErNHMxR1dQbGNvc3piQlpKYWFEYzRVbEIxbTZvaFRUWFk2N1FCR0t1Szlic24xeE5mcnZ1Q05mK1FsNUsxd3RacGNETXpEenFIU0dWazB0MHQxdnVvdDhET0xMY2JDY29lMFUzbUtZK0VaK1BoczhCTTJ5ZDUxbWE1WFc3Y1JlTkZhaTh5bkluN3lDT1ptdG1BSFNMb1pvMjhLYWh1VDVTVXZGVnJJMjBFMDhDMzByTzJtVGFBYXoxODJaZGlZZndlQnVvTkU9IiwiTUlJRUlUQ0NBd21nQXdJQkFnSVVPTUdrZ0NEeHFWczZuZHZvMTNxNmRzM2ZmczB3RFFZSktvWklodmNOQVFFTEJRQXdnWUV4Q3pBSkJnTlZCQVlUQWtSRk1ROHdEUVlEVlFRSURBWklaWE56Wlc0eEVqQVFCZ05WQkFjTUNWZHBaWE5pWVdSbGJqRVlNQllHQTFVRUNnd1BSWGhoYlhCc1pTQkRiMjF3WVc1NU1Rc3dDUVlEVlFRTERBSkpWREVtTUNRR0ExVUVBd3dkWkdWdGJ5NWpaWEowYVdacFkyRjBhVzl1TG05d1pXNXBaQzV1WlhRd0hoY05NalF3T1RBeU1UVXhPREE1V2hjTk1qVXdPVEF5TVRVeE9EQTVXakNCZ1RFTE1Ba0dBMVVFQmhNQ1JFVXhEekFOQmdOVkJBZ01Ca2hsYzNObGJqRVNNQkFHQTFVRUJ3d0pWMmxsYzJKaFpHVnVNUmd3RmdZRFZRUUtEQTlGZUdGdGNHeGxJRU52YlhCaGJua3hDekFKQmdOVkJBc01Ba2xVTVNZd0pBWURWUVFEREIxa1pXMXZMbU5sY25ScFptbGpZWFJwYjI0dWIzQmxibWxrTG01bGREQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUxkclRIdlY5ZkY3KzhTYm8rUTRRVEEzMmhkdFFMTWF6VDdWblJLMU9HNk03RndrYkVjYzZORW0zYzdaQVplbHZCUTR2RGdTdFdxMmRFR25nbERYVkhtMW1ncktEcVp3VnRPZk1YZHlYemdZeTlwc2puRlZvaCs5UGVpZkxWT1N2WVgzT09rN01nWGMzUStKd0hQcGpxaDFIeGZhVW5FUVcrYnk2b21wd2UwYnhsTG5wdFJZa09RZUlmdG9kU0tJNXhDNDk4Q0lJWTlCOXNuSVk5aHdJK295UHE5SjNic3k0dGJ3bmNsU1pvQTlZNXlEM25mcTh3VHBRdG9pZm5KWkRGR1VPOGxmb1VxOXpQOEVZSVFVeXRFQTZ5UHdaSmVQTHdrdGtUdUs5QWtoampOL0VuT1BVSUZ5NTVkMmVEbUNDVjVVenJhQkdMbkU4TCt4MStINDZROENBd0VBQWFPQmpqQ0JpekJxQmdOVkhSRUVZekJoZ2gxa1pXMXZMbU5sY25ScFptbGpZWFJwYjI0dWIzQmxibWxrTG01bGRJSVBkM2QzTG1WNFlXMXdiR1V1WTI5dGdoQnRZV2xzTG1WNFlXMXdiR1V1WTI5dGdoRmhibTkwYUdWeVpHOXRZV2x1TG1OdmJZY0V3S2dCQVljRXdLZ0JBakFkQmdOVkhRNEVGZ1FVUmNDdUVjNXJPYnAyd1RLVUs5SzE3bC9MT3lZd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFFRS9WTzloWlc4alE1Q1NyeDNWTEsyYlA1dnpZcVRaaVhIMUFGZVlhei9mSTdRYlBtWlg1K3U3OVJZbXd0L3lPQW5QZjhUV2VBQmI3dVNWTDV6Sm51YlNjTkZ1QkVobW1UcmNPcWJQcExoRlB6UjcvYmVOZTltOWh5cG03V1ZFRlYzdVBLTUxhd2YybUx5VW95Zy9xSWg2MlgxSVk4enVFN1pzaGZWenB0NW5UWHFVRGc2dUY3TzQ1LzlPTnNEOWszNFBIVUt4cVhScHJVL2hxaUhhM3VqeXdkT3RtVGhhN1dQQ0R2ejZMNGlRUEFKWXFnb3NlejJ6OHAyZnkvd0d3VEdOSkdoY242OVRWWXN1Ylczd2lVL0ZRdnFYdzZ3bzFRNm5ldG5RNFhIMnRKMUJlWXB3ZHpSTGdlNTcwYjN0aDRtQ0N3WEl1R0xpd01zYWVxNDVKUDg9Il0sImtpZCI6IjRjNDAxODA2LWI5ZDMtNGI1OC1iODI2LTAyMTk5ZjFmM2M3ZiIsImFsZyI6IlJTMjU2In0.eyJyZXNwb25zZV91cmkiOiJodHRwczovL2RlbW8uY2VydGlmaWNhdGlvbi5vcGVuaWQubmV0L3Rlc3QvYS94NTA5U2FuRG5zX3dpdGhfdmFsaWRfdHJ1c3RfY2hhaW4vcmVzcG9uc2V1cmkiLCJhdWQiOiJodHRwczovL3NlbGYtaXNzdWVkLm1lL3YyIiwiY2xpZW50X2lkX3NjaGVtZSI6Ing1MDlfc2FuX2RucyIsInByZXNlbnRhdGlvbl9kZWZpbml0aW9uIjp7ImlkIjoiNzQ3Y2JjODAtZTIyMi00ODc5LWEyZGMtZGY4ZjFjNjk4M2RjIiwibmFtZSI6IlBJRF9TRF9KV1QiLCJwdXJwb3NlIjoiVGVzdGluZyBPcGVuSUQgQ29uZm9ybWFuY2UiLCJmb3JtYXQiOnsidmMrc2Qtand0Ijp7ImFsZyI6WyJFUzI1NiIsIkVTMzg0Il19fSwiaW5wdXRfZGVzY3JpcHRvcnMiOlt7ImlkIjoic2Qtand0LXBpZCIsImZvcm1hdCI6eyJ2YytzZC1qd3QiOnsiYWxnIjpbIkVTMjU2IiwiRVMzODQiXX19LCJjb25zdHJhaW50cyI6eyJmaWVsZHMiOlt7InBhdGgiOlsiJC52Y3QiXSwiZmlsdGVyIjp7InR5cGUiOiJzdHJpbmciLCJjb25zdCI6InVybjpldS5ldXJvcGEuZWMuZXVkaTpwaWQ6MSJ9fSx7InBhdGgiOlsiJC5naXZlbl9uYW1lIl19LHsicGF0aCI6WyIkLmZhbWlseV9uYW1lIl19LHsicGF0aCI6WyIkLmJpcnRoZGF0ZSJdfSx7InBhdGgiOlsiJC5hZGRyZXNzLnN0cmVldF9hZGRyZXNzIl19LHsicGF0aCI6WyIkLmFkZHJlc3MubG9jYWxpdHkiXX0seyJwYXRoIjpbIiQuYWRkcmVzcy5wb3N0YWxfY29kZSJdfSx7InBhdGgiOlsiJC5hZGRyZXNzLmNvdW50cnkiXX1dfSwibGltaXRfZGlzY2xvc3VyZSI6InJlcXVpcmVkIn1dfSwicmVzcG9uc2VfdHlwZSI6InZwX3Rva2VuIiwibm9uY2UiOiJkZ2Q2aGRjREFlejYtLl9-IiwiY2xpZW50X2lkIjoiZGVtby5jZXJ0aWZpY2F0aW9uLm9wZW5pZC5uZXQiLCJjbGllbnRfbWV0YWRhdGEiOnsidnBfZm9ybWF0cyI6eyJ2YytzZC1qd3QiOnsic2Qtand0X2FsZ192YWx1ZXMiOlsiUlMyNTYiLCJSUzM4NCIsIlJTNTEyIiwiUFMyNTYiLCJQUzM4NCIsIlBTNTEyIiwiRVMyNTYiLCJFUzI1NksiLCJFUzM4NCIsIkVTNTEyIiwiRWREU0EiXSwia2Itand0X2FsZ192YWx1ZXMiOlsiUlMyNTYiLCJSUzM4NCIsIlJTNTEyIiwiUFMyNTYiLCJQUzM4NCIsIlBTNTEyIiwiRVMyNTYiLCJFUzI1NksiLCJFUzM4NCIsIkVTNTEyIiwiRWREU0EiXX19fSwicmVzcG9uc2VfbW9kZSI6ImRpcmVjdF9wb3N0In0.qSGlQYjLXTV-mxQ6XgYiWyzpE-arh3NNkS2LXHVHH9GhECmMAprzDbl4ZSq9rkvOKcqLc-7_oSrJK4HBq_oJA4YOhHIrbpzLWKeqOvwP8uG_4WD-Lq8z9PLeyh14rOJgbz-ThZJ_GeW69DEhsmAKctfR3MJRvVfR2S5HO5RiSW0USqk-4B-lSVoxyiqu3ysyhKW8aaHbU-aacHJZ9FXb1XhhNqlobTbq26bsdhM2w1y2-0lPKFL7NoQnmbeXkf9TTjgMCSE8Cv94MloWPqwIao4wRcNaClX3c9D4zItWH06OT1lVMCXEsnrnOuACzyKRHI_s6912qw0-YZoOCCY-4Q"; + + public static string SignedRequestObjectWithRs256AndInvalidSignature => + SignedRequestObjectWithRs256AndTrustChain.Remove( + SignedRequestObjectWithRs256AndTrustChain.Length - 1, + 1); + + public const string SignedRequestObjectWithRs256AndSingleSelfSigned = + "eyJ4NWMiOlsiTUlJRUlUQ0NBd21nQXdJQkFnSVVPTUdrZ0NEeHFWczZuZHZvMTNxNmRzM2ZmczB3RFFZSktvWklodmNOQVFFTEJRQXdnWUV4Q3pBSkJnTlZCQVlUQWtSRk1ROHdEUVlEVlFRSURBWklaWE56Wlc0eEVqQVFCZ05WQkFjTUNWZHBaWE5pWVdSbGJqRVlNQllHQTFVRUNnd1BSWGhoYlhCc1pTQkRiMjF3WVc1NU1Rc3dDUVlEVlFRTERBSkpWREVtTUNRR0ExVUVBd3dkWkdWdGJ5NWpaWEowYVdacFkyRjBhVzl1TG05d1pXNXBaQzV1WlhRd0hoY05NalF3T1RBeU1UVXhPREE1V2hjTk1qVXdPVEF5TVRVeE9EQTVXakNCZ1RFTE1Ba0dBMVVFQmhNQ1JFVXhEekFOQmdOVkJBZ01Ca2hsYzNObGJqRVNNQkFHQTFVRUJ3d0pWMmxsYzJKaFpHVnVNUmd3RmdZRFZRUUtEQTlGZUdGdGNHeGxJRU52YlhCaGJua3hDekFKQmdOVkJBc01Ba2xVTVNZd0pBWURWUVFEREIxa1pXMXZMbU5sY25ScFptbGpZWFJwYjI0dWIzQmxibWxrTG01bGREQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUxkclRIdlY5ZkY3KzhTYm8rUTRRVEEzMmhkdFFMTWF6VDdWblJLMU9HNk03RndrYkVjYzZORW0zYzdaQVplbHZCUTR2RGdTdFdxMmRFR25nbERYVkhtMW1ncktEcVp3VnRPZk1YZHlYemdZeTlwc2puRlZvaCs5UGVpZkxWT1N2WVgzT09rN01nWGMzUStKd0hQcGpxaDFIeGZhVW5FUVcrYnk2b21wd2UwYnhsTG5wdFJZa09RZUlmdG9kU0tJNXhDNDk4Q0lJWTlCOXNuSVk5aHdJK295UHE5SjNic3k0dGJ3bmNsU1pvQTlZNXlEM25mcTh3VHBRdG9pZm5KWkRGR1VPOGxmb1VxOXpQOEVZSVFVeXRFQTZ5UHdaSmVQTHdrdGtUdUs5QWtoampOL0VuT1BVSUZ5NTVkMmVEbUNDVjVVenJhQkdMbkU4TCt4MStINDZROENBd0VBQWFPQmpqQ0JpekJxQmdOVkhSRUVZekJoZ2gxa1pXMXZMbU5sY25ScFptbGpZWFJwYjI0dWIzQmxibWxrTG01bGRJSVBkM2QzTG1WNFlXMXdiR1V1WTI5dGdoQnRZV2xzTG1WNFlXMXdiR1V1WTI5dGdoRmhibTkwYUdWeVpHOXRZV2x1TG1OdmJZY0V3S2dCQVljRXdLZ0JBakFkQmdOVkhRNEVGZ1FVUmNDdUVjNXJPYnAyd1RLVUs5SzE3bC9MT3lZd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFFRS9WTzloWlc4alE1Q1NyeDNWTEsyYlA1dnpZcVRaaVhIMUFGZVlhei9mSTdRYlBtWlg1K3U3OVJZbXd0L3lPQW5QZjhUV2VBQmI3dVNWTDV6Sm51YlNjTkZ1QkVobW1UcmNPcWJQcExoRlB6UjcvYmVOZTltOWh5cG03V1ZFRlYzdVBLTUxhd2YybUx5VW95Zy9xSWg2MlgxSVk4enVFN1pzaGZWenB0NW5UWHFVRGc2dUY3TzQ1LzlPTnNEOWszNFBIVUt4cVhScHJVL2hxaUhhM3VqeXdkT3RtVGhhN1dQQ0R2ejZMNGlRUEFKWXFnb3NlejJ6OHAyZnkvd0d3VEdOSkdoY242OVRWWXN1Ylczd2lVL0ZRdnFYdzZ3bzFRNm5ldG5RNFhIMnRKMUJlWXB3ZHpSTGdlNTcwYjN0aDRtQ0N3WEl1R0xpd01zYWVxNDVKUDg9Il0sImtpZCI6IjlkNWQzZmIwLTljN2UtNGY3NS04YzFmLTlhYzhlZjhiYTc5NyIsImFsZyI6IlJTMjU2In0.eyJyZXNwb25zZV91cmkiOiJodHRwczovL2RlbW8uY2VydGlmaWNhdGlvbi5vcGVuaWQubmV0L3Rlc3QvYS94NTA5U2FuRG5zX3dpdGhfc2luZ2xlX3NlbGZfY2VydGlmaWNhdGUvcmVzcG9uc2V1cmkiLCJhdWQiOiJodHRwczovL3NlbGYtaXNzdWVkLm1lL3YyIiwiY2xpZW50X2lkX3NjaGVtZSI6Ing1MDlfc2FuX2RucyIsInByZXNlbnRhdGlvbl9kZWZpbml0aW9uIjp7ImlkIjoiNzQ3Y2JjODAtZTIyMi00ODc5LWEyZGMtZGY4ZjFjNjk4M2RjIiwibmFtZSI6IlBJRF9TRF9KV1QiLCJwdXJwb3NlIjoiVGVzdGluZyBPcGVuSUQgQ29uZm9ybWFuY2UiLCJmb3JtYXQiOnsidmMrc2Qtand0Ijp7ImFsZyI6WyJFUzI1NiIsIkVTMzg0Il19fSwiaW5wdXRfZGVzY3JpcHRvcnMiOlt7ImlkIjoic2Qtand0LXBpZCIsImZvcm1hdCI6eyJ2YytzZC1qd3QiOnsiYWxnIjpbIkVTMjU2IiwiRVMzODQiXX19LCJjb25zdHJhaW50cyI6eyJmaWVsZHMiOlt7InBhdGgiOlsiJC52Y3QiXSwiZmlsdGVyIjp7InR5cGUiOiJzdHJpbmciLCJjb25zdCI6InVybjpldS5ldXJvcGEuZWMuZXVkaTpwaWQ6MSJ9fSx7InBhdGgiOlsiJC5naXZlbl9uYW1lIl19LHsicGF0aCI6WyIkLmZhbWlseV9uYW1lIl19LHsicGF0aCI6WyIkLmJpcnRoZGF0ZSJdfSx7InBhdGgiOlsiJC5hZGRyZXNzLnN0cmVldF9hZGRyZXNzIl19LHsicGF0aCI6WyIkLmFkZHJlc3MubG9jYWxpdHkiXX0seyJwYXRoIjpbIiQuYWRkcmVzcy5wb3N0YWxfY29kZSJdfSx7InBhdGgiOlsiJC5hZGRyZXNzLmNvdW50cnkiXX1dfSwibGltaXRfZGlzY2xvc3VyZSI6InJlcXVpcmVkIn1dfSwicmVzcG9uc2VfdHlwZSI6InZwX3Rva2VuIiwibm9uY2UiOiJYMDFrTnZGamdrS3gtLl9-IiwiY2xpZW50X2lkIjoiZGVtby5jZXJ0aWZpY2F0aW9uLm9wZW5pZC5uZXQiLCJjbGllbnRfbWV0YWRhdGEiOnsidnBfZm9ybWF0cyI6eyJ2YytzZC1qd3QiOnsic2Qtand0X2FsZ192YWx1ZXMiOlsiUlMyNTYiLCJSUzM4NCIsIlJTNTEyIiwiUFMyNTYiLCJQUzM4NCIsIlBTNTEyIiwiRVMyNTYiLCJFUzI1NksiLCJFUzM4NCIsIkVTNTEyIiwiRWREU0EiXSwia2Itand0X2FsZ192YWx1ZXMiOlsiUlMyNTYiLCJSUzM4NCIsIlJTNTEyIiwiUFMyNTYiLCJQUzM4NCIsIlBTNTEyIiwiRVMyNTYiLCJFUzI1NksiLCJFUzM4NCIsIkVTNTEyIiwiRWREU0EiXX19fSwicmVzcG9uc2VfbW9kZSI6ImRpcmVjdF9wb3N0In0.SxqaozYjE9AFzJlRz5Spl0uZ4UHIKxZpcCjK8yJYtHhE59Y8pRT6VRy-u-1ajjNZGUPjdYMy6BKLxm_-XGbMGl5UfhfenTkoZHX-sjIaw5N3wTS7XTtWiSH7F1p8I5iL5y343Me9XmDHq77QS3Ee2FXH5K9ozBzKMTTIMSYbHWy51ug6AfSa751lIf6tEEkzo0tqDJvQjIyn6MfcUKPyI0M7x1tjnR4XhwW7vM4nJPZa3Ev04C8WgqPKnu5gMffV2qTcMDidOPedc32F7zwHJVgn1HmnLToFoQGqMDKc7lPSSza4I5NawTCyuFRtQopF0krkTxTZqOUbwOM-MxW18w"; + + public const string SignedRequestObjectWithRs256AndSingleNonSelfSigned = + "eyJ4NWMiOlsiTUlJRUx6Q0NBeGVnQXdJQkFnSVVVd2wxNlBqWEV0UkMycUtoV3NWdE16czNVb0l3RFFZSktvWklodmNOQVFFTEJRQXdiREVMTUFrR0ExVUVCaE1DUkVVeER6QU5CZ05WQkFnTUJraGxjM05sYmpFU01CQUdBMVVFQnd3SlJuSmhibXRtZFhKME1SQXdEZ1lEVlFRS0RBZFVaWE4wVDNKbk1TWXdKQVlEVlFRRERCMWtaVzF2TG1ObGNuUnBabWxqWVhScGIyNHViM0JsYm1sa0xtNWxkREFlRncweU5EQTVNVGN3T0RVNU1qZGFGdzB5TlRBNU1UY3dPRFU1TWpkYU1JR0VNUXN3Q1FZRFZRUUdFd0pFUlRFUE1BMEdBMVVFQ0F3R1NHVnpjMlZ1TVJJd0VBWURWUVFIREFsR2NtRnVhMloxY25ReEdqQVlCZ05WQkFvTUVVVjRZVzF3YkdVZ1EyOXRjR0Z1ZVNBeU1Rc3dDUVlEVlFRTERBSkpWREVuTUNVR0ExVUVBd3dlWkdWdGJ5NWpaWEowYVdacFkyRjBhVzl1TG05d1pXNXBaREl1Ym1WME1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBdzZWSEZBaG1rbHNtL1VzY1NQKzZqNXNoTzZEdkhTRnBWTm13Ni8xSDRLRG9oVkJrOWhJek8rb1B4amlmcHJocTNYT2I5WEtmWmFsOG5kWXNQN3NCMmx5WDVHNXlXZFhvTlZqMkJRa29hMEZMNlVJQ0hsWnM3bnpuSmZxNlBrdzdTcjdMWW5VY1p3akFrYWFWcGlVRlJCUW5TSHZRQTZzQ3M5cVZ0NTE0QXdPSU5CNDZuOFQ2RHI1YXRpMEwzUlp2YXd6bVl1eC9jUk1jWXYzMEVpOUpVY0doNkphWGxhTUJIbWU0Y3FFQmR0c3AzVTFVQnBWS0thdk03Q3UvSitrZUF5MnBqU2Q0cnIyQ0VWaDdXbzJLQnc4dnpUaGZDREFqdHZhNU9QSkFvbGJKcnNFT0NOWUJyd1luZUc4bzlPSUVRODZJN3g1OXliOGZlS1RPSHp1OWp3SURBUUFCbzRHdk1JR3NNR29HQTFVZEVRUmpNR0dDSFdSbGJXOHVZMlZ5ZEdsbWFXTmhkR2x2Ymk1dmNHVnVhV1F1Ym1WMGdnOTNkM2N1WlhoaGJYQnNaUzVqYjIyQ0VHMWhhV3d1WlhoaGJYQnNaUzVqYjIyQ0VXRnViM1JvWlhKa2IyMWhhVzR1WTI5dGh3VEFxQUVCaHdUQXFBRUNNQjBHQTFVZERnUVdCQlJUWkNzODRvM2ViZkdHa2JMaUJncDFhYWZ4VHpBZkJnTlZIU01FR0RBV2dCU3BVeHZ4ZHVkT1g1a3lsa1M4MzYvTDVKdU9BVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBT1NGSlRzNzdCQkJDaHZYb2N4VUUrNFJWaHg0a2w2ZUJUZGxvOE91OUdjTys2OHp2aWx2czduaUpsR1RTcDJYemFEdjlwS2UwNjhodlhuQzlZSitaenNtUnBmdkhxdFMwUkd4Yko1Zis2OHl4QlEyTlFlMUJMcVFDbENVdzcwcEd6WEpoTlEzdUw0Zmx1emptRjZNcmR3cUE2eURrQ1FIWWNjd1hGWmVQOUgwZEp6TkZxaTM1Wm4zYUJWU2YwZFNCSDg2STZDbGJDMkdSWHlLTzlFOEg0NVZvem9EVi84akJXcUsvMklUaCtjelkwZ3d6aXQzUFhSNERxZVozTFY4UnJmSG04U00vR2hGcGVjdjJPN2ozbzdIblJEVkFObFVVTGNMSDYwaFFZTTlTTk1GZVQ2UVRoeTJweGFOMkFvYWJsbG1EeWRzZm93RDNDbWFVWXZqTTZRPT0iXSwia2lkIjoiMjk4MDFkYmQtN2UzNy00YjU2LWE3YzktYjEzMGNlNWUyYzkxIiwiYWxnIjoiUlMyNTYifQ.eyJyZXNwb25zZV91cmkiOiJodHRwczovL2RlbW8uY2VydGlmaWNhdGlvbi5vcGVuaWQubmV0L3Rlc3QvYS94NTA5U2FuRG5zX3dpdGhfc2luZ2xlX25vbl9zZWxmX2NlcnRpZmljYXRlL3Jlc3BvbnNldXJpIiwiYXVkIjoiaHR0cHM6Ly9zZWxmLWlzc3VlZC5tZS92MiIsImNsaWVudF9pZF9zY2hlbWUiOiJ4NTA5X3Nhbl9kbnMiLCJwcmVzZW50YXRpb25fZGVmaW5pdGlvbiI6eyJpZCI6Ijc0N2NiYzgwLWUyMjItNDg3OS1hMmRjLWRmOGYxYzY5ODNkYyIsIm5hbWUiOiJQSURfU0RfSldUIiwicHVycG9zZSI6IlRlc3RpbmcgT3BlbklEIENvbmZvcm1hbmNlIiwiZm9ybWF0Ijp7InZjK3NkLWp3dCI6eyJhbGciOlsiRVMyNTYiLCJFUzM4NCJdfX0sImlucHV0X2Rlc2NyaXB0b3JzIjpbeyJpZCI6InNkLWp3dC1waWQiLCJmb3JtYXQiOnsidmMrc2Qtand0Ijp7ImFsZyI6WyJFUzI1NiIsIkVTMzg0Il19fSwiY29uc3RyYWludHMiOnsiZmllbGRzIjpbeyJwYXRoIjpbIiQudmN0Il0sImZpbHRlciI6eyJ0eXBlIjoic3RyaW5nIiwiY29uc3QiOiJ1cm46ZXUuZXVyb3BhLmVjLmV1ZGk6cGlkOjEifX0seyJwYXRoIjpbIiQuZ2l2ZW5fbmFtZSJdfSx7InBhdGgiOlsiJC5mYW1pbHlfbmFtZSJdfSx7InBhdGgiOlsiJC5iaXJ0aGRhdGUiXX0seyJwYXRoIjpbIiQuYWRkcmVzcy5zdHJlZXRfYWRkcmVzcyJdfSx7InBhdGgiOlsiJC5hZGRyZXNzLmxvY2FsaXR5Il19LHsicGF0aCI6WyIkLmFkZHJlc3MucG9zdGFsX2NvZGUiXX0seyJwYXRoIjpbIiQuYWRkcmVzcy5jb3VudHJ5Il19XX0sImxpbWl0X2Rpc2Nsb3N1cmUiOiJyZXF1aXJlZCJ9XX0sInJlc3BvbnNlX3R5cGUiOiJ2cF90b2tlbiIsIm5vbmNlIjoiN3JmUWpWdHA1dXZYLS5ffiIsImNsaWVudF9pZCI6ImRlbW8uY2VydGlmaWNhdGlvbi5vcGVuaWQubmV0IiwiY2xpZW50X21ldGFkYXRhIjp7InZwX2Zvcm1hdHMiOnsidmMrc2Qtand0Ijp7InNkLWp3dF9hbGdfdmFsdWVzIjpbIlJTMjU2IiwiUlMzODQiLCJSUzUxMiIsIlBTMjU2IiwiUFMzODQiLCJQUzUxMiIsIkVTMjU2IiwiRVMyNTZLIiwiRVMzODQiLCJFUzUxMiIsIkVkRFNBIiwiRWQyNTUxOSIsIkVkNDQ4Il0sImtiLWp3dF9hbGdfdmFsdWVzIjpbIlJTMjU2IiwiUlMzODQiLCJSUzUxMiIsIlBTMjU2IiwiUFMzODQiLCJQUzUxMiIsIkVTMjU2IiwiRVMyNTZLIiwiRVMzODQiLCJFUzUxMiIsIkVkRFNBIiwiRWQyNTUxOSIsIkVkNDQ4Il19fX0sInJlc3BvbnNlX21vZGUiOiJkaXJlY3RfcG9zdCJ9.UFbAAw0bBspL3WI9UJ09hiZRfFl90J4RqKu89McwMGDGCsnBTYQz_qqLzFdId9qg92KFC1FytgHhvgIIBZT56oOZy559T-_Ys9hPT5fdyEQqyeo-Pqk8lAhwMvD9jo28tE_2j_7JFw6-aAKmYh9NXznoXuuwKZR2VAUO-dJPIrs3yUFT-1SC7NeEuurL2sDnZ5nJ_z20mFl6WhMW2petyccpyuyviIMnt_VJ2ZJ_YpRKb8nNNH0qH-kn5ZZJtbUambjWOud8BIk83vFMsHjea5nz6h3iuBfhVZvOwe4w2DVMV0C8FkkF4A-88SI9hZ_EwcsKnu3S29aHYfFseBwjLg"; +} diff --git a/test/WalletFramework.Oid4Vc.Tests/Oid4Vp/AuthRequest/X509SanDnsTests.cs b/test/WalletFramework.Oid4Vc.Tests/Oid4Vp/AuthRequest/X509SanDnsTests.cs new file mode 100644 index 00000000..2106837c --- /dev/null +++ b/test/WalletFramework.Oid4Vc.Tests/Oid4Vp/AuthRequest/X509SanDnsTests.cs @@ -0,0 +1,80 @@ +using FluentAssertions; +using WalletFramework.Oid4Vc.Oid4Vp.Models; +using static WalletFramework.Oid4Vc.Tests.Oid4Vp.AuthRequest.Samples.AuthRequestSamples; + +namespace WalletFramework.Oid4Vc.Tests.Oid4Vp.AuthRequest; + +public class X509SanDnsTests +{ + [Fact] + public void Valid_Jwt_Signature_Is_Accepted() + { + var requestObject = RequestObject.CreateRequestObject(SignedRequestObjectWithRs256AndTrustChain); + + var sut = requestObject.ValidateJwt(); + + sut.Should().NotBeNull(); + } + + [Fact] + public void Invalid_Jwt_Signature_Results_In_An_Error() + { + var requestObject = RequestObject.CreateRequestObject(SignedRequestObjectWithRs256AndInvalidSignature); + try + { + requestObject.ValidateJwt(); + Assert.Fail("Expected validation to fail"); + } + catch (Exception) + { + // Pass + } + } + + [Fact] + public void Trust_Chain_Is_Being_Validated() + { + var requestObject = RequestObject.CreateRequestObject(SignedRequestObjectWithRs256AndTrustChain); + + var sut = requestObject.ValidateTrustChain(); + + sut.Should().NotBeNull(); + } + + [Fact] + public void Single_Self_Signed_Certificate_Is_Allowed() + { + var requestObject = RequestObject.CreateRequestObject(SignedRequestObjectWithRs256AndSingleSelfSigned); + + var sut = requestObject.ValidateTrustChain(); + + sut.Should().NotBeNull(); + } + + [Fact] + public void Single_Non_Self_Signed_Certificate_Is_Not_Allowed() + { + var requestObject = RequestObject.CreateRequestObject(SignedRequestObjectWithRs256AndSingleNonSelfSigned); + + try + { + requestObject.ValidateTrustChain(); + Assert.Fail("Expected validation to fail"); + } + catch (Exception) + { + // Pass + } + + } + + [Fact] + public void Checks_That_San_Name_Equals_Client_Id() + { + var requestObject = RequestObject.CreateRequestObject(SignedRequestObjectWithRs256AndTrustChain); + + var sut = requestObject.ValidateSanName(); + + sut.Should().NotBeNull(); + } +}