Access Keys:
Skip to content (Access Key - 0)

Since writing this, the Context.WS has evolved, and we can go ahead slightly differently:

  • A single ticket will be sent from AS to the proxy tool to represent a user-authenticated session
  • If the proxy tool wants elevated priviliges it merely connects using tool-authentication (which must be configured as part of proxy tool configuration)

The lifetime of the Session built from the ticket will be subject to the usual session lifetime restrictions (is requested as part of the loginticket call).

Follow these steps to generate a MAC to compare with the one sent in the POST operation:

  1. Sort all of your post data based on the key name (so if you have timestamp=1235,returnurl=xxx then you will sort these by the key and get returnurl,timestamp)
  2. Append all the values together in that sorted order. (above example would generate a data string of xxx1235)
  3. Append the shared secret to this data. (Continuing the example, if your secret was "secret" then your string would now be "xxx1235secret"
  4. Turn this string into a string of utf-8 bytes - ie. in java byte[]mybytes = mystring.getBytes("UTF-8")(in this example it would still be 78342345secret but if the secret was non-us-ascii then it would obviously be different)
  5. Create a message digest (either MD5 or SHA depending on registered algorithm, default is MD5) from these bytes (In java this would be similar to: byte [] md5hash = MessageDigest.getInstance(digestAlgorithm).digest(mybytes);
  6. Convert the resulting byte array into a base-64 encoded string, removing all the \r and \n characters:
          BASE64Encoder encoder = new BASE64Encoder();
                String result = encoder.encodeBuffer(md5hash);
                result = result.replace( "\r", "" );
                result = result.replace( "\n", "" );
    

    1. check that the timestamp is within n minutes of NOW
    2. check that the nonce argument has not been already used

Here is the sample proxy server that ships with Blackboard Learn. It includes a class ProxyUtil.java with these methods to perform mac validation.

Refer to the source code included with the installation for the final version.
private String generateMac( Map<String,String> args, String secret, String digestAlgorithm )
  {
    try
    {
      if (digestAlgorithm == null)
      {
        digestAlgorithm = "MD5";
      }
      // 1. Sort the posted data based on the key names and then concatenate all of the values together in the sorted order 
      Set<String> argsKeySet = args.keySet();
      List<String> argsKeys = new ArrayList<String>();
      for (String k:argsKeySet) {
        argsKeys.add( k );
      }
      Collections.sort( argsKeys );
      StringBuffer dataValue = new StringBuffer(256);
      for (String key:argsKeys) {
        dataValue.append(args.get( key ));
      }
            // 2. Turn this number into a string and append the shared secret 
            String withSecret = dataValue + secret;
            // 3. Turn this string into a string of utf-8 bytes 
            byte [] utf8bytes = withSecret.getBytes("UTF-8");
            // 4. Create an MD5 message digest from these bytes 
            byte [] md5hash = MessageDigest.getInstance(digestAlgorithm).digest(utf8bytes);
            // 5. Convert the resulting byte array into a base64 encoded string
            BASE64Encoder encoder = new BASE64Encoder();
            String result = encoder.encodeBuffer(md5hash);
            result = result.replace( "\r", "" );
            result = result.replace( "\n", "" );
        } catch (Exception e) {
            e.printStackTrace(); // TODO log
        }

        return new Random().nextInt() + ""; // If we threw any exception just return a random number so we don't 
                             // accidentally send out an empty-string mac or validate against an empty-string mac
    }
    
    public boolean validate(List<String> macArgs , String secret, String mac, String timestamp) {
        String expected = generateMac(macArgs, secret);
        if (expected.equals(mac)) {
            long now = System.currentTimeMillis();
            try {
                long then = new Long(timestamp).longValue();
                if (now <= (then + FIVE_MINUTES))
                    return true;
            } catch (Exception ignore) {
                // Ignore exceptions
            }
        }
        return false;
    }
Adaptavist Theme Builder (4.1.3) Powered by Atlassian Confluence 3.3, the Enterprise Wiki