Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Inleiding

Het aanbod van MAGDA werd begin 2020 uitgebreid met REST diensten. Voor deze REST diensten wordt gebruik gemaakt van een token-gebaseerd authenticatie systeem.
Token-gebaseerde authenticatie laat toe om authenticatie informatie naadloos te laten doorstromen van het ene systeem naar een andere. Ook multi-partijen scenario’s kunnen hiermee gemakkelijker ondersteund worden. Bijvoorbeeld een scenario met een resource server (MAGDA) die informatie (of resources) ter beschikking stelt, een opdrachtgever (de entiteit die de recht heeft om de informatie te raadplegen) en een leverancier (de entiteit die de opvraging uitvoert in naam van de opdrachtgever).

Definities

Een token is een stuk informatie dat kan gebruikt worden om een identiteit en/of een recht te representeren.
Tokens kunnen ofwel onleesbaar (opaque) of leesbaar zijn.
Opaque tokens zijn een niet interpreteerbaar, ruw stuk informatie. Om ze te gebruiken is er altijd een derde partij (meestal de autorisatie provider) nodig om te weten als het token geldig is of aan welke toegang of identiteit het token is gelinkt.
Leesbare tokens zijn tokens die informatie bevatten over identiteit en/of autorisatie. JSON Web Tokens (JWTs) zijn leesbare tokens.

Context

MAGDA maakt gebruik van de AIV Authenticatie en Autorisatie Server (AAS) voor het authentiseren en autoriseren van afnemers in het geval van token authenticatie.

Werken met tokens omvat twee fases:

  1. Het krijgen van een access token van een authenticatie en autorisatie server

  2. Het gebruiken van een access token om een oproep naar een resource server te doen (bv een MAGDA dienst)

De access tokens die uitgekeerd worden door de AIV AAS zijn opaque tokens. Tijdens het proces om ze te krijgen wordt er gebruik gemaakt van een JWT (aanvraag token).

Stappen 1 en 2 maken deel uit fase 1: het krijgen van een access token.

Stappen 3 en 4 maken deel uit fase 2: het opvragen van MAGDA resource / diensten

Magda is ook de “resource server”.

Fase 0: Opzet bij de Token provider van de Vlaamse Overheid (AIV AAS)

Alvorens er gestart kan worden, dient de afnemer opgezet zijn bij de token provider van de Vlaamse Overheid (AIV AAS).

Wanneer dat gebeurd is, ontvangt de gemandateerde gebruiker het recht om de publieke sleutels te beheren bij AIV AAS.

Hij of zij dient dan de publieke sleutel van het certificaat dat gebruikt zal worden, 1-malig op te laden (via de user interface van AIV AAS).

Bijkomende informatie over hoe dat dient te geburen kan u vinden op: https://beta.oauth.vlaanderen.be/authorization/Help/Api/ClientCredentialsGrant

Fase 1: Het krijgen van een access token

Authenticatie en autorisatie aanvraag aanmaken

De afnemer stuurt een request met 1-Way SSL naar de AIV AAS om een access token te krijgen.

Deze request moet een aanvraag token bevatten. Dit aanvraag token is een JWT token. Dit procedure volgt de RFC 7523: “JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants”.

Dit token wordt getekend door een de private key die overeenstemt met het VO DCB certificaat met een door MAGDA erkende Common Name. Deze Common Name krijgt de afnemer van MAGDA tijdens het aansluitingsproces.

Er zijn twee AA servers, een voor TNI en een andere voor productie:

JWT token aanmaken

Het JWT aanvraag token moet het volgende bevatten:

“iss” issuer eigenschap

de client id nummer die aan de afnemer gecommuniceerd is na de accounts aangemaakt zijn

“sub” subject eigenschap

moet ook met de client id ingevuld worden

“aud” eigenschap

audience van de request, moet altijd de URI van het token endpoint zijn, zie hierboven gemeeld lijst.

“exp” expiry eigenschap

het moment vanaf wanneer het token niet meer geldig zal zijn

“iat” issued at eigenschap

het moment waarop het token is aangemaakt

“jti” token id

een uniek nummer voor de aanvraag. (om “replay attacks” voort te komen)

Voorbeeld:

