We strongly recommend that service providers confirm all communications that take place with Auðkenni's system.
What do I need to confirm?
This can be roughly divided into two:
Verify that the responses are coming from our servers
Confirm signatures and certificates that are in the responses from Auðkenni’s system
To verify that responses are coming from our servers
To verify that the responses are coming from us, there is a so-called ".wellknown" endpoint that you can use to access information. Inside this endpoint, you can find the "jwks_uri" which is a path to the keys that can be used to verify that the response you receive is from our server.
An example of a .wellknown endpoint (replace "pfzww" with your Base URL):
https://pfzww.audkenni.is/sso/oauth2/realms/root/realms/audkenni/.well-known/openid-configuration
This endpoint will give you a response similar to this:
{"request_parameter_supported":true,"introspection_signing_alg_values_supported":["ES384","PS384","ES256","PS256","PS512","EdDSA","HS512","RS384","RS256","RS512","HS256","ES512","HS384"],"introspection_encryption_alg_values_supported":["RSA-OAEP-256","ECDH-ES+A256KW","A128KW","A192KW","RSA-OAEP","ECDH-ES+A192KW","A256KW","ECDH-ES","ECDH-ES+A128KW","dir"],"claims_parameter_supported":false,"introspection_endpoint":"https://pfzww.audkenni.is:443/sso/oauth2/realms/root/realms/audkenni/introspect","check_session_iframe":"https://pfzww.audkenni.is:443/sso/oauth2/realms/root/realms/audkenni/connect/checkSession","scopes_supported":["signature","openid","profile"],"backchannel_logout_supported":true,"issuer":"https://pfzww.audkenni.is:443/sso/oauth2/realms/root/realms/audkenni","id_token_encryption_enc_values_supported":["A256GCM","A128GCM","A256CBC-HS512","A128CBC-HS256","A192CBC-HS384","A192GCM"],"acr_values_supported":["nexus","sim-auth","app-auth","app-certificate-choice","nexus-sign","default","newcards-auth","app-sign","app-sign-with-certificate","apidefault","sim-sign","sim","sim-sign-pkcs1","oldcards-auth"],"userinfo_encryption_enc_values_supported":["A256GCM","A128CBC-HS256","A192CBC-HS384","A192GCM","A128GCM","A256CBC-HS512"],"authorization_endpoint":"https://pfzww.audkenni.is:443/sso/oauth2/realms/root/realms/audkenni/authorize","request_object_encryption_enc_values_supported":["A128GCM","A256GCM","A192CBC-HS384","A256CBC-HS512","A128CBC-HS256","A192GCM"],"introspection_encryption_enc_values_supported":["A128CBC-HS256","A192CBC-HS384","A256GCM","A256CBC-HS512","A128GCM","A192GCM"],"rcs_request_encryption_alg_values_supported":["RSA1_5","dir","A192KW","RSA-OAEP-256","RSA-OAEP","A256KW","A128KW"],"claims_supported":["profile","name","locale"],"userinfo_signing_alg_values_supported":["ES256","HS512","ES512","HS384","RS256","ES384","HS256"],"rcs_request_signing_alg_values_supported":["RS512","PS384","PS256","HS256","HS384","ES512","RS256","RS384","HS512","ES384","ES256","PS512"],"token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","self_signed_tls_client_auth","tls_client_auth","none","client_secret_basic"],"tls_client_certificate_bound_access_tokens":true,"backchannel_logout_session_supported":true,"token_endpoint":"https://pfzww.audkenni.is:443/sso/oauth2/realms/root/realms/audkenni/access_token","response_types_supported":["code token id_token","code","code id_token","device_code","id_token","code token","none","token","token id_token"],"revocation_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","self_signed_tls_client_auth","tls_client_auth","none","client_secret_basic"],"request_uri_parameter_supported":true,"rcs_response_encryption_enc_values_supported":["A256CBC-HS512","A192CBC-HS384","A256GCM","A128GCM","A192GCM","A128CBC-HS256"],"userinfo_encryption_alg_values_supported":["RSA-OAEP","dir","A256KW","RSA-OAEP-256","A128KW","A192KW","RSA1_5"],"grant_types_supported":["refresh_token","authorization_code","urn:openid:params:grant-type:ciba","urn:ietf:params:oauth:grant-type:uma-ticket","idm_delegation","urn:ietf:params:oauth:grant-type:jwt-bearer"],"end_session_endpoint":"https://pfzww.audkenni.is:443/sso/oauth2/realms/root/realms/audkenni/connect/endSession","rcs_request_encryption_enc_values_supported":["A256GCM","A256CBC-HS512","A192GCM","A128CBC-HS256","A128GCM","A192CBC-HS384"],"revocation_endpoint":"https://pfzww.audkenni.is:443/sso/oauth2/realms/root/realms/audkenni/token/revoke","version":"3.0","rcs_response_encryption_alg_values_supported":["dir","A256KW","RSA-OAEP-256","A128KW","A192KW","RSA-OAEP","RSA1_5"],"userinfo_endpoint":"https://pfzww.audkenni.is:443/sso/oauth2/realms/root/realms/audkenni/userinfo","token_endpoint_auth_signing_alg_values_supported":["RS512","RS384","RS256","ES512","HS256","HS384","PS512","ES384","PS256","ES256","HS512","PS384"],"require_request_uri_registration":true,"code_challenge_methods_supported":["plain","S256"],"id_token_encryption_alg_values_supported":["A128KW","A192KW","RSA-OAEP-256","RSA-OAEP","A256KW","RSA1_5","dir"],"jwks_uri":"https://pfzww.audkenni.is:443/sso/oauth2/realms/root/realms/audkenni/connect/jwk_uri","subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS384","RS256","PS512","ES512","HS384","HS256","PS256","ES256","PS384","ES384","RS512","HS512"],"registration_endpoint":"https://pfzww.audkenni.is:443/sso/oauth2/realms/root/realms/audkenni/register","request_object_signing_alg_values_supported":["RS256","ES512","PS512","RS384","HS512","ES256","ES384","HS256","HS384","PS384","RS512","PS256"],"request_object_encryption_alg_values_supported":["RSA-OAEP-256","A256KW","RSA-OAEP","RSA1_5","dir","A128KW","A192KW"],"rcs_response_signing_alg_values_supported":["PS256","ES384","RS512","ES256","HS512","PS384","RS256","ES512","PS512","HS384","HS256","RS384"]}
An example of a jwks_uri from the response (replace Base URL):
https://pfzww.audkenni.is/sso/oauth2/realms/root/realms/audkenni/connect/jwk_uri
This endpoint will give you a response similar to this:
{"keys":[{"kty":"RSA","kid":"K4h0TN2QBJYOVXAx3gYfn6nlajU=","use":"sig","x5t":"1hxzBQ9NTcb1VVUvWQAyenjNp9Y","x5c":["MIIDADCCAeigAwIBAgIJAMsP+6XcMJMbMA0GCSqGSIb3DQEBCwUAMC4xETAPBgNVBAoTCEF1ZGtlbm5pMRkwFwYDVQQDExByc2Fqd3RzaWduaW5na2V5MB4XDTIxMTExOTA4NDAyNFoXDTI0MTEwODA4NDAyNFowLjERMA8GA1UEChMIQXVka2VubmkxGTAXBgNVBAMTEHJzYWp3dHNpZ25pbmdrZXkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCIN35nnkS8DClKB3XkAfcb6C8tc6AX7c1Aq3cPFgvNaw2bB0xv36oLBN4SllDfbM86O4bqHKEdiURAL4do4iKtH1w0McvYbhAmvdTpYrP6zed7ja86UvQJ/KDBFoZNS1GbS4G/Mca1g6JwfV9jIaGrFQj75IGWTwSGNrBk0u90W2ejd9+HfoAIgC/mGPjktQLMU12fNmLlFBOeIA96x2D1OstcC66opQc9TTorIffwFuodAB1ui6BEmheGZSVKEEFqSgkfn4Xh3Ugz4cm7VTb1+9heaHxsu+6FYzQd9ZKpmMU+g++diMQlJZxQxrGtVpudvlVlh1iS+4CAK3UGLcgnAgMBAAGjITAfMB0GA1UdDgQWBBTqS4Xyp0PaVkdEfisylXiv9aBlDzANBgkqhkiG9w0BAQsFAAOCAQEAVth59x6fdyAt5VmFzm2iftk2ZVYC6JXkFWlp+h6jGuTeoRWAsq/ZrAjoKc+ohSh7RqecNtO/K5XcpOCrtKQAzXvgDlzr0vKefDLE/iyV0SPzDlEl/dfVC99xC6Fnd4JRLIVBw3qpdOpNOcr9Ox3P2wWZxENswUCgY2h9BF5AIYFMtBFkXfFgTaPKcWFHK/Lu/LEBNmzw97SainJVOOIBd/lwwZfVwDXHEGKdLbfOIsskynmRu0q45jaqGGtJQhAaibmdsQrmaHAJRtaBXQA0ghSulvEvQB8kmub/ENoEQYX0kxyONkLcQ5+t7r+KcMclZ5ycOdYkzfaUy7/TX7GkyA=="],"n":"iDd-Z55EvAwpSgd15AH3G-gvLXOgF-3NQKt3DxYLzWsNmwdMb9-qCwTeEpZQ32zPOjuG6hyhHYlEQC-HaOIirR9cNDHL2G4QJr3U6WKz-s3ne42vOlL0CfygwRaGTUtRm0uBvzHGtYOicH1fYyGhqxUI--SBlk8EhjawZNLvdFtno3ffh36ACIAv5hj45LUCzFNdnzZi5RQTniAPesdg9TrLXAuuqKUHPU06KyH38BbqHQAdbougRJoXhmUlShBBakoJH5-F4d1IM-HJu1U29fvYXmh8bLvuhWM0HfWSqZjFPoPvnYjEJSWcUMaxrVabnb5VZYdYkvuAgCt1Bi3IJw","e":"AQAB"},{"kty":"RSA","kid":"NefWEBJqU24exUROW9FARwKaztg=","use":"enc","x5t":"-7rTDZk0bOt3bIjnLfVDSOZjIzw","x5c":["MIIC5zCCAc+gAwIBAgIILsOY4ny5r+IwDQYJKoZIhvcNAQELBQAwIjERMA8GA1UEChMIQXVka2VubmkxDTALBgNVBAMTBHRlc3QwHhcNMjExMTE5MDg0MDI1WhcNMjQxMTA4MDg0MDI1WjAiMREwDwYDVQQKEwhBdWRrZW5uaTENMAsGA1UEAxMEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOq/HN4UV98QMsRhodA0EX7ErX9lMyhznM83bxB8pTtiPUTCjw6omSp/RMBXhIc2gmGEqLyVstFqH+t7d6rByE7idRbQK4GX9R2xmpkxSv4tn19jFHszUWeGmjwoun6EQ/Rs03PJBEGhUhrRD3jrEgSM7dbORHmMTmhvXPNxIVaL+4Gk7AmXRF0vMq6M6g4ab1/sCiMIEH4Vhj2taYSW9jxWK0tgRYz+IOK0VbZ19kLjFVajIzqYxIY86KD/poXn/5BUHxP/xbrhTK+wIO9nuDCR6iCgqoQaA3P0qt/Okps5wlU0vsg/rW26CsbQEhbKnxOEu13wnmaoEqcsVa392CMCAwEAAaMhMB8wHQYDVR0OBBYEFP9TPPHzuc5aJoFE1yJOUwuoeECuMA0GCSqGSIb3DQEBCwUAA4IBAQDIEQD5ucXUIUInzkQPb80B6wKD3s7TWXeZ4IWQn8h5zw/r5pRUEZx+EwtiDGMoCQWsJ4YtQ9KH4MAd37ZqYIJDN+7ZkaLp5FSdcVtBXlqFg3EKDDkrV/hY5YA+JVxJ/KJ5WyRHCe72No1r3+lxFjQti5xf8Uo1WfZbt0nYkZZuO/11mfCiUvUY6bFs6oNVbL886lF9XvPoxkx+637V2GPjlGgmrwaaP19bYIjxMQMsUrZYqhFfIehOPPKkRRjYrcjdSfxDjCygoTUMTYrDb4lEzHimo8BiybEP4qJXhfjYdGTCfvwnnj0nCX8j8exASAJOZJLXNGiXWf15d71XPaHY"],"n":"6r8c3hRX3xAyxGGh0DQRfsStf2UzKHOczzdvEHylO2I9RMKPDqiZKn9EwFeEhzaCYYSovJWy0Wof63t3qsHITuJ1FtArgZf1HbGamTFK_i2fX2MUezNRZ4aaPCi6foRD9GzTc8kEQaFSGtEPeOsSBIzt1s5EeYxOaG9c83EhVov7gaTsCZdEXS8yrozqDhpvX-wKIwgQfhWGPa1phJb2PFYrS2BFjP4g4rRVtnX2QuMVVqMjOpjEhjzooP-mhef_kFQfE__FuuFMr7Ag72e4MJHqIKCqhBoDc_Sq386SmznCVTS-yD-tbboKxtASFsqfE4S7XfCeZqgSpyxVrf3YIw","e":"AQAB"},{"kty":"EC","kid":"T4HpP8SlCjLmEazbTNZ8j1/IIvk=","use":"sig","x5t":"BIiX_2w5Io0PAS3oDURSbDVK_f8","x5c":["MIIB6TCCAUmgAwIBAgIJAInrJIB0ytKkMAwGCCqGSM49BAMCBQAwIzERMA8GA1UEChMIQXVka2VubmkxDjAMBgNVBAMTBWVzNTEyMB4XDTIxMTExOTA4NDAyM1oXDTI0MTEwODA4NDAyM1owIzERMA8GA1UEChMIQXVka2VubmkxDjAMBgNVBAMTBWVzNTEyMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAvrU4j5XbfkssKFLubKZxI6aCRf3pNH/Gb4W/tSpN7oytwtSpHZ5I3Ub4/+kaKxAnlBkeUWDRK4FRh7DmYaFzyE0BKS5L0G1LMsEJl1weaSf4Vbt0srWFM6sqeLOtJNNERadQ7hoZidEdw+Xq/LE8SBdnQhBuONP6N3eYN3J0LBL0Jp+jITAfMB0GA1UdDgQWBBQtDvXgmH0XW1SMMDb4FeqrArVfczAMBggqhkjOPQQDAgUAA4GLADCBhwJBcHbXNhzOM6JAARfYjJyexby4xbzDGry/nlLVprO3iTS0XPgER9dMg1uW3+mtMeaQDW3jKRIGSSEn3BFnkARBcGACQgD1CxPiS14Hq4hFn+vSGtOn0Jb6ErnAlp+wDWPl8kuYma45ehskPQ41c1bYm4LuJgfh6annnLCNEXQIIsuyT7LZVQ=="],"x":"AL61OI-V235LLChS7mymcSOmgkX96TR_xm-Fv7UqTe6MrcLUqR2eSN1G-P_pGisQJ5QZHlFg0SuBUYew5mGhc8hN","y":"ASkuS9BtSzLBCZdcHmkn-FW7dLK1hTOrKnizrSTTREWnUO4aGYnRHcPl6vyxPEgXZ0IQbjjT-jd3mDdydCwS9Caf","crv":"P-521"},{"kty":"EC","kid":"HmusVH9OnoQd7evfLa+CyKihFoo=","use":"sig","x5t":"-30DTwdwKD8qMHoEd7B38jj8OMI","x5c":["MIIBYTCCAQagAwIBAgIJAJ/bjPetKN9JMAwGCCqGSM49BAMCBQAwIzERMA8GA1UEChMIQXVka2VubmkxDjAMBgNVBAMTBWVzMjU2MB4XDTIxMTExOTA4NDAyMVoXDTI0MTEwODA4NDAyMVowIzERMA8GA1UEChMIQXVka2VubmkxDjAMBgNVBAMTBWVzMjU2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEv0me73TLfyMb1NVvKsPUG3p36pc02R+5GNZPkMrgqWzJRLt27vDN/m1SUKMTmlbTjiliyNnsnbxfLLz4ffhnO6MhMB8wHQYDVR0OBBYEFD+Ibr3+RmDGlBW+KKHjW3Ijhd94MAwGCCqGSM49BAMCBQADRwAwRAIgKIBV7lRgm5tbwVf9zsoZnBBsSQ+DNims6FqyyDhRZioCIBBhDp5xVit8sqduqmZbYV/gLyPHKdR9f+M/a9i0KkmM"],"x":"v0me73TLfyMb1NVvKsPUG3p36pc02R-5GNZPkMrgqWw","y":"yUS7du7wzf5tUlCjE5pW044pYsjZ7J28Xyy8-H34Zzs","crv":"P-256"},{"kty":"EC","kid":"CbJYmrtlpLrMno/Z+n5b7FmBmKI=","use":"sig","x5t":"de4wUDcaPEccuV5iqJNPWd36p4M","x5c":["MIIBnjCCASKgAwIBAgIIWZA4UG2LS94wDAYIKoZIzj0EAwIFADAjMREwDwYDVQQKEwhBdWRrZW5uaTEOMAwGA1UEAxMFZXMzODQwHhcNMjExMTE5MDg0MDIyWhcNMjQxMTA4MDg0MDIyWjAjMREwDwYDVQQKEwhBdWRrZW5uaTEOMAwGA1UEAxMFZXMzODQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQPHeOgtn7uOwY4RfFJr//wgtgviEOKCRRldqGU9oKCQPpB5Et2gKI3/Lz+y7xx2h0QrSN6FrooJZMzyRABHYwfL+fR40/m1bfpUFqO9XD7EO+oAYp87c8ei80uBJys7KOjITAfMB0GA1UdDgQWBBTEZU3eSxBux51Twq7L1eNC3rriPTAMBggqhkjOPQQDAgUAA2gAMGUCMC6QTW70G1hfhBNu/9FFrE3aqyF6cJY6IFN9ktPHRFNR2YbIbQc23/yoBK0J/muh/QIxAII4ORu+nKWZWw3Zc1x1tLFNyUafUniJrZEnbshL8J+2XvgpX7J2AQyxNtlZQ9JiAQ=="],"x":"Dx3joLZ-7jsGOEXxSa__8ILYL4hDigkUZXahlPaCgkD6QeRLdoCiN_y8_su8cdod","y":"EK0jeha6KCWTM8kQAR2MHy_n0eNP5tW36VBajvVw-xDvqAGKfO3PHovNLgScrOyj","crv":"P-384"}]}
This response hold keys you can use to verify the access_token and id_token from our system.
To verify the responses
There are ready-made tools available for most programming languages that can be used to verify JWT signatures. Below is a small code example in C# that hopefully makes it clearer:
public bool VerifyJwt(string token, string secretKey, string audience, string issuer) { try { byte[] certificateData = Convert.FromBase64String(secretKey); X509Certificate2 certificate = new X509Certificate2(certificateData); X509SecurityKey securityKey = new X509SecurityKey(certificate); // Define token validation parameters var tokenHandler = new JwtSecurityTokenHandler(); var validationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = securityKey, ValidateIssuer = true, // Set to true if you want to validate the issuer ValidIssuer = issuer, // Replace with the expected issuer ValidateAudience = true, // Set to true if you want to validate the audience ValidAudience = audience, // Replace with the expected audience ValidateLifetime = true, ClockSkew = TimeSpan.Zero }; // Parse and validate the token SecurityToken validatedToken; var principal = tokenHandler.ValidateToken(token, validationParameters, out validatedToken); // You can access the claims in the token via the principal.Claims property if needed. // For example: // var userId = principal.Claims.FirstOrDefault(c => c.Type == "sub")?.Value; // If the token is valid, the validation process will not throw an exception. // So, if you reach this point, the token is valid. return true; } catch (Exception) { // If an exception is thrown, the token is not valid. return false; } }
To verify the signatures
You receive PKCS7 or PKCS1 signature, depending on which method you are using.
To verify PKCS7 signature
There are ready-made tools available for most programming languages that can be used to verify PKCS7 signatures. Below is a small code example in C# that hopefully makes it clearer:
private bool validateSignPKCS7(string toValidate) { byte[] fromCMSString = null; SignedCms cms = null; try { fromCMSString = Convert.FromBase64String(toValidate); cms = new SignedCms(); cms.Decode(fromCMSString); cms.CheckSignature(true); } catch (Exception u) { return false; } return true; }
The PKCS7 signature should contain the certificate of the person who is authenticating/signing. That certificate needs to be verified.
To verify PKCS1 signature
When our system returns a PKCS1 response, the digital signature and the authenticated user's certificate are included separately. To verify the digital signature, we use:
The user’s certificate
The signature
The Hash string we sent in the beginning of the authentication process (“binding_content” in CIBA, the IDToken7 input in REST API)
There are ready-made tools available for most programming languages that can be used to verify PKCS1 signatures. Below is a small code example in C# that hopefully makes it clearer:
private bool validatePKCS1Signature(string signatureFromAudkenni, string hashStringFromAuthenticationRequest, string usersCertFromAudkenni) { byte[] orgHash = null; byte[] signedHash = null; byte[] certBytes = null; X509Certificate2 certificate = null; try { orgHash = Convert.FromBase64String(hashStringFromAuthenticationRequest); signedHash = Convert.FromBase64String(signatureFromAudkenni); certBytes = Convert.FromBase64String(usersCertFromAudkenni); certificate = new X509Certificate2(certBytes); RSA key = (RSA)certificate.PublicKey.Key; RSAPKCS1SignatureDeformatter formatter = new RSAPKCS1SignatureDeformatter(key); formatter.SetHashAlgorithm("SHA512"); var result = formatter.VerifySignature(orgHash, signedHash); return result; } catch (CryptographicException e) { return false; } }
To verify Certificates
To verify that the user's certificate you receive in the response is correct, you can use our intermediate certificate and the root certificate
For Testing environment:
Intermediate certificate: https://repo.audkenni.is/Skilriki/certs/FAP2021test.cer
Root certificate: https://repo.audkenni.is/Skilriki/certs/IslandsrotTest2021.cer
More details here: https://repo.audkenni.is/Skilriki/profanir.cfm
For Production environment:
Intermediate certificate:
Fullgilt audkenni 2021 - Valid from 09.09.2021 to 09.09.2036: https://skrar.audkenni.is/skilrikjakedjur/islandsrot/Fullgiltaudkenni2021.cer
Fullgilt audkenni - Valid from 16.11.2017 to 16.11.2032: https://skrar.audkenni.is/skilrikjakedjur/islandsrot/older/Milliskilriki.cer
More details here: https://repo.audkenni.is/Skilriki/milliskilriki.cfm
Root certificate:
Islandsrot 2021 - Valid from 09.09.2021 to 09.09.2051: https://skrar.audkenni.is/skilrikjakedjur/islandsrot/Islandsrot2021.cer
Islandsrot - Valid from 02.12.2015 to 02.12.2055: https://skrar.audkenni.is/skilrikjakedjur/islandsrot/older/Islandsrot.cer
More details here: https://repo.audkenni.is/Skilriki/rotarskilriki.cfm