0

I've done quite a few JSON/REST integrations and only two SOAPs, but this API I'm working with looks like neither. They do offer example code. I stuck with the java example because that is what I've been told apex is based off of.

This is the code I've been able to come up with so far.

public class SFax {

    private final String userName = 'user';
    private final String apiKey = '123';  
    private final String vector = 'abc';
    private final String encryptionKey = 'zyx';
    private final String baseUrl = 'https://api.sfaxme.com/api/';

    public String send() {       
        String encodedUrl = baseUrl + 'SendFax?' + getParams();

        try{
            HttpRequest req = new HttpRequest();
            req.setEndpoint(encodedUrl);
            req.setMethod('POST');
            req.setHeader('Content-Length', String.valueOf(encodedUrl.length()));
            req.setHeader('Content-Type', 'multipart/form-data;boundary=*****');

            //The timeout can be any value between 1 and 120,000 milliseconds.
            //req.setTimeout(5000);
            // Make call
            Http client = new Http();
            HttpResponse rsp = client.send(req);
            return rsp.getBody();
        } catch (Exception e) {
            return String.valueOf(auraError('Error: ' + e.getMessage()));
        }        
    }

    private String getParams() {
        String parameters;
        Map<String, String> params = new Map<String, String>();

        params.put('apikey',apiKey);
        params.put('token', generateToken());
        params.put('RecipientFax','1234567890');    
        params.put('RecipientName', 'Test');

        for(String key : params.keySet()) {
            String value = params.get(key);

            parameters += EncodingUtil.base64Encode(Blob.valueOf(key));
            parameters += '=';
            parameters += EncodingUtil.base64Encode(Blob.valueOf(value));
            parameters += '&';
        }

        return String.valueOf(parameters).removeEnd('&');
    } 

    private String generateToken() {
        String token = 'Username='+userName+'&';
        token += 'ApiKey='+apiKey+'&';
        token += 'GenDT='+DateTime.now();

        return initCipher(token);
    }

    private String initCipher(String token) {     
        Blob ek = Blob.valueOf(encryptionKey);
        Blob iv = Blob.valueOf(vector);
        Blob data = Blob.valueOf(token);

        return String.valueOf(Crypto.encrypt('AES256', ek, iv, data));
    }

    private Object auraError(String message) {
        AuraHandledException auraError = new AuraHandledException(message);
        auraError.setMessage(message);
        throw auraError;         
    }      

}

It appears to me that I need to do a POST request and encrypt certain values only through a URL with parameters. No body is needed. Where I'm confused is if you download the java example, and go to the file

src > com > sfax > rest > security > SecurityToken.java

you can see that they are calling initCiphers() on initialization of SecurityToken. They then call calling generateToken(), which in turn uses encrypt() that uses the initialized cipher with the token information.

I'm confused on how much the apex crypto class can do for me versus what I need to code myself. The code above, when fired, times out.

Any resources on what type of API this is would be appreciated. I have not come across anything like this on Trailhead.

  • Their crypto is kind of weird, but the timeout should be independent of that. Are you able to get a successful authentication through to any endpoint? The "send fax" endpoint might not be the best experimentation point. – David Reed Mar 28 at 1:01
  • @DavidReed it looks like they want you to authenticate and 'send fax' at the same time. There doesn't appear to be an authentication endpoint. I'm not sure what would qualify as the best endpoint from their docs. – Tyler Zika Mar 28 at 1:14
  • 1
    I was wondering if maybe you could call something read-only like ReceiveOutboundFax as a test of how you're authenticating. I think Apex Crypto will do everything you need, and you're clearly on the right track, but this may take some experimentation. – David Reed Mar 28 at 1:27
  • I agree. I've contacted their support to see if there is a way to debug the time out. Interestingly, when I remove the Content-Length from my header, I get a 400 response saying Content-Length is required. – Tyler Zika Mar 28 at 1:37
0

It's just a REST API, but it has an authentication mechanism that to my eyes is fairly odd and fairly broken (I'm not a professional cryptographer, just a layman with a copy of Applied Cryptography and too much free time). It's kind of like a webhook, but not really, and I suspect based on the sample code that it was implemented by someone without a lot of cryptography experience. In particular, their key derivation function is just plain wrong.

That said, it looks to me like (a) you are on the right track and (b) pretty much everything you need is in the Crypto class. The padding that class uses should be compatible with the padding this service wants.

Cryptography-wise, I think your primary issues are going to be getting your Initialization Vector and Key represented in the right format.

    Blob ek = Blob.valueOf(encryptionKey);
    Blob iv = Blob.valueOf(vector);

This might be what you want, but it's going to depend on how you configured the remote service. Based on their sample code, your key and initialization vector should be exactly 32 and 16 bytes, respectively - what I don't know from looking at the sample code is how you populate those values in their UI, so I'm not sure if we're looking at ASCII strings or hashed binary representations here - but I suspect you're just providing ASCII strings here.

I'd try that first - just pop testtesttesttest in here for your IV and twice that for your key, and the same in the service, and see if you can get a successfully authenticated message through.

The only other potential issue I see on a a review is that you want to make sure your datetime stamp yields exactly the format they're looking for. I don't think the implicit cast you're doing will come out right.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.