Code Block
languagejson
{
"iss": "3318",
"sub": "3318",
"aud": "https://beta.oauth.vlaanderen.be/authorization/ws/oauth/v2/token",
"exp": "1587979731",
"iat": "1587979431",
"jti": "19017aeb-3673-4f04-8ef7-e0756b80a667"
}

Tekenen van het JWT token

Het JWT aanvraag token voor de access token aanvraag moet getekend zijn met de private key die overeen stemt met het VO DCB certificaat. Tekenen van JWT volgt de JWS standard (RFC 7515).

De AIV AAS ondersteunt de “x5c”, “x5u”, “jwk”, “jku” eigenschappen van JWS niet. Dit betekent dat het certificaat dat gebruikt wordt om te tekenen, niet bij het token mag zitten. Dat De public key van het certificaat moet op voorhand opgeladen worden door de afnemer in Geosecure. Dit kan via de volgende link: https://beta.oauth.vlaanderen.be/admin/ (voor TNI) en https://oauth.vlaanderen.be/admin (voor Productie). Hier kan je bij de desbetreffende OAuth client via de link 'Client certificaat toevoegen' de publieke sleutel van je VO-DCB certificaat opladen.

De getekende JWT moet in de “client_assertion”.

Aanvraag HTTP request

De request moet een HTTP POST request zijn met x-www-form-urlencoded parameters. De volgende parameters moeten ingevuld worden:

  • “grant_type”: “client_credentials”

  • “scope”: moet ingevuld worden met een lijst van scopes waaraan het access token toegang gaat geven. Er wordt een scope per dienst gebruikt. Dus voor elke MAGDA dienst die men wil aanroepen, moet hier de nodige scope aangevraagd worden. “scopes” worden door middel van een spatie van elkaar afgescheiden.

  • “client_assertion_type”: “urn:ietf:params:oauth:client-assertion-type:jwt-bearer”

  • “client_assertion”: het JWT token beschreven in het vorige punt

Voorbeeld:

