A java helper utilities that form HTTP security header for authentication and verification
Include this helper class in your java project to perform API Security operations
This project use Maven or Gradle as its build and management tools
- Download and Install Maven (3.5.0 or above)
- Java (1.8)
Option 1: Compile and package into JAR
mvn package
The compiled jar file will be located in the target folder
- java-apex-api-security-.jar
- java-apex-api-security--jar-with-dependencies.jar (this includes log4j libraries)
Import this jar file into your java classpath to use the utility class
Option 2: Compile and install the package into your local maven repository
mvn install
-
The compiled package file will be installed under com.api.util.ApiSecurity in the .m2 repo
-
For Maven Client Project setup, add the following dependencies to your pom.xml file :
<dependency>
<groupId>com.api.util</groupId>
<artifactId>ApiSecurity</artifactId>
<version>2.1.3</version>
</dependency>
Note:
- This project is leveraging on Log4j Version2 framework for the logging. If you are using logging implementation other than Log4j Version2 , you can change to other type of implementation such as nop,simple,jdk14,logback. You could replace the following xml in pom.xml.
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.17.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.0</version>
</dependency>
Pull centralised Unit Test-cases from the following Github url: https://github.com/GovTechSG/test-suites-apex-api-security/tree/master with Mavem command (in project root folder)
mvn scm:checkout -D checkoutDirectory=src/main/resources/test-suites
To execute unit-test with Maven command (in project root folder)
mvn test
- Download and Install Gradle (4.0 or above)
- Java (1.8)
As some of the test cases contains UTF-8 characters, you have to set following property before the JVM execute the Gradle Daemon.
export GRADLE_OPTS="-Dfile.encoding=utf-8"
Option 1: Compile and package into JAR
gradle clean build
To test with Jacoco and publish a html report
gradle test jacocoTestReport
The compiled jar file will be located in the build/libs folder
- java-apex-api-security-2.1.3.jar
Import this jar into your java classpath to use the utility class
Option 2: Compile and install the package into your local maven repository
-
Refer to Maven Guide > Build > Option 2, for the maven repo installation (excluding Maven Client Project setup)
-
For Gradle Client Project setup, add the following dependencies to your build.gradle file :
repositories {
mavenLocal()
}
dependencies {
compile group: 'com.api.util', name: 'ApiSecurity', version: '2.1.3'
}
Append this signature token into the Authorization header of the HTTP request.
Apex_l1_eg realm="https://XYZ.api.gov.sg/abc/def", apex_l1_eg_app_id="APP_ID", apex_l1_eg_nonce="SOME_RANDOM_STRING", apex_l1_eg_signature_method="HMACSHA256", apex_l1_eg_timestamp="SOME_TIMESTAMP", apex_l1_eg_version="1.0", apex_l1_eg_signature="SOME_SIGNATURE"
Authorization: Apex_l1_eg realm="https://XYZ.api.gov.sg/abc/def", apex_l1_eg_app_id="APP_ID", apex_l1_eg_nonce="SOME_RANDOM_STRING", apex_l1_eg_signature_method="HMACSHA256", apex_l1_eg_timestamp="SOME_TIMESTAMP", apex_l1_eg_version="1.0", apex_l1_eg_signature="SOME_SIGNATURE"
The HTTP method, i.e. GET
, POST
, etc.
The full API endpoint (with query parameters if any).
The APEX App ID.
The APEX App secret. Not required if you want to use L2 authentication with SHA256WITHRSA.
Data which should be passed in the request (for POST
requests
usually). For GET
requests, this value is not necessary.
The password of the keystore. Not required for L1.
The alias of the keystore. Not required for L1.
The p12 file path. Not required for L1.
The random generated string which to be used to generate the token. If not set, a new random string will be generated.
Timestamp which should be used to generate the token. Not required if you want to use the current timestamp.
try {
AuthParam authParam = new AuthParam();
authParam.url = URI.create("https://<<URL>>/api/v1");
authParam.httpMethod = "GET";
authParam.appName = "<<appId>>";
String certFileName = "certificates/ssc.alpha.example.com.p12";
String password = "<<passphrase>>";
String alias = "alpha";
authParam.privateKey = ApiSigning.getPrivateKey(certFileName, password, alias);
AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam);
// Add this signature value to the authorization header when sending the request.
// authorizationToken.getToken();
}
catch (ApiUtilException e)
{
e.printStackTrace();
}
try {
AuthParam authParam = new AuthParam();
authParam.url = URI.create("https://<<URL>>/api/v1");
authParam.httpMethod = "POST";
authParam.appName = "<<appId>>";
String certFileName = "certificates/ssc.alpha.example.com.p12";
String password = "<<passphrase>>";
String alias = "alpha";
authParam.privateKey = ApiSigning.getPrivateKey(certFileName, password, alias);
AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam);
// Add this signature value to the authorization header when sending the request.
// authorizationToken.getToken();
}
catch (ApiUtilException e)
{
e.printStackTrace();
}
The ApiSecurity Library provide the utility class ApiList and FormList to construct request Query String and Form Data.
Generate QueryString
ApiList queryData = new ApiList();
queryData.add("clientId", "1256-1231-4598");
queryData.add("accountStatus", "active");
queryData.add("txnDate", "2017-09-29");
String queryString = queryData.toString(true);
String baseUrl = String.format("https://example.com/resource?%s", queryString);
// https://example.com/resource?accountStatus=active&clientId=1256-1231-4598&txnDate=2017-09-29
Generate FormData
FormList formData = new FormList();
formData.add("phoneNo", "+1 1234 4567 890");
formData.add("street", "Hellowood Street");
formData.add("state", "AP");
String formDataString = formData.toFormData();
// phoneNo=%2B1+1234+4567+890&street=Hellowood+Street&state=AP
String signingUrl = "https://<<URL>>/api/v1/?param1=first¶m2=123";
try {
FormList formData = new FormList();
formData.add("param1", "data1");
AuthParam authParam = new AuthParam();
authParam.url = URI.create(https://<<URL>>/api/v1");
authParam.httpMethod = "POST";
authParam.appName = "<<appId>>";
authParam.nonce = "<<nonce>>";
authParam.timestamp = "<<timestamp>>";
authParam.formData = formData;
AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam);
assertEquals(expectedBaseString, authorizationToken.getBaseString());
}
catch (ApiUtilException e)
{
e.printStackTrace();
}
NOTE
For formData parameter used for Signature generation, the key value parameters do not need to be URL encoded, When your client program is making the actual HTTP POST call, the key value parameters has to be URL encoded (refer to formPostData)
FormList formData = new FormList();
formData.add("phoneNo", "+1 1234 4567 890");
formData.add("street", "Hellowood Street");
formData.add("state", "AP");
try {
AuthParam authParam = new AuthParam();
authParam.url = URI.create("https://<<URL>>/api/v1");
authParam.httpMethod = "POST";
authParam.appName = "<<appId>>";
authParam.appSecret = "<<appSecret>>";
authParam.formData = formData;
// get the authorization token for L1
AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam);
System.out.println("BaseString :: " + authorizationToken.getBaseString());
System.out.println("Authorization Token :: " + authorizationToken.getToken());
// make api call with authorizationToken.getToken()
}
catch (ApiUtilException e)
{
e.printStackTrace();
}
FormList formData = new FormList();
formData.add("phoneNo", "+1 1234 4567 890");
formData.add("street", "Hellowood Street");
formData.add("state", "AP");
ApiList queryData = new ApiList();
queryData.add("clientId", "1256-1231-4598");
queryData.add("accountStatus", "active");
queryData.add("txnDate", "2017-09-29");
String queryString = queryData.toString(true);
String baseUrl = String.format("https://<<URL>>/api/v1?%s", queryString);
try {
AuthParam authParam = new AuthParam();
authParam.url = URI.create(baseUrl);
authParam.httpMethod = "POST";
authParam.appName = "<<appId>>";
String certFileName = "certificates/ssc.alpha.example.com.p12";
String password = "<<passphrase>>";
String alias = "alpha";
authParam.privateKey = ApiSigning.getPrivateKey(certFileName, password, alias);
authParam.formData = formData;
// get the authorization token for L2
AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam);
System.out.println("BaseString :: " + authorizationToken.getBaseString());
System.out.println("Authorization Token :: " + authorizationToken.getToken());
// make api call with authorizationToken.getToken()
}
catch (ApiUtilException e)
{
e.printStackTrace();
}
(for cross zone api from internet to intranet)
FormList formData = new FormList();
formData.add("phoneNo", "+1 1234 4567 890");
formData.add("street", "Hellowood Street");
formData.add("state", "AP");
ApiList queryData = new ApiList();
queryData.add("clientId", "1256-1231-4598");
queryData.add("accountStatus", "active");
queryData.add("txnDate", "2017-09-29");
String queryString = queryData.toString(true);
try {
AuthParam authParam_WWW = new AuthParam();
String baseUrl_WWW = String.format("https://<<URL_WWW>>/api/v1?%s", queryString);
authParam_WWW.url = URI.create(baseUrl_WWW);
authParam_WWW.httpMethod = "POST";
authParam_WWW.appName = "<<appId_WWW>>";
String certFileName = "certificates/ssc.alpha.example.com.p12";
String password = "<<passphrase>>";
String alias = "alpha";
authParam_WWW.privateKey = ApiSigning.getPrivateKey(certFileName, password, alias);
authParam_WWW.formData = formData;
AuthParam authParam_WOG = new AuthParam();
authParam_WOG.httpMethod = "POST";
authParam_WOG.appName = "<<appId_WOG>>";
authParam_WOG.appSecret = "<<appSecret_WOG>>";
String baseUrl_WOG = String.format("https://<<URL_WOG>>/api/v1?%s", queryString);
authParam_WOG.url = URI.create(baseUrl1);
authParam_WWW.nextHop = authParam_WOG;
// get the authorization token for L21
AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam_WWW);
System.out.println("BaseString :: " + authorizationToken.getBaseString());
System.out.println("Authorization Token :: " + authorizationToken.getToken());
// make api call with authorizationToken.getToken()
}
catch (ApiUtilException e)
{
e.printStackTrace();
}
(for cross zone api from intranet to internet)
FormList formData = new FormList();
formData.add("phoneNo", "+1 1234 4567 890");
formData.add("street", "Hellowood Street");
formData.add("state", "AP");
ApiList queryData = new ApiList();
queryData.add("clientId", "1256-1231-4598");
queryData.add("accountStatus", "active");
queryData.add("txnDate", "2017-09-29");
String queryString = queryData.toString(true);
try {
AuthParam authParam_WOG = new AuthParam();
authParam_WOG.httpMethod = "POST";
authParam_WOG.appName = "<<appId_WOG>>";
authParam_WOG.appSecret = "<<appSecret_WOG>>";
String baseUrl_WOG = String.format("https://<<URL_WOG>>/api/v1?%s", queryString);
authParam_WOG.url = URI.create(baseUrl1);
AuthParam authParam_WWW = new AuthParam();
String baseUrl_WWW = String.format("https://<<URL_WWW>>/api/v1?%s", queryString);
authParam_WWW.url = URI.create(baseUrl_WWW);
authParam_WWW.httpMethod = "POST";
authParam_WWW.appName = "<<appId_WWW>>";
String certFileName = "certificates/ssc.alpha.example.com.p12";
String password = "<<passphrase>>";
String alias = "alpha";
authParam_WWW.privateKey = ApiSigning.getPrivateKey(certFileName, password, alias);
authParam_WWW.formData = formData;
authParam_WOG.nextHop = authParam_WWW;
// get the authorization token for L12
AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam_WOG);
System.out.println("BaseString :: " + authorizationToken.getBaseString());
System.out.println("Authorization Token :: " + authorizationToken.getToken());
// make api call with authorizationToken.getToken()
}
catch (ApiUtilException e)
{
e.printStackTrace();
}
For more information about contributing PRs and issues, see CONTRIBUTING.md.
See CHANGELOG.md.