Digitale ondertekening IMKL-pakket

Om het risico gelinkt aan verlies van integriteit van plannen te reduceren, is het optioneel plaatsen van een digitale handtekening op de IMKL-bestanden mogelijk. Dit is een mature wijdverspreide state-of-the-art technologie.

  • Digitale handtekening garandeert integriteit van de IMKL-bestanden
  • Digitale handtekening garandeert authenticiteit van de IMKL-bestanden

IMKL-bestanden zijn in feite zip-bestanden en worden als geheel ondertekend.

De aanwezigheid en controle van een digitale handtekening op het IMKL-bestand garandeert dus de non-repudiation (of niet-weerlegbaarheid) van de KLB met betrekking tot de door de KLB correct afgeleverde plannen. Deze controle werkt end-to-end (d.w.z. van bij de KLB tot in het IMKL-archief), is onafhankelijk van de onderliggende (transport)technologie (in casu HTTPS) en compenseert dus ook (mogelijk nog onbekende) kwetsbaarheden in het onderliggende transportprotocol volgens het “defense-in-depth” principe.

De aanwezigheid van een digitale handtekening levert op zich echter voor de KLB nog geen bewijs dat Digitaal Vlaanderen de doorgestuurde plannen ook effectief correct heeft ontvangen. Om de ketting sluitend te maken, is een door Digitaal Vlaanderen digitaal ondertekende ontvangstbevestiging van de plannen aan de KLB vereist. Daarom zal Digitaal Vlaanderen het validatierapport van een digitaal ondertekend IMKL-bestand op zijn beurt digitaal ondertekenen.

Een positief validatierapport wordt daardoor een (cryptografisch) “ontvangstbewijs”, soms ook een “message disposition notification” genoemd. Dit ontvangstbewijs bevat ook de datum en het exacte tijdstip van ontvangst (UTC). Het vormt daardoor een bijkomende garantie dat het IMKL-bestand niet achteraf vervangen is en vormt dus een extra bescherming tegen antedateren. In feite wordt door dit ontvangstbewijs het principe van een aangetekend schrijven in digitale vorm geïmplementeerd.

Digitaal Vlaanderen zal in dit validatierapport de cryptografische hash (SHA-256) van het IMKL-bestand opnemen, samen met het tijdstip (UTC) van ontvangst en het validatierapport digitaal ondertekenen.

KLB’s die cryptografische zekerheid willen over de integriteit van het transport en het opgeslagen IMKL-document, kunnen de hash vergelijken met een hash die ze zelf hebben berekend voordat het document werd verstuurd en de handtekening van Digitaal Vlaanderen valideren. Als ze het IMKL-bestand digitaal ondertekend hebben, hoeven ze in principe de hash niet te vergelijken. De ondertekende bevestiging dat Digitaal Vlaanderen hun handtekening heeft gevalideerd, kan volstaan.

Het plaatsen van de digitale handtekening op het IMKL-bestand is optioneel.

Digitale ondertekening van het IMKL-pakket

Digitale ondertekening is optioneel. De digitale ondertekening van het IMKL-pakket gebeurt volgens het RSA-algoritme en de SHA-256-hashfunctie. Cfr http://en.wikipedia.org/wiki/RSA_(cryptosystem).

Een digitaal ondertekend IMKL-pakket bestaat uit een zip-bestand met daarin:

  • Eén tekstbestand met de signature (base64-gecodeerde geëncrypteerde hash) (*.txt)
  • Eén X.509-certificaat met de publieke sleutel van het certificaat dat gebruikt werd om de hash te encrypteren (*.cer).
  • Eén IMKL-pakket (*.zip)

De naam van de bestanden (met uitzondering van de bestandsextensie) is vrij te kiezen.

Voorbeeld van een digitaal ondertekend IMKL-pakket:

Digitaal Vlaanderen zal de geldigheid van het gebruikte certificaat controleren. Daartoe moet de KLB de publieke keys van het root-certificaat en de eventuele tussenliggende certificaten aan Digitaal Vlaanderen bezorgen. Dit kan via e-mail aan digitaal.vlaanderen@vlaanderen.be. Self-signed certificaten zijn niet toegelaten. Ook het subject van het gebruikte certificaat zal vergeleken worden met het subject van het certificaat zoals ingegeven op de profielpagina. Als het subject op de profielpagina ontbreekt, of het subject verschilt van het subject van het certificaat in het IMKL-pakket, dan zal het IMKL-pakket niet valideren.