Code Block
POST https://beta.oauth.vlaanderen.be/authorization/ws/oauth/v2/token
Content-Type: application/x-www-form-urlencoded
User-Agent: PostmanRuntime/7.22.0
Accept: */*
Cache-Control: no-cache
Host: beta.oauth.vlaanderen.be
Accept-Encoding: gzip, deflate, br
Content-Length: 855
Connection: keep-alive

grant_type=client_credentials&scope=msg_statuses_v1_G%20msg_mailbox_v1_P%20msg_msg_v1_P&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzMzE4Iiwic3ViIjoiMzMxOCIsImF1ZCI6Imh0dHBzOi8vYmV0YS5vYXV0aC52bGFhbmRlcmVuLmJlL2F1dGhvcml6YXRpb24vd3Mvb2F1dGgvdjIvdG9rZW4iLCJleHAiOiIxNTk4NTMyOTQzIiwiaWF0IjoiMTU5ODUzMjY0MyIsImp0aSI6IjI2MWQ4NmQ4LTVhY2MtNGNiNS1hMGZkLWEyYTNkZDJmY2I5NCJ9.W5dEZa5RDZ1U250Sm65qkvd5bW6O1ZeZWW8W_RjfKcNg0hSAXOXdJKP8TXnFlqpWswhb8OGYMDS-6YcSVpyrV6roD3FyioPqpaxFkFe1xzanoF6KZTMYA4TTkYROMZkG1U166ZQf7y09S5CekeNzfGF9ORk1toEiROksVZa-inTe46ioJSdVzUf8t-IRXPWR-ucHO9A8IXEQ-dwCrzJ_f1dq24R8eZsiZmplx_nXtfQ9yXD3XPDu-NZiSLY0TKexqPdd4uvlrdzoFe6O8usMkhpBfsC3inUCzMfKT30rk1uJvvKH2s0iYpUp-FZCCn-0unWATwivWhMkvBEVZOWbGA

Antwoord van de Authenticatie en Autorisatie Server (AAS)

Als de authenticatie aanvraag correct verlopen is, krijgt de aanvrager een JSON payload terug met het access token.

Code Block
languagejson
{
    "access_token": "POtyWHuQd94pk6AtHbew3B==",
    "scope": "msg_statuses_v1_G msg_mailbox_v1_P msg_msg_v1_P",
    "expires_in": 57599,
    "token_type": "Bearer"
}

De eigenschap “access_token” bevat het token voor de call naar elke MAGDA REST dienst (resource).

Access tokens worden uitgereikt met een bepaalde levensduur. Die levensduur wordt meegeven in het “expires_in” veld (uitgedrukt in seconden). Momenteel bedraagt die 16 uur, maar het staat onze token provider vrij dit interval eenzijdig aan te passen.

We raden aan om een token maximaal te hergebruiken en pas kort voor het einde van haar levensduur, een nieuw exemplaar aan te vragen. Dit verlaagt immers de load naar onze token provider. Bovendien vermijdt u op die manier dat onze token provider u geen tokens meer zal toekennen (momenteel ligt die grens op maximaal 2000 token bevragingen per uur per afnemer).

Wanneer u een token gebruikt waarvan de levensduur verstreken is, zal u van MAGDA een 401 UNAUTHORIZED fout terug krijgen. U zal dan een nieuw token moeten aanvragen om verdere requests naar MAGDA te kunnen uitvoeren.

Code voorbeeld

In de volgende voorbeelden wordt er van Apache Http Components (HttpClient) gebruik gemaakt om de HTTP Call uit te voeren en van jose4j voor het aanmaken van de JWT. Andere libraries zullen op een gelijkaardige manier werken.

Voor C# verwijzen we naar deze JSON Web Token .

Het bouwen van de getekend JWT met jose4j:

Code Block
languagejava
private static String buildClientAssertion(String clientId, PrivateKey key) throws JoseException {
        JwtClaims claims = new JwtClaims();
    	claims.setIssuer(clientId);
    	claims.setAudience(AIP_AUDIENCE);
    	claims.setExpirationTimeMinutesInTheFuture(2.0f);
    	claims.setIssuedAtToNow();
    	claims.setGeneratedJwtId();
    	claims.setSubject(clientId);
    	
    	JsonWebSignature signature = new JsonWebSignature();
    	signature.setPayload(claims.toJson());
    	signature.setKey(key);
    	signature.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
    	signature.setContentTypeHeaderValue("JWT");
    	    	
    	return signature.getCompactSerialization();
}

Voor het aanmaken van een HttpClient http request:

Code Block
languagejava
private static HttpPost buildAccessTokenRequest(String scopes, String signedJWT) throws UnsupportedEncodingException {
    	HttpPost post = new HttpPost(AIP_TOKEN_PROVIDER);
    
    	ArrayList<BasicNameValuePair> retVal = new ArrayList<BasicNameValuePair>();
    	
    	retVal.add(new BasicNameValuePair("grant_type", "client_credentials"));
    	retVal.add(new BasicNameValuePair("scope", scopes));
    	retVal.add(new BasicNameValuePair("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"));
    	retVal.add(new BasicNameValuePair("client_assertion", signedJWT));
    	
    	post.setEntity(new UrlEncodedFormEntity(retVal));
    	
    	post.setHeader("Accept", "application/json");
    	
    	return post;
}

Daarna kan de request verstuurd worden:

Code Block
languagejava
public static String getAccessToken(PrivateKey authenticationKey, String scopes[]) throws JsonParseException, JsonMappingException, UnsupportedOperationException, IOException, JoseException {
    	HttpClient c = HttpClientBuilder.create().build();
    	
    	String scopeStr = String.join(" ", scopes);
    	String signedJWT = buildClientAssertion("3318", authenticationKey);
    	
    	HttpPost target = buildAccessTokenRequest(scopeStr, signedJWT);
    	HttpResponse resp = c.execute(target);

    	assertEquals(200, resp.getStatusLine().getStatusCode());
    	ObjectMapper mapper = new ObjectMapper();
    	LiveTest.AccessTokenResponse token = mapper.readValue(resp.getEntity().getContent(), LiveTest.AccessTokenResponse.class);
    	
    	return token.access_token;
}

Als het request correct is, antwoord de AAS met een antwoord zoals beschreven in 4.2

Fase 2: Het opvragen van Magda resource

Authenticatie

Voor elke MAGDA resource “request” is een geldig access token nodig, dat de afnemer in Fase 1 verkregen heeft.

Het access token wordt meegegeven in de “Authorization” HTTP header. De token wordt voorafgegaan door de prefix “Bearer““Bearer “.

Code Block
POST https://.........................../api/v1/messages/messages
Authorization: Bearer POtyWHuQd94pk6AtHbew3B==

Maximal Load

Opmaken van een Digest

De digest is een hash van de gehele payload van de request zoals in RFC 3230 Instance Digests for HTTP en IETF draft-ietf-httpbis-digest-headers-00 gedefinieerd.

HTTP headers, query parameters, domein naam en end point vormen geen onderdeel van de digest.

U kan kiezen tussen 2 hashing algoritmen: SHA-256 of SHA-512.

De digest dienst BASE64 encoded te worden en dan meegegeven te worden in de “Digest” HTTP parameter. Hij wordt voorafgegaan door een prefix die aangeeft welk hashing algoritme gebruikt werd:

  • “SHA-256=”, indien er gebruik gemaakt werd van SHA-265 om de hash te berekenen

  • “SHA-512=” indien er gebruik gemaakt werd van SHA-512 om de hash te berekenen

Code Block
POST https://.........................../api/v1/messages/messages
Digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=

Request signing

Om bericht integriteit te garanderen over meerdere technische en functionele integratie lagen, wordt met HTTP signing gewerkt. HTTP Signing is een draft IETF standard.

HTTP signing wordt goed ondersteund in Java via de Apache CXF library. Er bestaat ook een library voor .Net (zie laatste hoofdstuk).

Deze HTTP signing gebruikt het VO DCB certificaat met een door MAGDA erkende Common Name.

Meer info over signing in het volgende hoofdstuk Message Signing

Maximal Load

Om een maximale dienstverlening te kunnen garanderen gebruikt Magda throttling. Bij een overtreding tegen 1 van de 4 onderstaande regels: zal MAGDA dan ook een 429 response geven:

  • Maximum calls per domein: 18000 transacties per minuut

  • Maximum calls per dienst: 2400 transacties per minuut

  • Maximum calls per afnemer: 1800 transacties per minuut

  • Maximum calls per afnemer per dienst: 1800 transacties per minuut

Message Signing

Message Signing verwijst naar het tekenen van berichten om hun integriteit te garanderen. Het tekenen van het bericht met een certificaat helpt de oorsprong van het bericht te identificeren. Deze message signing met certificaat is enerzijds nodig als beveiliging bovenop het TLS kanaal. Anderzijds biedt dit de mogelijkheid om de HTTP berichten door meerdere kanalen te laten gaan zonder het bewijs van de identiteit van de bron te verliezen en zonder de gegarandeerde integriteit te verliezen .

Voor het tekenen van HTTP berichten bestaat er een IETF draft standaard (12de iteratie): https://tools.ietf.org/id/draft-cavage-http-signatures-12.html

Omdat REST geen standaardisatie heeft voor payload signing, is er geen officieel gestandaardiseerde implementatie (library) van de IETF draft HTTP handtekening specificatie. Er zijn daarentegen wel een aantal “open source” implementaties.

Message Signing Principes

Het principe is als volgt:

  • De verplichte kenmerken van het HTTP bericht worden verzameld:

o   Tijdstip van het request (“Date” header)

o   Digest van het bericht (“Digest” header)

o   Resource identificatie (http verb + target path), wordt hieronder “(request-target)” vernoemd.

o   De publieke sleutel die gebruikt mag worden om de signature te valideren

  • Bovenstaande kenmerken worden met een private sleutel getekend.

  • De handtekening wordt in de request als HTTP header toegevoegd  (“Signature” header), samen met een rekeningmethode die beschrijft welke kenmerken getekend werden, en in welke volgorde.

  • De publieke sleutel om de handtekening te kunnen valideren dient als een JWK (Json Web Key – een representatie van de publieke delen van het gebruikte certificaat) doorgestuurd te worden in een HTTP header (“Signature-public-key” header)

Bijvoorbeeld:

Code Block
   POST /foo HTTP/1.1
   Host: example.org
   Date: Tue, 07 Jun 2014 20:51:35 GMT
   Content-Type: application/json
   Digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=
   Content-Length: 18
   Signature: keyId="rsa-key-1",algorithm="rsa-sha256", headers="(request-target) date digest signature-public-key", signature="XX…XXX"
   Signature-public-key: {"kty":"RSA","kid":"rsa-key-1","n":"r3-......_D4iw6fDFw2mEQ", "e":"AQAB","x5c":["MIIFtjCCB.........="]}
   
   {"hello": "world"}

De “Signature” HTTP header heeft de volgende parameters:

  • keyId: de naam van de sleutel die gebruikt was. Dit is handig als er meerdere sleutels gebruikt kunnen worden.

  • algorithm: een beschrijving van het algoritme (van het tekenen) dat gebruikt is geweest. Bijvoorbeeld: rsa-sha256 of rsa-sha512.

  • headers: de lijst van de kenmerken die gebruikt zijn om de signing string op te maken. Deze zijn altijd HTTP headers, behalve voor “(request-target)” die de target url vertegenwoordigd.

  • signature: de handtekening zelf, als Base64 gecodeerd.

In de context van MAGDA, zullen de asymmetrische sleutels van een VO-DCB Signing certificaat moeten komen (voor entiteiten die niet onder VODCB scope vallen zullen ze uitzonderlijk op voorhand moeten gecommuniceerd worden). Op die manier zullen de sleutels duidelijk erkend worden en zal ook de mogelijkheid bestaan om een revoke van de certificaat te doen. De geldigheid van de certificaat wordt ook gecontroleerd.

Alle requests van MAGDA afnemers moeten met dit systeem tekenen, met een keyId die het Magda certificaat identificeert. Bijvoorbeeld: “AfnemerXCertificaat” (de naam van de sleutel is niet belangrijk, zolang die coherent blijft tussen de sleutel en de signature).

Om toe te laten deze verificatie te doen zonder het certificaat op voorhand te moeten uitwisselen, zal het certificaat mee in de request moeten staan.

Java code voorbeeld

Met Apache HttpClient component en tomitribe.http-signatures-java:

Code Block
languagejava
public static void httpSign(HttpEntityEnclosingRequestBase request, PrivateKey signingKey, X509Certificate certificate) throws UnsupportedOperationException, NoSuchAlgorithmException, IOException, JoseException {
    	computeSHA256Digest(request);
    	request.addHeader("signature-public-key", wrapCertificateInJWK(certificate, "key1"));
    	
    	final Signature signature = new Signature("key1", "rsa-sha256", null, "(request-target)", "date", "digest", "signature-public-key");
 
    	final Signer signer = new Signer(signingKey, signature);
    	final Signature signed = signer.sign(request.getMethod(), request.getURI().getPath().toString(), extractHeadersToSign(request));
    	
    	request.addHeader("Signature", signatureToHeaderFormat(signed));
}

static String signatureToHeaderFormat(Signature s) {
    	return "keyId=\"" + s.getKeyId() + '\"' +
                ",algorithm=\"" + s.getAlgorithm().getPortableName() + '\"' +
                ",headers=\"" + Join.join(" ", s.getHeaders()) + '\"' +
                ",signature=\"" + s.getSignature() + '\"';
}

private static Map<String,String> extractHeadersToSign(AbstractHttpMessage message) {
    	HashMap<String,String> result = new HashMap<String,String>();
    	
    	result.put("date", message.getFirstHeader("date").getValue());
    	result.put("digest", message.getFirstHeader("digest").getValue());
    	result.put("signature-public-key", message.getFirstHeader("signature-public-key").getValue());
    	
    	return result;
}

private static computeSHA256Digest(HttpEntityEnclosingRequestBase request) {
    	byte[] payload = getByteArrayFromEntity(message.getEntity());
    	final byte[] digest = MessageDigest.getInstance("SHA-256").digest(payload);
    	final String digestHeader = "SHA-256="+new String(Base64.getEncoder().encodeToString(digest));
    	message.addHeader("digest", digestHeader);
}

/**
  * This helper method exists in replacement of HttpEntity.getContent, because some implementation
  * (for instance MultipartFormEntity) throw an exception for payloads bigger than 25kb.
  * @param entity
  * @return a byte array representing the body of the request
  * @throws IOException
  */
