Skip to main content

Java HMAC signing example

This is a Java example of how to enable HMAC signing within your application. The example implementation of the HMAC signing algorithm allows you to authenticate with the Veracode APIs.

HmacRequestSigner.java

package com.veracode.hmac_request_signing;

import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Locale;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

public final class HmacRequestSigner {

// Included in the signature to inform Veracode of the signature version.
private static final String VERACODE_REQUEST_VERSION_STRING = "vcode_request_version_1";

// Expected format for the unencrypted data string.
private static final String DATA_FORMAT = "id=%s&host=%s&url=%s&method=%s";

// Expected format for the Authorization header.
private static final String HEADER_FORMAT = "%s id=%s,ts=%s,nonce=%s,sig=%s";

// Expect prefix to the Authorization header.
private static final String VERACODE_HMAC_SHA_256 = "VERACODE-HMAC-SHA-256";

// HMAC encryption algorithm.
private static final String HMAC_SHA_256 = "HmacSHA256";

// Charset to use when encrypting a string.
private static final String UTF_8 = "UTF-8";

// A cryptographically secure random number generator.
private static final SecureRandom secureRandom = new SecureRandom();

// Private constructor.
private HmacRequestSigner() {
/*
* This is a utility class that should only be accessed through its
* static methods.
*/
}

/**
* Entry point for HmacRequestSigner. Returns the value for the
* Authorization header for use with Veracode APIs when provided an API ID,
* secret key, and target URL.
*
* @param id
* An API id for authentication
* @param key
* The secret key corresponding to the API ID
* @param url
* The URL of the called API, including query parameters
*
* @return The value to be put in the Authorization header
*
* @throws UnsupportedEncodingException
* @throws IllegalStateException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
*/
public static String getVeracodeAuthorizationHeader(final String id, final String key, final URL url, final String httpMethod)
throws InvalidKeyException, NoSuchAlgorithmException, IllegalStateException, UnsupportedEncodingException {
final String urlPath = (url.getQuery() == null) ? url.getPath() : url.getPath().concat("?").concat(url.getQuery());
final String data = String.format(DATA_FORMAT, id, url.getHost(), urlPath, httpMethod);
final String timestamp = String.valueOf(System.currentTimeMillis());
final String nonce = DatatypeConverter.printHexBinary(generateRandomBytes(16)).toLowerCase(Locale.US);
final String signature = getSignature(key, data, timestamp, nonce);
return String.format(HEADER_FORMAT, VERACODE_HMAC_SHA_256, id, timestamp, nonce, signature);
}

/*
* Generate the signature expected by the Veracode platform by chaining
* encryption routines in the correct order.
*/
private static String getSignature(final String key, final String data, final String timestamp, final String nonce)
throws InvalidKeyException, NoSuchAlgorithmException, IllegalStateException, UnsupportedEncodingException {
final byte[] keyBytes = DatatypeConverter.parseHexBinary(key);
final byte[] nonceBytes = DatatypeConverter.parseHexBinary(nonce);
final byte[] encryptedNonce = hmacSha256(nonceBytes, keyBytes);
final byte[] encryptedTimestamp = hmacSha256(timestamp, encryptedNonce);
final byte[] signingKey = hmacSha256(VERACODE_REQUEST_VERSION_STRING, encryptedTimestamp);
final byte[] signature = hmacSha256(data, signingKey);
return DatatypeConverter.printHexBinary(signature).toLowerCase(Locale.US);
}

// Encrypt a string using the provided key.
private static byte[] hmacSha256(final String data, final byte[] key)
throws NoSuchAlgorithmException, InvalidKeyException, IllegalStateException, UnsupportedEncodingException {
final Mac mac = Mac.getInstance(HMAC_SHA_256);
mac.init(new SecretKeySpec(key, HMAC_SHA_256));
return mac.doFinal(data.getBytes(UTF_8));
}

// Encrypt a byte array using the provided key.
private static byte[] hmacSha256(final byte[] data, final byte[] key)
throws NoSuchAlgorithmException, InvalidKeyException {
final Mac mac = Mac.getInstance(HMAC_SHA_256);
mac.init(new SecretKeySpec(key, HMAC_SHA_256));
return mac.doFinal(data);
}

// Generate a random byte array for cryptographic use.
private static byte[] generateRandomBytes(final int size) {
final byte[] key = new byte[size];
secureRandom.nextBytes(key);
return key;
}

}

Main.java

package com.veracode.hmac_request_signing;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.json.JSONException;
import org.json.JSONObject;

public class Main {

private static final String URL_BASE = "api.veracode.com";
private static final String URL_PATH = "/appsec/v1/applications/";
private static final String GET = "GET";
private static final String APP_GUID = "8b86411e-65f9-4224-948a-64559c777d10";
private static final String ACCESS_KEY_ID = "dbb6f2a2ed0b6890bbd32e949f72c8c8";
private static final String SECRET_ACCESS_KEY = "530da152f87e5530c82f786907fbc74b09a6894785a78bab3891632ba69325400a40713bdc11d2a6d2d1c3969431281c0a73f455a53c0ed5ea0756e9c54f366c";

/**
* The main method for our demo. This makes a simple API call using our example HMAC signing class
* and writes the response to the output stream.
*
* @param args command line arguments - ignored
*/
public static void main(final String[] args) {
try {
/*
* Combine the URL base with the specific URL endpoint we wish to access.
* This is REST, so the GUID we are accessing is in the URL.
*/
final URL applicationsApiUrl = new URL("https://" + URL_BASE + URL_PATH + APP_GUID);

/*
* Now we use the url above and our example HMAC signer class to generate a Veracode HMAC header for later use.
*/
final String authorizationHeader = HmacRequestSigner.getVeracodeAuthorizationHeader(ACCESS_KEY_ID, SECRET_ACCESS_KEY, applicationsApiUrl, GET);

/*
* Here we are using Java built in HTTPS protocols to handle making a call to the API's URL.
* We also set the request method to GET.
*/
final HttpsURLConnection connection = (HttpsURLConnection) applicationsApiUrl.openConnection();
connection.setRequestMethod(GET);

/*
* This is where we add the Authorization header with the value returned by our example HMAC signer class.
*/
connection.setRequestProperty("Authorization", authorizationHeader);

/*
* Now we just need to make the actual call by opening up the response stream and read from it.
*/
try (InputStream responseInputStream = connection.getInputStream()) {
readResponse(responseInputStream);
}
} catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) {
e.printStackTrace();
}
}

/*
* A simple method to read an input stream (containing JSON) to System.out.
*/
private static void readResponse(InputStream responseInputStream) throws IOException, JSONException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] responseBytes = new byte[16384];
int x = 0;
while ((x = responseInputStream.read(responseBytes, 0, responseBytes.length)) != -1) {
outputStream.write(responseBytes, 0, x);
}
outputStream.flush();
System.out.println((new JSONObject(outputStream.toString())).toString(4));
}

}