U vindt het subject van het certificaat in de gedetailleerde eigenschappen van het certificaat.

Voorbeeld code (C#)

voor het berekenen van de base64-gecodeerde gesignde SHA-256-hash van een IMKL-pakket. Deze waarde komt in het *.txt bestand:

public class ImklSigner
{
public string Sign(byte[] zip, X509Certificate2 cert)
{
using (var rsa = (RSACryptoServiceProvider)cert.PrivateKey)
{
return Convert.ToBase64String(rsa.SignData(zip, "SHA256"));
}
}
}

Digitale ondertekening van het validatierapport

De validatierapporten van IMKL-pakketten worden door Digitaal Vlaanderen digitaal ondertekend. Dergelijke validatierapporten bevatten ook het tijdstip van ontvangst en de cryptografische hash van het opgeladen IMKL-pakket.

Een KLB kan een validatierapport opvragen via volgende URI: ws/klip/v2/Imkl/Report/MapRequest/{mapRequestId}/UnaZone/{unaZoneId}

Een validatierapport in XML wordt digitaal ondertekend volgens het XML-DSig-principe. Cfr. http://en.wikipedia.org/wiki/XML_Signature.

Er bestaat momenteel nog geen internationale standaard voor het digitaal ondertekenen van JSON-berichten. Vandaar dat Digitaal Vlaanderen zijn eigen formaat heeft gedefinieerd.

Voorbeeld van een digitaal ondertekend validatierapport in XML:

<ImklReport xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://api.agiv.be/ws/klip/v1">
<DateReceived>2015-10-26T08:43:20.107Z</DateReceived>
<SignatureValue>XtOnuzGYsVmqUFmXbRg3zKXs6/6lkivNERBNfEB3MrMi0iUE97xB+KwDcA1DaCmV7f9XfOWQkyBVKyyYuf2BhHoe2y8gNW8rQlr42VpuCBlnHZ/oYDWvD/rbkYbosc3sPVikh83a9qhy+BaNuRDB53TmK1C4y6kCaoVNe1Ip0qjn2tUY5TvuzePoif91qxT+VLaVxS7TAuwT97bf0iljHi6naVCt22KPCD/VfQjM5Z2lNCFaLAWYpXz6Xgwg6xnduhQCjOW11H2mNgvt1Ou/gFm1b/cw7VvCDNkIIwwRZ+tRTCDiJU1Sbjh+jpKUOdsJI9h1Ig+E3eDv+9ZloZbe8A==</SignatureValue>
<CertificateThumbprint>2081c1d735b2f224d61c13f7e29e3b056afa2529</CertificateThumbprint>
<MapRequestId>1a540b0f-8536-47d2-90af-28333be7ab66</MapRequestId>
<ZoneId>e444932b-4a04-4aed-bdf7-2e1f70d47cab</ZoneId>
<Status>https://klip.vlaanderen.be/api/cl/klip/v2/ImklStatus/datavalid</Status>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<DigestValue>qVmokZ+v9HWvkti/NABuQpwsIj3nf1mNsc9ymCDRjjc=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>YMH3hEPHL8sH1sgyjsjmxeCGOJhJW0kyNxAFyK8kOQSS9WBeDi7Oa7GNgY2dEGhaWf3ijrYJzKNzOTki+0CCbc2OHNSSmlRmcTJrlgmjqUzjhY6+pFJt3gTEh1d+fKwVW3tv0bTNKb4eMg1WO3XGf/hTEh6pqvkqaoIjAoput/GDts69iGvhuTI6cWC2rmJlJOMkYTU2rEPGLwuypRAomobmgp1TrS1vRvYG9Q/sRIGLJDmAegyGgmxEfjDRd0DDvRKCQuXtcRZNvGyZ5S3wyvdC3yR/5RAIFsk5XW9fo9/NkDSgHyK/EKA99oRZidXwcTxZfD8ZycFhquSPlLKT4Q==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>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</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</ImklReport>

Voorbeeld van een digitaal ondertekend validatierapport in JSON:

{
"DateReceived" : "2015-10-26T08:43:20.107Z",
"SignatureValue" : "XtOnuzGYsVmqUFmXbRg3zKXs6/6lkivNERBNfEB3MrMi0iUE97xB+KwDcA1DaCmV7f9XfOWQkyBVKyyYuf2BhHoe2y8gNW8rQlr42VpuCBlnHZ/oYDWvD/rbkYbosc3sPVikh83a9qhy+BaNuRDB53TmK1C4y6kCaoVNe1Ip0qjn2tUY5TvuzePoif91qxT+VLaVxS7TAuwT97bf0iljHi6naVCt22KPCD/VfQjM5Z2lNCFaLAWYpXz6Xgwg6xnduhQCjOW11H2mNgvt1Ou/gFm1b/cw7VvCDNkIIwwRZ+tRTCDiJU1Sbjh+jpKUOdsJI9h1Ig+E3eDv+9ZloZbe8A==",
"CertificateThumbprint" : "2081c1d735b2f224d61c13f7e29e3b056afa2529",
"MapRequestId" : "1a540b0f-8536-47d2-90af-28333be7ab66",
"ZoneId" : "e444932b-4a04-4aed-bdf7-2e1f70d47cab",
"Status" : "https://klip.vlaanderen.be/api/cl/klip/v2/ImklStatus/datavalid",
"Signature" : {
"SignedInfo" : {
"SignatureMethod" : {
"Algorithm" : "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
}
},
"SignatureValue" : "DbaSpEWOoestxKrZXcyX1r8WRd/yUnBy3ltPiwcJbAZT9Xt9abHlI/MKha8dCTJs7mATfiEED8LpIbAjGoRqtyLE5ASAGjR6bJKCGqkgmlD1B7A6tPJ+xkP3gt/doASjAFdKeEpFNTAFXdL5ZTn6rzmRQWW3Xmt0ZDeECVGUQnge7p9SRiOM/2yieRp3NJCSfG/JzhYv5JorY0Tgoaowfa5Hqsyw+8Nv+q7F7wwzdatovELZwfdOm9yuU9FUj8hoA5qevKAt8dP5wtmWoNjEXYwQgHw+/BXDcGlOJHnG9QctwtqEw+j+lsbLXWy3MoWQ0HP5iH6qlN2OgqwzH/7brw==",
"KeyInfo" : {
"X509Data" : {
"X509Certificate" : "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"
}
}
}
}

Voorbeeld code (C#) voor het verifiëren van een digitaal ondertekend XML-bericht:

namespace Gisvl.Klipdf.ImklReportSignatureVerifier
{
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Xml;
using System.Xml.Linq;
public class XmlSignatureVerifier
{
public bool IsValid(string xml)
{
CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
var xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = false;
xmlDocument.LoadXml(xml);
var signedXml = new SignedXml(xmlDocument);
var nodeList = xmlDocument.GetElementsByTagName("Signature");

// assert that there is only one Signature element
if (nodeList.Count != 1)
{
return false;
}

// Load the signature node.
var element = (XmlElement)nodeList[0];
signedXml.LoadXml(element);

// get the public key
var xelement = XElement.Parse(element.OuterXml);
XNamespace ns = "http://www.w3.org/2000/09/xmldsig#";
var certificate = xelement.Descendants(ns + "X509Certificate").Single();
var binaryCertData = Convert.FromBase64String(certificate.Value);
var cert = new X509Certificate2(binaryCertData);
// assert that the reference uri is an empty string
var uri = xelement.Descendants(ns + "Reference").Single().Attribute("URI");
if (!string.IsNullOrEmpty(uri.Value))
{
return false;
}

// assert that the certificate is not expired
var notBefore = cert.NotBefore;
var notAfter = cert.NotAfter;
if (DateTime.Now < notBefore || DateTime.Now > notAfter)
{
return false;
}
// assert that the certificate is an Informatie Vlaanderen certificate (beta: 2081C1D735B2F224D61C13F7E29E3B056AFA2529 ; production: D5D39A8C82FD1D68B5D86FBE94ED96AC8F8F9953)
if (cert.Thumbprint != "2081C1D735B2F224D61C13F7E29E3B056AFA2529")
{
return false;
}

// Verify the signature and if certificate is valid and not revoked and return the result
return signedXml.CheckSignature(cert, true);
}
}
}

namespace Gisvl.Klipdf.ImklReportSignatureVerifier
{
using System;
using System.Security.Cryptography;
public sealed class RSAPKCS1SHA256SignatureDescription : SignatureDescription
{
public RSAPKCS1SHA256SignatureDescription()
{
KeyAlgorithm = typeof(RSACryptoServiceProvider).FullName;
DigestAlgorithm = typeof(SHA256Managed).FullName;
FormatterAlgorithm = typeof(RSAPKCS1SignatureFormatter).FullName;
DeformatterAlgorithm = typeof(RSAPKCS1SignatureDeformatter).FullName;
}
public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
var deformatter = new RSAPKCS1SignatureDeformatter(key);
deformatter.SetHashAlgorithm("SHA256");
return deformatter;
}
public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
var formatter = new RSAPKCS1SignatureFormatter(key);
formatter.SetHashAlgorithm("SHA256");
return formatter;
}
}
}