private static byte[] getByteArrayFromEntity(HttpEntity entity) throws IOException {
	final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
	entity.writeTo(outStream);
	outStream.flush();
	byte [] result =  outStream.toByteArray();
	outStream.close();
	return result;
}

Met Apache CXF

Code Block
languagejava
public Response getSourceResponse(String address) {
    MessageSigner messageSigner = new MessageSigner(keyId -> privateSignKey, "rsa-key-1");
    signatureFilter.setMessageSigner(messageSigner);
    signatureFilter.setDigestAlgorithmName("SHA-256");
    signatureFilter.setAddDigest(true);

    WebClient webClient = WebClient.create(address, Collections.singletonList(signatureFilter));
    webClient.getConfiguration().getOutInterceptors().add(new TLSInterceptor());
    webClient.header("Content-Type", "application/json");
    webClient.header("Accept", "application/json");
    webClient.header("Date", httpDateFormater.format(Calendar.getInstance().getTime());

    Response response = webClient.get();

    return response;
}

Met Apache CXF is het zeer eenvoudig, het is genoeg om een “signature filter” toe te voegen aan de WebClient.

Publieke sleutel voor verificatie van de handtekening

MAGDA stelt een asymmetrisch signing protocol voor. Dit betekent dat er alleen nood is aan de publieke sleutel om de handtekening te valideren.

De gemakkelijkste manier om publieke sleutels te verspreiden zijn de X.509 Certificaten. Zij laten toe om de identiteit van de sleuteldrager te verzekeren.

Voor het tekenen van MAGDA request wordt er gevraagd om met VODCB uitgegeven certificaten te werken. Op deze manier zijn de certificaten automatisch door MAGDA vertrouwd en moeten zij niet op voorhand gekend zijn (door b.v. ze op voorhand te installeren in onze systemen). Daardoor is ook vernieuwing van het certificaat eenvoudiger.

Die certificaat moet minimum de volgende key-usage hebben: (zoals in sectie 4.2.1.3 van RFC 5280 PKIX Certificate and CRL Profile gedefinieerd):

  • digitalSignature (bit 0)

  • nonRepudiation (bit 1)

De certificaat maag andere key usage hebben boven de hierboven vermelde en verplichte usage.

Om de handtekening ter alle tijden te kunnen valideren, wordt het certificaat mee gegeven met elke request. Dit gebeurt door het certificaat in een JSON Web Key (JWK) in de “signature-public-key” te steken.

Het JWK formaat laat toe om X.509 Certificaten en sleutels te sturen. In ons geval is het verplicht om het certificaat in de JWK te steken (door de x5c eigenschap te gebruiken).

De JWK moet ook een “keyId” bevatten en die moet met de “keyId” van de signature overeenkomen. Dus als de handtekening verwijst naar een sleutel met id “magda” dan moet de JWK keyId ook “magda” zijn. Als een sleutel met overeenkomende id niet gevonden kan worden, gaat de signing validatie falen.

Info

Opmerking: Zolang de berichten worden getekend met dezelfde sleutel, is de publieke sleutel ook altijd dezelfde. Dus de inhoud van de “signature-public-key” blijft ook constant.

Java code voorbeeld

Dit voorbeeld gebruikt de Jose4j library.

Code Block
languagejava
public static String wrapCertificateInJWK(X509Certificate cert, String keyName) throws JoseException {
    	PublicJsonWebKey jwk =
                PublicJsonWebKey.Factory.newPublicJwk(cert.getPublicKey());
    	
    	jwk.setCertificateChain(cert);
    	jwk.setKeyId(keyName);
 			
    	return jwk.toJson();
}

Dit functie geeft de inhoud terug die in het “signature-public-key” header moet staan

Code Block
signature-public-key: {"kty":"RSA","kid":"key1","n":"r3-......_D4iw6fDFw2mEQ", "e":"AQAB","x5c":["MIIFtjCCB.........="]}

Antwoord signing

MAGDA tekent alle haar antwoorden met het eigen MAGDA certificaat.

Het antwoord van MAGDA wordt met het MAGDA certificaat getekend. Bijvoorbeeld: keyId=”magda-response-signing-key”.

Bijkomende informatie

HTTP message signing implementaties

  • Voor Java

    • Apache CXF: dit implementatie is door MAGDA getest en bevestigd te werken.

    • Tomitribe “http-signatures-java”: deze implementatie is door MAGDA getest en bevestigd dat deze correct werkt. Opmerking: opletten op validatie van “date” en “digest” headers: deze zit niet in de library en moeten expliciet door de client in de validatie headers toegevoegd worden.

  • In Javascript

    • Inspiratie zou kunnen genomen van dit Postman script, die momenteel alleen HMAC signing ondersteunt, maar zou kunnen uitgebreid worden om RSA signing te ondersteunen. Indien nodig kan MAGDA zo’n implementatie ter beschikking stellen.

  • In C# is de aanbevolen library de volgende:

View file
nameWebUtils.cs
View file
nameAsyncHelpers.cs

C# Code Voorbeelden

Aanmaken van “signature-public-key” header

De “signature-public-key” is een JWK object met een ingebouwde certificaat. Hier is een code voorbeeld om die in C# aan te maken:

Code Block
languagec#
private string WrapCertificateInJWK(X509Certificate2 certificate, string keyName)
{
       var x509SigningKey = new X509SigningCredentials(certificate);
       var x509Key = new X509SecurityKey(certificate);
       var cert64 = Convert.ToBase64String(x509Key.Certificate.RawData);        

       var pubKey = x509Key.PublicKey as RSA;

       var parameters = pubKey.ExportParameters(false);
       var exponent = Convert.ToBase64String(parameters.Exponent);
       var modulus = Convert.ToBase64String(parameters.Modulus);
       var webKey = new JsonWebKey
       {
                Kty = "RSA",
                Use = "sig",
                Kid = keyName,
                X5t = x509Key.Certificate.Thumbprint,
                E = exponent,
                N = modulus,
                Alg = x509SigningKey.Algorithm
       };
       webKey.X5c.Add(cert64);
       return JsonConvert.SerializeObject(webKey, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }});
}

