Rekvirent3
The rekvirent3 API allows communication with reseptformidleren using JWE/JWS as an alternative to WS-Security. The API supports the same message types as the base rekvirent API, however some older message versions are not supported.
To provide mutual certificate binding with JWE wrapped JWS we utilize the x5c claim(https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.6) of the JWS. This MUST be a list containing only the senders certificate. Note that sender in this context is whomever created the JWS. The JWS MUST also be signed with the private key matching the public key of the provided certificate.
For encryption the sender MUST use the public key of the recipient to encrypt the symmetric key. The JWE MUST specify the kid (https://www.rfc-editor.org/rfc/rfc7516#section-4.1.6) used to encrypt.
The kid MUST be the Base64Url encoding of the SHA-256 of the JWK thumbprint. The thumbprint must be on the format defined by RFC7638
Process
Request
- Create KITH payload(out-of-scope)
- Create a JWS
- payload: base64 encoded kith
cty: application/xmlx5c: your X509 certificate, must be the same used to sign the message. The returned JWE will be encrypted using the public key of this certificate
- Create a JWE
- payload: created JWS
cty: JWT, see: https://www.rfc-editor.org/rfc/rfc7519#section-5.2- kid: base64url(sha256(JWK thumprint)
- Prepare HTTP request
- headers:
- Content-Type: application/jwt
- Authorization: DPoP <access_token>
- DPoP:
<proof>
- headers:
Response
The response follows the same format as the request, with inverted certificates.
The figure below details how to handle the response codes and content-types. Note that RF does not respect the accept header. In the general case a JWE is returned. For errors that occur before or during authentication or JWE/JWS parsing, a json response is returned. The json payload contains a simple message describing the cause.
{ "message": "an explanation of what went wrong" }
graph TD
response((response))
check_http_status{check-http-status}
check_content_type{check-content-type}
response --> check_http_status
check_http_status -->|401,application/json| auth_failure
check_http_status -->|5xx| check_content_type
check_http_status -->|200,application/jwt| handle_ok_response
check_http_status -->|others| undefined_behaviour
check_content_type -->|application/json| raw_error
check_content_type -->|application/jwt| handle_apprec_error
handle_apprec_error["`
handle-apprec-error:
1. JWE/JWS unpack
2. Decode Apprec
3. Handle`"]
Sample implementation
Below is a sample implementation. The implementation is written in java using Nimbus for JWT functionality. Key and certificate material is not provided, the reader should be able to infer the relevant values from the provided sample. Note this sample is not functional, some edits have been made to improve readability.
public static createRequestPayload() {
final RSAKey senderPrivateKey;
final X509Certificate senderCertificate;
final X509Certificate recipientCertificate;
final byte[] signedKith = signKith(kithXml);
// Note: Payload is just a type wrapper used by nimbus
final Payload payload = new Payload(
Base64.getEncoder().encode(signedKith));
try {
final JWSObject jws = createJWS(payload, senderPrivateKey, senderCertificate);
final JWEObject jwe = createJWE(jws, recipientCertificate);
return jwe.serialize();
} catch (Exception e) {
throw new RuntimeException("Error creating JWE object: " + e.getMessage(), e);
}
}
private static JWSObject createJWS(
Payload payload,
RSAKey senderPrivateKey,
X509Certificate senderCertificate) throws Exception {
JWSSigner signer = new RSASSASigner(senderPrivateKey);
List<String> x5c = new ArrayList<>();
x5c.add(Base64.getEncoder().encodeToString(senderCertificate.getEncoded()));
JWSObject jwsObject = new JWSObject(
new JWSHeader.Builder(JWS_SIGNATURE_ALGORITHM) // e.g. RS256
.x509CertChain(x5c)
.contentType("application/xml")
.build(),
payload);
jwsObject.sign(signer);
return jwsObject;
}
private static JWEObject createJWE(
JWSObject jwsObject,
X509Certificate recipientCertificate)
throws Exception {
RSAKey encryptionKey = new RSAKey.Builder((RSAPublicKey) recipientCertificate.getPublicKey())
.keyIDFromThumbprint()
.build();
JWEEncrypter encrypter = new RSAEncrypter(encryptionKey);
JWEObject jweObject = new JWEObject(
new JWEHeader.Builder(JWE_ENCRYPTION_ALGORITHM, JWE_ENCRYPTION_METHOD) // e.g. RSA_OAEP_256, A256GCM
.keyID(encryptionKey.getKeyID())
.contentType("JWT")
.build(),
new Payload(jwsObject.serialize()));
jweObject.encrypt(encrypter);
return jweObject;
}