namespace Gisvl.Klipdf.ImklReportSignatureVerifier
{
using NUnit.Framework;
public class XmlSignatureVerifierTest
{
[Test]
public void Test()
{
const string xml = "<imklreport xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://api.agiv.be/ws/klip/v1\"><datereceived>2015-10-26T08:43:20.107Z</datereceived><signaturevalue>XtOnuzGYsVmqUFmXbRg3zKXs6/6lkivNERBNfEB3MrMi0iUE97xB+KwDcA1DaCmV7f9XfOWQkyBVKyyYuf2BhHoe2y8gNW8rQlr42VpuCBlnHZ/oYDWvD/rbkYbosc3sPVikh83a9qhy+BaNuRDB53TmK1C4y6kCaoVNe1Ip0qjn2tUY5TvuzePoif91qxT+VLaVxS7TAuwT97bf0iljHi6naVCt22KPCD/VfQjM5Z2lNCFaLAWYpXz6Xgwg6xnduhQCjOW11H2mNgvt1Ou/gFm1b/cw7VvCDNkIIwwRZ+tRTCDiJU1Sbjh+jpKUOdsJI9h1Ig+E3eDv+9ZloZbe8A==</signaturevalue><certificatethumbprint>2081c1d735b2f224d61c13f7e29e3b056afa2529</certificatethumbprint><maprequestid>1a540b0f-8536-47d2-90af-28333be7ab66</maprequestid><zoneid>e444932b-4a04-4aed-bdf7-2e1f70d47cab</zoneid><status>https://klip.vlaanderen.be/api/cl/klip/v2/ImklStatus/datavalid</status><signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><signedinfo><canonicalizationmethod algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\" /><signaturemethod algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\" /><reference uri=\"\"><transforms><transform algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\" /></transforms><digestmethod algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\" /><digestvalue>qVmokZ+v9HWvkti/NABuQpwsIj3nf1mNsc9ymCDRjjc=</digestvalue></reference></signedinfo><signaturevalue>YMH3hEPHL8sH1sgyjsjmxeCGOJhJW0kyNxAFyK8kOQSS9WBeDi7Oa7GNgY2dEGhaWf3ijrYJzKNzOTki+0CCbc2OHNSSmlRmcTJrlgmjqUzjhY6+pFJt3gTEh1d+fKwVW3tv0bTNKb4eMg1WO3XGf/hTEh6pqvkqaoIjAoput/GDts69iGvhuTI6cWC2rmJlJOMkYTU2rEPGLwuypRAomobmgp1TrS1vRvYG9Q/sRIGLJDmAegyGgmxEfjDRd0DDvRKCQuXtcRZNvGyZ5S3wyvdC3yR/5RAIFsk5XW9fo9/NkDSgHyK/EKA99oRZidXwcTxZfD8ZycFhquSPlLKT4Q==</signaturevalue><keyinfo><x509data><x509certificate>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</x509certificate></x509data></keyinfo></signature></imklreport>";
var verifier = new XmlSignatureVerifier();
Assert.IsTrue(verifier.IsValid(xml));
}
}
}