Signature header aanmaken met “HttpMessageSigning” van David Lievrouw

Code Block
languagec#
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
    try
    {
        var magdaCertificate = _certificateLocator.LocateCertificate(_magdaRestConfiguration.ClientCertificateThumbprint);
        var magdaJsonWebKey = WrapCertificateInJWK(magdaCertificate, KeyId);
        var currentAuthorizationHeader = request.Headers.Authorization;
        request.Headers.Add(SignaturePublicKeyHeaderName, magdaJsonWebKey);
        var publicKeyParams = magdaCertificate.GetRSAPublicKey().ExportParameters(false);
        var privateKeyParams = magdaCertificate.GetRSAPrivateKey().ExportParameters(true);
        var signatureAlgorithm = new RSASignatureAlgorithm(HashAlgorithmName.SHA512, publicKeyParams, privateKeyParams);
        var requestSigner = _requestSignerFactory.Create(KeyId, new SigningSettings
            {
                 SignatureAlgorithm = signatureAlgorithm,
                 DigestHashAlgorithm = HashAlgorithmName.SHA512,
                 EnableNonce = false,
                 Headers = new[]
                 {
                    (HeaderName) "signature-public-key"
                 },
                 UseDeprecatedAlgorithmParameter = true
             });
        await requestSigner.Sign(request);
        request.Headers.Add(SignatureHeaderName, request.Headers.Authorization.Parameter);
        request.Headers.Authorization = currentAuthorizationHeader;
    }
    catch (Exception e)
    {
         _logger.LogError(e, e.Message);
    }
    return await base.SendAsync(request, cancellationToken);
}

