Encryption in transit
Encrypt the message
- Create the payload (or url) you wish to have - these follow the ones detailed (for example: make payment)
- Generate an
AES
key, and encrypted the payload from step 1 with this key - Encrypted the
AES
with the provided Fondy public key - Build the
payload/url
withBase64(publicKeyEncrypted(AES))+DELIMITER+Base64(aesEncrypted(payload/url))
base64
encode the final string- Do this for the
URL
,user
andpayload
in the encryptedobject
- Set the method (assuming make payment this will be
POST
) - Combine
payload
,url
, andmethod
, and use your private key to generate a signature [1] base64
encode the signature- Send as a
POST
to the/.../{your clientRef}/encrypted
Legend:
[1] The order for the message to be signed is base64URL+base64Method+base64Payload
The logic is also shown below in the step by step:
Encrypt: Use AES:
Encrypt the String with a generated AES key
-> AESEncrypted(value)
Fromt the above string -> base64 encode this
-> base64Encode(AESEncrypted(value))
Encrypt the AES key using the PUBLIC key of FONDY (RSA encrypted the AES Key)
-> RSAEncrypted(AESKey)
Then Base64 encode the encrptyed AES key
-> base64Encode(RSAEncrypted(AESKey))
Create a string of
-> base64Encode(RSAEncrypted(AESKey)) + Delimiter + base64Encode(AESEncrypted(value))
Base64encode this final string and send/use
Decrypt:
Base64decode the passed in string this gives you
-> base64Encode(RSAEncrypted(AESKey)) + Delimiter + base64Encode(AESEncrypted(value))
Create 2 strings (split on delimiter) of base64Encode(RSAEncrypted(AESKey)) and base64Encode(AESEncrypted(value))
Base64decode these two strings: RSAEncrypted(AESKey), AESEncrypted(value)
Using YOUR PRIVATE key
-> decrypt RSAEncrypted(AESKey) => key
Using the key in the line above
-> decrypt AESEncrypted(value) => readable value
Return the readable value
Delimiter
public static final String DELIMITER="-----";
Example Code
public class EncryptedObject {
//all values are base64Encoded when in transit AS WELL as the encryption.
String encrypted_payload;
String encrypted_user;
String encrypted_url;
String method; //this MUST NOT be encoded or encrypted -> it is used for "blind" routing
String nonce ; // used only on webhooks
String signature;
}
public class TheClassThatDoesTheWork {
public static final String DELIMITER="-----";
public EncryptedObject buildOutboundPayload(String json, String URL, String method, Strung user){
EncryptedObject encObject = new EncryptedObject();
encObject.encrypted_url = buildString (URL);
encObject.encrypted_payload = buildString (json);
encObject.encrypted_user = buildString (user);
encObject.method = method;
encObject.signature = signMessage(encObject);
return encObject;
}
private String buildString(String value){
// this is psuedo code - it will not compile!
String aes= generateAESKey();
byte[] byteCipherText = aesCipher.doFinal(value.getBytes());
// encode the encrypted Text to B64
String outB64EncValue = Base64.encode(byteCipherText);
// grab the outbound pub key (clients)
PublicKey puKey = .......
cipher.init(Cipher.PUBLIC_KEY, puKey);
// Encode the AES key -> with RSA pub
byte[] encryptedKey = rsaCipher.doFinal(key.getEncoded());
// encode the key to B64
String outB64EncKey = Base64.encode(encryptedKey);
// build up outB64EncKey+delimiter+outB64EncLoad and then base64 that string
return Base64.encode((outB64EncKey + DELIMITER + outB64EncValue).getBytes());
}
private String signMessage(EncryptedObject encObject){
//this is psuedo code - it will not compile!
String stringToSign = ""+encObject.encrypted_url+encObject.method+encObject.encrypted_payload+encObject.encrypted_user;
//sign the string
String signature = signStringWithPrivateKey(stringToSign);
//base64 it and return it
return Base64.encode(signature);
}
}
Signature of messages (to/from Fondy) request/response not webhooks
There will be a signature on the message - it will be signed with Fondy's private key, and this is for you. To check that the message was from us, and not changed with a MITM
style attack.
There order is:
base64Encoded
EncryptedURL
- MUST be there on messages to Fondy, not populated by Fondy on returnbase64Encoded
Method
- MUST be there on messages to Fondy, not populated by Fondy on returnbase64Encoded
EncyptedPayload
- MUST be populated on post/patches not getsbase64Encoded
encrypted_user
- MUST be there on messages to Fondy, the username required to perform this action
NOTE: null/empty strings MUST be added as "" (zero length string)
And the format of the string will be:
// assuming
// "base64Encoded EncryptedURL" = "STRING_1"
// "base64Encoded Method" = "STRING_2"
// "base64Encoded EncryptedPayload" = "STRING_3"
// "base64Encoded EncryptedUser" = "STRING_4"
// sign with your private key, and use SHA256withDSA
// MESSAGES TO FONDY
String toSign = base64Encoded(EncryptedURL) + Method + base64Encoded(EncyptedPayload) + base64Encoded(EncryptedUser)
// from example
String toSign = STRING_1STRING_2STRING_3STRING_4
// The signature is then base64 encoded.
// MESSAGES FROM FONDY (responses)
String toSign = base64Encoded EncryptedPayload
// from example
String toSign = STRING_3
// The signature is then base64 encoded.
To decrypt the message
This is the reverse of what happens when you encrypted the message so:
- get the response from the
POST
to the/.../{your clientRef}/encrypted
- decode with
base64
theencrypted_payload
(not all other fields will be empty) - split the string with
DELIMITER
as the delimiter (Base64(encAESKey)
thenBase64(aesEncryptedPayload)
is the order) - decode the
Base64
for both values - decrypted the encrypted the
AES
key with the YOUR private key - decrypted with the
AES
key the payload - convert to (say)
JSON
Examples
In this section ENCRYPTED(...)
refers to the process detailed above
Sending "get all accounts":
{
"encrypted_payload": null,
"encrypted_user": ENCRYPTED(<username>),
"encrypted_url": ENCRYPTED("/accounts"),
"method": "GET"
}
Response:
{
"encrypted_payload": ENCRYPTED(accountsArray)
"encrypted_url": null
"method": null
}
where accountsArray
is the /accounts
below responses
Sending "post a payment"
{
"encrypted_payload": ENCRYPTED(paymentObject),
"encrypted_user": ENCRYPTED(<username>),
"encrypted_url": ENCRYPTED("/accounts/{uuid of account}/payment"),
"method": "POST"
}
Response:
{
"encrypted_payload": ENCRYPTED(paymentObject)
"encrypted_url": null
"method": null
"nonce": null
}
where paymentObject
is the /accounts/{id}/payments
below
Handling Errors
The response when an error will be:
{
"encrypted_payload": ENCRYPTED(errorObject)
"encrypted_url": null
"method": null
"nonce": null
}
where errorObject
is the error object
below
Things to note
- Any processing errors will come back as an encrypted payload, other errors (example making a get call to the
URL
) will come back inplan text
BUT with not details that arePII/PCI
, and fairly "generic" - this is by design - Webhooks function in the same manner - the webhook will be the encrypted object, and will need to be decoded in the same manner as if it were a
HTTP
request/response call (TBC) - Webhooks signature will be the
encryptedBase64
Payload + Nonce (TBC)
Example URLs
Below are example URLs that are the endpoints to call
Description | URL |
---|---|
base URL | https://api.fondy.io/clientapi/{client-ref}/encrypted/ |
(POST) ALL | https://api.fondy.io/clientapi/{client-ref}/encrypted/ |
(POST) For Nonce off the webhook | https://api.fondy.io/clientapi/{client-ref}/encrypted/ack |
Provided Jar
Fondy has provided a jar, if you are a Java based system, that does all the heavy lifting for you. This is covered in the API Guides