Voorbeeld code (C#) voor het verifiëren van een digitaal ondertekend JSON-bericht:

namespace Gisvl.Klipdf.ImklReportSignatureVerifier
{
using Newtonsoft.Json.Linq;
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
public class JsonSignatureVerifier
{
public bool IsValid(string json)
{
var jObject = JObject.Parse(json);
// get the public key
var certToken = jObject.SelectToken("Signature.KeyInfo.X509Data.X509Certificate");
var binaryCertData = Convert.FromBase64String(certToken.Value<string>());
var cert = new X509Certificate2(binaryCertData);
// assert that the certificate is not expired
var notBefore = cert.NotBefore;
var notAfter = cert.NotAfter;
if (DateTime.Now < notBefore || DateTime.Now > notAfter)
{
return false;
}
// assert that the certificate is an AIV certificate (beta: 2081C1D735B2F224D61C13F7E29E3B056AFA2529 ; production: D5D39A8C82FD1D68B5D86FBE94ED96AC8F8F9953)
if (cert.Thumbprint != "2081C1D735B2F224D61C13F7E29E3B056AFA2529")
{
return false;
}
// verify if the certificate is valid and not revoked
if (!cert.Verify())
{
return false;
}
// get the signature
var signatureToken = jObject.SelectToken("Signature.SignatureValue");
var signature = signatureToken.Value<string>();
// remove the signature element
jObject.Remove("Signature");
var strippedjson = jObject.ToString(Newtonsoft.Json.Formatting.None);
// verify
using (var rsa = (RSACryptoServiceProvider)cert.PublicKey.Key)
{
return rsa.VerifyData(Encoding.UTF8.GetBytes(strippedjson), "SHA256", Convert.FromBase64String(signature));
}
}
}
}
namespace Gisvl.Klipdf.ImklReportSignatureVerifier
{
using NUnit.Framework;
public class JsonSignatureVerifierTest
{
[Test]
public void Test()
{
const string json = "{\"DateReceived\":\"2015-10-26T08:43:20.107Z\",\"SignatureValue\":\"XtOnuzGYsVmqUFmXbRg3zKXs6/6lkivNERBNfEB3MrMi0iUE97xB+KwDcA1DaCmV7f9XfOWQkyBVKyyYuf2BhHoe2y8gNW8rQlr42VpuCBlnHZ/oYDWvD/rbkYbosc3sPVikh83a9qhy+BaNuRDB53TmK1C4y6kCaoVNe1Ip0qjn2tUY5TvuzePoif91qxT+VLaVxS7TAuwT97bf0iljHi6naVCt22KPCD/VfQjM5Z2lNCFaLAWYpXz6Xgwg6xnduhQCjOW11H2mNgvt1Ou/gFm1b/cw7VvCDNkIIwwRZ+tRTCDiJU1Sbjh+jpKUOdsJI9h1Ig+E3eDv+9ZloZbe8A==\",\"CertificateThumbprint\":\"2081c1d735b2f224d61c13f7e29e3b056afa2529\",\"MapRequestId\":\"1a540b0f-8536-47d2-90af-28333be7ab66\",\"ZoneId\":\"e444932b-4a04-4aed-bdf7-2e1f70d47cab\",\"Status\":\"https://klip.vlaanderen.be/api/cl/klip/v2/ImklStatus/datavalid\",\"Signature\":{\"SignedInfo\":{\"SignatureMethod\":{\"Algorithm\":\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\"}},\"SignatureValue\":\"DbaSpEWOoestxKrZXcyX1r8WRd/yUnBy3ltPiwcJbAZT9Xt9abHlI/MKha8dCTJs7mATfiEED8LpIbAjGoRqtyLE5ASAGjR6bJKCGqkgmlD1B7A6tPJ+xkP3gt/doASjAFdKeEpFNTAFXdL5ZTn6rzmRQWW3Xmt0ZDeECVGUQnge7p9SRiOM/2yieRp3NJCSfG/JzhYv5JorY0Tgoaowfa5Hqsyw+8Nv+q7F7wwzdatovELZwfdOm9yuU9FUj8hoA5qevKAt8dP5wtmWoNjEXYwQgHw+/BXDcGlOJHnG9QctwtqEw+j+lsbLXWy3MoWQ0HP5iH6qlN2OgqwzH/7brw==\",\"KeyInfo\":{\"X509Data\":{\"X509Certificate\":\"MIIF+TCCA+GgAwIBAgIOAQAAAAABSR5mwERBAI0wDQYJKoZIhvcNAQELBQAwNjELMAkGA1UEBhMCQkUxFjAUBgNVBAMTDUdvdmVybm1lbnQgQ0ExDzANBgNVBAUTBjIwMTQwMjAeFw0xNDEwMTcxMzU4MTBaFw0yMDAxMTcxMzU4MTBaMIGkMSIwIAYDVQQDDBlrbGlwLnNpZ25pbmcuYWdpdi5iZS9iZXRhMQswCQYDVQQGEwJCRTEfMB0GCSqGSIb3DQEJARYQc2VjdXJpdHlAYWdpdi5iZTENMAsGA1UEBwwER2VudDEYMBYGA1UECAwPT29zdC1WbGFhbmRlcmVuMRIwEAYDVQQLDAlJVC1EaWVuc3QxEzARBgNVBAoMCjA4ODE3NDY1MjkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDMR4iZqa/ba/ljL5hYdDgh2d682yEVy1zb2WP0MAkM5bTWvnmNagtT5B3ZWsayyCmmfL6vhbQ/Koa6rP2O+3K83Ng3uCb389HdAaspPXM6QukLhbCuF73HEqMBFs5wGBpzwcTC4XDKpSLi9CD3S96sLknXj0r8RbMw3WxZPGuOhhZoQb9WUs9aNYXV3oxBY3QvEdmajEMgmeEb/8w4WMt2fN5JBRCCeovhL5NbmmkfKCfQsx7uocXA+qvD7J5P8Vp+NSOz3T3HSv3XF+znpk5fe4UrqJMf06k63v4hPQ24/SAvmI74egZshy5ye1rDL04Z1X+0o2XcJLtnBdQ8rx5jAgMBAAGjggGUMIIBkDAfBgNVHSMEGDAWgBSO6Dkuol6Mul/B7QIppc9Aw7mbujBwBggrBgEFBQcBAQRkMGIwNgYIKwYBBQUHMAKGKmh0dHA6Ly9jZXJ0cy5wa2kuYmVsZ2l1bS5iZS9iZWxnaXVtcnM0LmNydDAoBggrBgEFBQcwAYYcaHR0cDovL29jc3AucGtpLmJlbGdpdW0uYmUvMjAJBgNVHRMEAjAAMEQGA1UdIAQ9MDswOQYHYDgMAQEDAzAuMCwGCCsGAQUFBwIBFiBodHRwOi8vcmVwb3NpdG9yeS5wa2kuYmVsZ2l1bS5iZTA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLnBraS5iZWxnaXVtLmJlL2dvdmVybm1lbnQyMDE0MDIuY3JsMA4GA1UdDwEB/wQEAwIE8DARBglghkgBhvhCAQEEBAMCBLAwHQYDVR0OBBYEFAJAEZ2JWVM1qboizHpo7jAqzyuEMCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYBBQUHAwQwDQYJKoZIhvcNAQELBQADggIBAB5aQzbgSTIH7zoPOI/JTNhSIXj7UHWs1dmow2AXgtoPToJR4/qq++/mMoHFM0R79xPrQuWrnkMKcuhsemrc21XzmSvA1FYYcCjrXHrSnffesCF1yb2g/2JiPlOYjWYeNCD279brnL0vEoMFXwGyGMvTh0lKiB3uO1R0MLhmdMkavi6iQ2XDTO/RC1lgC4x8gcPituldM7mrbxwNbnYV3VP063AmeA9vgwrgqbhbTLrE5rVZ1PbS0EBtoiTLIhUSLIsY+CQf2MNMHR00gwnv8cUSErO4te5oJEWjKiGd0aiSW6wbbVJSzn97kafmk/lB7/ZiEKyl0xYHJ+8cegSRxPkQN1VvHIj8WhO5zLpMxRh0xfW5XIo0yPLCMBz1PDMlWQOk7LHE81DqpzJgwifuHSGlnuLNwmSmYr6ntBA9Y/9gKkBHtmrLa5c3UYJ3QdGEE3aXQ+WKwZT6B+YkPAKH+LPH0MSeI6Gu7YeSHbe3Znczl2e88unlWt4uy/kGURaG5bPwlv3g4lU2R62kSK/EyrNG3wSItfODEocgjJwoP62RydOhqrugRMDulPVREyXWVyWWQng3V0vxbeXyeeOfonXzFaDSQh7Yx1H+44FukCbhtDH/fqYuy52QprllinNaSTjC7aA0CKKWmDaCOdeaQZ/TBCwJJt/XCJoTfDUVgOdU\"}}}}";
var verifier = new JsonSignatureVerifier();
Assert.IsTrue(verifier.IsValid(json));
}
}
}