Python code voorbeeld

Maakt gebruikt van de hierboven vermeld “requests-http-signature” Python library.

Code Block
languagepy
# 'date' and 'digest' headers are automatically added and computed by the library. headers = { "(request-target)", "signature-public-key", "date": datetime.now().strftime("%a, %d %h %Y %H:%M:%S %Z CEST") } auth = HTTPSignatureHeaderAuth(key_id='20FCD0294BF63B2A5E86E9020CF14C18FF03F490', key=secret, algorithm='rsa-sha512',headers=headers) request.get(url, auth=auth)

Om een maximale dienstverlening te kunnen garanderen gebruikt Magda throttling. Bij een overtreding tegen 1 van de 4 onderstaande regels: zal MAGDA dan ook een 429 response geven:

  • Maximum calls per domein: 18000 transacties per minuut

  • Maximum calls per dienst: 2400 transacties per minuut

  • Maximum calls per afnemer: 1800 transacties per minuut

  • Maximum calls per afnemer per dienst: 1800 transacties per minuut

Antwoord signing

MAGDA tekent alle haar antwoorden met het eigen MAGDA certificaat.

Het antwoord van MAGDA wordt met het MAGDA certificaat getekend. Bijvoorbeeld: keyId=”magda-response-signing-key”.

Table of Contents