AMS DocumentationAMS

Loading Remote Data

It is possible to load data into an AMS message from an external web application or service. This can be used as a way to look up records from external databases.

To allow AMS to load data from an external web application you must provide a web service accessible over the internet using HTTP or HTTPS that accepts a XML POST request and replies with a correctly formatted XML response.

General Request - Response Process

Remote Load Process

  1. User selects Remote Load command from the phone menu.
  2. Current data in the message is sent to the AMS message gateway.
  3. This data is transformed into XML.
  4. The message XML is wrapped inside a XML tag with name “remoteLoadRequest”
  5. An optional “authKey” attribute is added to the “remoteLoadRequest” element and as a custom HTTP header with name “x-ams-auth-key”. This key is an encrypted version of the key provided in the Remote Load command definition, details of the format are provided below.
  6. The XML is sent as an HTTP form POST to the url provided in the Remote Load command definition.
  7. The response from the remote server, if successful, is read and parsed by the AMS gateway into binary for merging into the current message on the phone. The response from the remote server must have content type “application/xml” and be wrapped in a “remoteLoadResponse” element.
  8. The data is merged with the current message on the phone and the screen refreshed. Data from the response will replace any existing data in the message.

Verifying Hashed Auth Keys

The general process used to verify the hashed value of the auth key is:

  1. Get the hash size in bytes for the algorithm used (SHA256 by default)
  2. Decode the auth key from Base64
  3. Extract the salt
  4. Compute the hash of the auth key using the salt
  5. Compare the generated hash with the one provided in the request

The general process used to compute the hashed value of the auth key is:

  1. Generate a random salt of any size, or use one provided
  2. Convert your auth key into bytes using UTF8 encoding
  3. Combine your encoded key with your salt
  4. Hash the combined result using SHA256 algorithm
  5. Combine your hash result with your salt
  6. Encode the combined hash and salt with Base64 encoding

.Net Sample

This is a basic sample outlining the above process in C# targeting .Net 3.5. It is only for illustration and is not suitable for production use.

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
static Boolean verifyHash(HashAlgorithm algorithm, string text, string hash)
{
int hashSizeInBits = algorithm.HashSize;
int hashSizeInBytes = hashSizeInBits / 8;

    if (!String.IsNullOrWhiteSpace(hash) && !String.IsNullOrWhiteSpace(text))
    {

        byte[] hashWithSaltBytes = Convert.FromBase64String(hash);

        byte[] saltBytes = new byte[hashWithSaltBytes.Length - hashSizeInBytes];

        for (int i = 0; i < saltBytes.Length; i++)
        {
            saltBytes[i] = hashWithSaltBytes[hashSizeInBytes + i];
        }

        string computedHash = computeHash(algorithm, text, saltBytes);

        return computedHash.Equals(hash);
    }

    return false;

}

static string computeHash(HashAlgorithm algorithm, string text, byte[] saltBytes)
{
if (saltBytes == null)
{
int saltSize = 8;
saltBytes = new byte[saltSize];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetNonZeroBytes(saltBytes);
}

    byte[] plainTextBytes = Encoding.UTF8.GetBytes(text);
    byte[] plainTextWithSaltBytes = new byte[plainTextBytes.Length + saltBytes.Length];

    for (int i = 0; i < plainTextBytes.Length; i++)
    {
        plainTextWithSaltBytes[i] = plainTextBytes[i];
    }

    for (int i = 0; i < saltBytes.Length; i++)
    {
        plainTextWithSaltBytes[plainTextBytes.Length + i] = saltBytes[i];
    }

    byte[] hashBytes = algorithm.ComputeHash(plainTextWithSaltBytes);

    byte[] hashWithSaltBytes = new byte[hashBytes.Length + saltBytes.Length];

    for (int i = 0; i < hashBytes.Length; i++)
    {
        hashWithSaltBytes[i] = hashBytes[i];
    }

    for (int i = 0; i < saltBytes.Length; i++)
    {
        hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];
    }

    string hashValue = Convert.ToBase64String(hashWithSaltBytes);

    return hashValue;

}

Java Sample

This is a basic sample outlining the above process in Java. It is only for illustration and is not suitable for production use. The Apache Commons Lang and Codec libraries are used.

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
public static String computeHash(final HashAlgorithm algorithm, final String text, final byte[] salt ) {

    byte[] saltBytes = null;

    if (salt == null) {

        int saltSize = 8;
        SecureRandom rand = new SecureRandom();
        saltBytes = new byte[saltSize];
        rand.nextBytes(saltBytes);

    } else {
        saltBytes = salt;
    }

    byte[] plainTextBytes = org.apache.commons.codec.binary.StringUtils.getBytesUtf8(text);
    byte[] plainTextWithSaltBytes = ArrayUtils.addAll(plainTextBytes, saltBytes);

    byte[] hashBytes = null;

    if (algorithm.equals(HashAlgorithm.SHA1)) {
        hashBytes = DigestUtils.sha(plainTextWithSaltBytes);
    } else if (algorithm.equals(HashAlgorithm.SHA256)) {
        hashBytes = DigestUtils.sha256(plainTextWithSaltBytes);
    } else if (algorithm.equals(HashAlgorithm.SHA384)) {
        hashBytes = DigestUtils.sha384(plainTextWithSaltBytes);
    } else if (algorithm.equals(HashAlgorithm.SHA512)) {
        hashBytes = DigestUtils.sha512(plainTextWithSaltBytes);
    } else if (algorithm.equals(HashAlgorithm.MD5)) {
        hashBytes = DigestUtils.md5(plainTextWithSaltBytes);
    } else {
        hashBytes = DigestUtils.md5(plainTextWithSaltBytes);
    }

    if (hashBytes != null) {
        byte[] hashBytesWithSalt = ArrayUtils.addAll(hashBytes, saltBytes);
        String hashValue = new String(Base64.encodeBase64(hashBytesWithSalt)); // avoid CRLF
        return hashValue;
    }

    return null;

}

public static boolean verifyHash(final HashAlgorithm algorithm, final String text, final String hash) {

    int hashSizeInBits = 0;

    if (algorithm.equals(HashAlgorithm.SHA1)) {
        hashSizeInBits = 160;
    } else if (algorithm.equals(HashAlgorithm.SHA256)) {
        hashSizeInBits = 256;
    } else if (algorithm.equals(HashAlgorithm.SHA384)) {
        hashSizeInBits = 384;
    } else if (algorithm.equals(HashAlgorithm.SHA512)) {
        hashSizeInBits = 512;
    } else if (algorithm.equals(HashAlgorithm.MD5)) {
        hashSizeInBits = 128;
    } else {
        hashSizeInBits = 128;
    }

    int hashSizeInBytes = hashSizeInBits/8;


    if (StringUtils.isNotBlank(hash) && StringUtils.isNotBlank(text)) {

        byte[] hashWithSaltBytes = Base64.decodeBase64(hash);

        byte[] saltBytes = new byte[hashWithSaltBytes.length - hashSizeInBytes];

        for (int i = 0; i < saltBytes.length; i++) {
            saltBytes[i] = hashWithSaltBytes[hashSizeInBytes + i];
        }

        String computedHash = computeHash(algorithm, text, saltBytes);

        return computedHash.equals(hash);
    }

    return false;

}