Encryption and Integrity
All communication with Eclipse is over a TLS 1.2+ encrypted socket with Eclipse having a valid & verified certificate with a full certificate chain leading to the publicly available Amazon root CA. Clients must not trust unverified certificates as doing so could enable a man-in-the-middle attack.
When Eclipse does webhooks back to tenants' systems, the tenant can verify the message integrity by using a special “Eclipse-Signature” HTTP header sent in the webhook.
HMAC SHA256 Signature for Eclipse->Tenant Webhooks
The signature is of the format:
Header Name: Eclipse-Signature
Header Value: t=xxx,v1=yyy
Where: xxx is a timestamp being the milliseconds since the UTC EPOCH at which Eclipse sent the webhook. yyy is a Base64 encoded HMAC-SHA256 of the timestamp concatenated with a “.” and then concatenated with the message body (If there is no message body then treat the body as an empty string). The secret key used in the HMAC is a configuration on the tenant called HMACOutboundSignatureKey.
As an example:
HMACOutboundSignatureKey=HelloWorld
MessageBody={"alias":"bluXSSCPCZ","cardOnFileId":"8e2c86a5-1acf-48b7-aa70-11a1b18c0d84","last4Digits":"0028"}
Eclipse-Signature header value:
t=1625602409535,v1=b2+TTx2JbaHqeM67fpRye30Go1vdyvyGdoljBqdL9Rg=
HMACSHA256 of “1625602409535.{"alias":"bluXSSCPCZ","cardOnFileId":"8e2c86a5-1acf-48b7-aa70-11a1b18c0d84","last4Digits":"0028"}” and key “HelloWorld” is:
Hex: 6f6f934f1d896da1ea78cebb7e94727b7d06a35bddcafc8676896306a74bf518
Base64: b2+TTx2JbaHqeM67fpRye30Go1vdyvyGdoljBqdL9Rg=
Hence signature matches
Tenants are requested to verify signatures in order to verify the authenticity of Eclipse webhooks.
HMAC SHA256 Signature for Tenant->Eclipse API Calls
For inbound message integrity verification (into Eclipse), if the configuration value for HMACInboundSignatureKey and HMACInboundSignaturePositions is set, then Eclipse will expect all HTTP requests from any users whose position is included in HMACInboundSignaturePositions to include an Eclipse-Signature header generated in the exact same way. If no value is set in the tenant configuration for HMACInboundSignatureKey then no integrity is checked. Ukheshe encourages tenants to make use of inbound integrity checks for TENANT_SYSTEM admin users by setting HMACInboundSignatureKey with a complex secret and HMACInboundSignaturePositions=TENANT_SYSTEM. The reason HMACInboundSignaturePositions configuration exists is so that one can enforce signatures for server to server communication with "powerful" credentials such as TENANT_SYSTEM, while allowing end customer identities to skip this more complex process. The positions refer to the position of the admin user e.g.:
Eclipse will reject messages where the timestamp in the signature is more than 5 minutes old. Tenants can do the same. This ensures replay of old messages will be rejected. Tenants should ensure their servers utilise NTP or similar to ensure clocks do not drift.
Eclipse rejects invalid or missing signatures with an HTTP 403 and Body
[{"type":"BUSINESS","severity":"INFO","description":"Missing Eclipse-Signature header to ensure message integrity","code":"SEC001","traceId":"4a02a06f5e052b14"}]
RSA Signature (For Inbound & Outbound)
In addition to HMAC signatures above, Eclipse also supports RSA-based signatures. This is done by both parties sharing their public keys with one another. The flow is the same as above, except that instead of the v1 tag used for Base64 encoded HMAC-SHA256, the tag will be v2 and contain the Base64 encoded RSA signature of the timestamp and message body, exactly as per the HMAC-SHA256 case.
For example:
Eclipse-Signature header value: t=1625602409535,v2=`Base64 encoded RSA signature of timestamp and body`
The public key to be used for this signature can be found here for sandbox and production.
Webhook Authorization Header
Some third-party systems require an Authorization header in all webhooks or callbacks. To support this scenario a tenant config parameter can be configured webhookAuthorizationHeader:
When configured, this value will be added in the Authorization HTTP header on any webhooks or callbacks to the tenant:
Full Payload Encryption
In certain circumstances such as the transfer of card details and other very sensitive data, bank security teams may request that API calls to Eclipse utilise not only TLS, but additional payload level encryption. Eclipse supports this on all API endpoints by utilising the JWE standard for encrypting strings and passing the ciphertext along with IV and key and algorithm data alongside the payload. For more details on the specification, refer to https://datatracker.ietf.org/doc/html/rfc7516
Although the specification covers details on how a payload can be encrypted, it does not standardise how it is passed in a HTTP body, nor how a client and server should interact in the case where a client sends encrypted data and the server decrypts and replies with encrypted data. Eclipse implements this in the following way:
- To indicate that the payload sent to Eclipse uses JWE, an HTTP header "content-encryption" must be passed and the value must start with the string "jwe". More details on this will follow.
- The message body (if present) will be JSON encoded and have a single string parameter "encryptedPayload". This will hold the JWE payload. E.g. {"encryptedPayload":"eyJraWQiOiJteWZp..."}
- Every API request to Eclipse should have a random AES key (known as CEK (content encryption key)) and IV used to encrypt the JSON payload. The CEK is in turn RSA encrypted with a public key provided by Eclipse out of band. Along with the given public key, it will have an associated keyID that identifies the keyPair in use for the tenants JWE encryption. Tenants may rotate keys and the keyID is used to identify what RSA Key is being used in the interaction.
- The recommended JWE encryption method and algorithm specified in the JWE header are: {"kid":"key ID provided by Ukheshe","enc":"A256GCM","alg":"RSA-OAEP-256"}
- The CEK chosen by the client for a request will in turn be used by Eclipse to encrypt the response back to the client. This negates the need for the client to share a public key with Eclipse for the response encryption.
- Eclipse's response body will be of the format {"encryptedPayload":"eyJraWQiOiJteWZp..."} just like the body sent by the client. The only difference is that the response JWE will have DIR (Direct) as the algorithm and will not have any CEK as the CEK will be the one created by the client in the request. E.g. the JWE header from Eclipse could be { "enc": "A256GCM", "alg": "dir"}. Eclipse will use the same AES encryption method in the response as was used in the clients request.
One detail to consider is for GET requests that have no request message body but may want the response body to be a JWE payload. For this use case, the client should pass the content-encryption header as follows:
content-encryption: jwe.JWE payload encrypting an empty string
E.g. content-encryption: jwe.eyJraWQiOiJteWZpbmdlcnByaW50IiwiZW5jIjoiQTI1NkdDTSIsImFsZyI6IlJTQS1PQUVQLTI1NiJ9.EkMOKgI-KNco...
Eclipse will use the CEK from this JWE in the content-encryption header to encrypt the payload in the response. The payload will then come back to the client in the format E.g. {"encryptedPayload":"eyJraWQiOiJteWZp..."}
It's important to note that the use of payload encryption is at the discretion of the API caller and all API's can use this mechanism. Eclipse transparently notices that the content-encryption header starts with "jwe" and then does the associated decrypting on the request leg and encrypting on the response leg.
Note. For testing in Sandbox, use the following public key:
KeyID: sandboxjwe
Public Key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA90AOEmdaTaE3IySVBBX3wLBK/hzwZUilxjwQ/gcdO0IRKdDtu2R6KmR9k3ZDZuH2x+5DKo6v8Q30qy8ji/0OerqYmwZCAnV9AUbHkcOg8LWCV2UpqpqAIMbmzGoiAnvHkDg8eJtdwvji1ubcUPfwSk8TidFOqdbSlEfHUmyCbFf3wk1CGop6Db/TGvpOUWGwXOnTS5bw+MzqVEdUZ2RWIL8W1FIQsYigi+SBbauysb0X3ZWABqRrBT1S4G+ISbFCRUM4eyoy72q/CjbeD833Pe7xq/Y58DQbfEGF9fcqoE9zEw9WJKDA5YOfL9r38ZKEeGdGGdoiHUR7jOFQtkuQmQIDAQAB
For production use the following public key:
KeyID: prodjwe1
Public Key:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk2VFBK9mhOENUr1OfEVmD36JEQCIrofYqiWajsR9QBC6lgLmPeP+9A9ycCI4tt3WbQ3R4JAC6b1CKFwEDzo8cCEoDfld2NQG3ZRvNOk3JUlgIvFLILdC8qptHbbNHhCnrn8al8/F2WRNQxLBveBbL/amgP9tcZmUX1aO2XoK+IX9/VR4cEm052UUw4PD7qzoh7PvFod+IOff9foxAdWOITb4SA9st1DjXYrC+kix0V+vYgIyisBaEa3eeEyYoZJLx3QU2L7n06OoJFXD9pqYrOBUEYERiKDRBXuTJDtzTGJ6i5oyBIn0TlrlyqSD3DfFDUusPdCtu6XWrAfbJsYnxwIDAQAB
Implementation Note
Multiple asymmetric keys can be managed by Eclipse. To add a key, create a RSA2048 keypair and share the Public key with the tenant and agree on a key Id such as "prodjwe2". Then add the private key base64 encoded into property "private.keys.by.fingerprint" with the form <keyid>=<base64 encoded private key>. E.g.
property name:
private.keys.by.fingerprint
property value:
prodjwe1=<awsencrypted>....existing base64...<awsencrypted>
prodjwe2=<awsencrypt>....new key base64...<awsencrypt>
As per all keys, ensure the key is surrounded by <awsencrypt> when added so that it ends up being encrypted by KMS.
Updated 26 days ago