Encryption in transit

Encrypt the message

  1. Create the payload (or url) you wish to have - these follow the ones detailed (for example: make payment)
  2. Generate an AES key, and encrypted the payload from step 1 with this key
  3. Encrypted the AES with the provided Fondy public key
  4. Build the payload/url with Base64(publicKeyEncrypted(AES))+DELIMITER+Base64(aesEncrypted(payload/url))
  5. base64 encode the final string
  6. Do this for the URL, user and payload in the encrypted object
  7. Set the method (assuming make payment this will be POST)
  8. Combine payload, url, and method, and use your private key to generate a signature [1]
  9. base64 encode the signature
  10. 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:

  1. base64Encoded EncryptedURL - MUST be there on messages to Fondy, not populated by Fondy on return
  2. base64Encoded Method - MUST be there on messages to Fondy, not populated by Fondy on return
  3. base64Encoded EncyptedPayload - MUST be populated on post/patches not gets
  4. base64Encoded 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:

  1. get the response from the POST to the /.../{your clientRef}/encrypted
  2. decode with base64 the encrypted_payload (not all other fields will be empty)
  3. split the string with DELIMITER as the delimiter (Base64(encAESKey) then Base64(aesEncryptedPayload) is the order)
  4. decode the Base64 for both values
  5. decrypted the encrypted the AES key with the YOUR private key
  6. decrypted with the AES key the payload
  7. 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

  1. Any processing errors will come back as an encrypted payload, other errors (example making a get call to the URL) will come back in plan text BUT with not details that are PII/PCI, and fairly "generic" - this is by design
  2. 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)
  3. Webhooks signature will be the encryptedBase64 Payload + Nonce (TBC)

Example URLs

Below are example URLs that are the endpoints to call

DescriptionURL
base URLhttps://api.fondy.io/clientapi/{client-ref}/encrypted/
(POST) ALLhttps://api.fondy.io/clientapi/{client-ref}/encrypted/
(POST) For Nonce off the webhookhttps://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


Language
Click Try It! to start a request and see the response here!