package com.spec.extender.updater; import java.io.UnsupportedEncodingException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.SimpleTimeZone; import java.util.StringTokenizer; import com.spec.extender.exception.HeaderUpdaterException; import com.spec.extender.exception.UnimplementedException; import com.spec.extender.util.CryptUtil; import com.spec.extender.util._debug; public class AWSUpdater extends BaseUpdater { final static String REGION = "us-east-1"; final static String SERVICE = "AWSCognitoIdentityService"; final static String X_TARGET = "X-Amz-Target"; static final String ALGORITHM = "AWS4-HMAC-SHA256"; static final String TERMINATOR = "aws4_request"; private String serviceBaseURL; private String clientID; private String hmacKey; private String signaturePlaceholder; public AWSUpdater(String _serviceBaseURL, String _clientID, String _hmacKey, String _signaturePlaceholder){ this.serviceBaseURL = _serviceBaseURL; this.hmacKey = _hmacKey; this.clientID = _clientID; this.signaturePlaceholder = _signaturePlaceholder; } public static void main(String args[]){ // AWSHeaderUpdater ahu = new AWSHeaderUpdater(); // ArrayList<String> awsHeaders = ahu.createAWSHeader(); // ahu.retrieveSignedHeaders(awsHeaders); } public String getClientID(){ return this.clientID; } public String getHmacKey() { return this.hmacKey; } @Override public String getSignaturePlaceholder() { return this.signaturePlaceholder; } @Override public String getServiceBaseURL() { return this.serviceBaseURL; } /** * Sample request: POST / HTTP/1.1 Host: cib.us-east-1.amazonaws.com Accept-Encoding: identity Content-Length: 217 Content-Encoding: amz-1.0 User-Agent: aws-cli/1.4.4 Python/2.7.8 Linux/3.2.6 X-Amz-Date: 20140927T182907Z X-Amz-Target: AWSCognitoIdentityService.CreateIdentityPool Content-Type: application/x-amz-json-1.1 Authorization: AWS4-HMAC-SHA256 Credential=abc/20140927/us-east-1/cognito-identity/aws4_request, SignedHeaders=content-encoding;content-type;host;user-agent;x-amz-date;x-amz-target, Signature=f5d9219160b2bd57fbe48e2972e2ee306dbbbca99322c1a948b1f425cf5da32e {"IdentityPoolName": "paul_\"", "DeveloperProviderName": "provider", "SupportedLoginProviders": {"name": "paul.com", "value": "1234"}, "AllowUnauthenticatedIdentities": true} http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python * @throws Exception */ protected List<String> updateHeaders(List<String> headers, String requestBody){ throw new UnimplementedException(this.getClass().getName() + ": method: updateHeaders"); } protected String updateBody(List<String> headers, String requestBody){ throw new UnimplementedException(this.getClass().getName() + ": method: requestBody"); } /** * canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash * @throws UnsupportedEncodingException * */ public String buildCanonicalRequest(List<String> headers, String requestBody) throws UnsupportedEncodingException{ String[] methodCanonicalUQ = buildMethodCanonicalURIQuery(headers.get(0)); String method = methodCanonicalUQ[0].trim(); String canonicalURI = methodCanonicalUQ[1].trim(); String canonicalQuery = methodCanonicalUQ[2].trim(); String signedHeaders = retrieveSignedHeaders(retrieveAuthHeader(headers)); String canonicalHeaders = buildCanonicalHeaders(signedHeaders, headers); String payloadHash = CryptUtil.doSha256(requestBody.trim()); StringBuffer canonicalRequest = new StringBuffer(); canonicalRequest.append(method + "\n" + canonicalURI + "\n" + canonicalQuery + "\n" + canonicalHeaders + "\n" + signedHeaders + "\n" + payloadHash); return canonicalRequest.toString(); } public String buildCanonicalHeaders(String signedHeaders, List<String> headers){ ArrayList<String> signedHeaderList = AWSUpdater.retrieveSignedHeaderList(signedHeaders); StringBuffer canonicalHeaders = new StringBuffer(); for (String signedHeader : signedHeaderList){ canonicalHeaders.append(signedHeader + ":" + retrieveCaseInsensitiveHeader(headers, signedHeader) + "\n"); } return canonicalHeaders.toString(); } public String[] buildMethodCanonicalURIQuery(String requestMUP){ String[] mup = getRequestMethodUrlProtocol(requestMUP); String method = mup[0].trim(); String URI = mup[1].trim(); String canonicalURI = URI; String canonicalQueryString = ""; int queryIndex = URI.indexOf('?'); if (queryIndex > 0){ canonicalURI = URI.substring(0, queryIndex); canonicalQueryString = URI.substring(queryIndex, URI.length()); } String[] methodCanonicalUQ = new String[3]; methodCanonicalUQ[0]= method; methodCanonicalUQ[1]= canonicalURI; methodCanonicalUQ[2]= canonicalQueryString; return methodCanonicalUQ; } public String doSig(List<String> headers, String requestBody, String hmacKey) throws Exception{ String sigPayload = buildCanonicalRequest(headers, requestBody.trim()); Date sigDateStamp = new Date(); String regionName = retriveRegion(headers); String serviceName = retriveService(headers); byte[] sigKey = getSignatureKey(hmacKey, sigDateStamp, regionName, serviceName); byte[] sig = CryptUtil.doHmacSHA256(sigPayload, sigKey); String sigStr = new String(sig, "UTF-8"); return sigStr; } /** * This method is cloned from http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-java * @param sigKey: AWS acces key * @param dateStamp * @param regionName * @param serviceName * @return * @throws Exception */ private byte[] getSignatureKey(String sigKey, Date dateStamp, String regionName, String serviceName) throws Exception { byte[] kSecret = ("AWS4" + sigKey).getBytes("UTF8"); byte[] kDate = CryptUtil.doHmacSHA256(getDateTimeStamp(dateStamp), kSecret); byte[] kRegion = CryptUtil.doHmacSHA256(regionName, kDate); byte[] kService = CryptUtil.doHmacSHA256(serviceName, kRegion); byte[] kSigning = CryptUtil.doHmacSHA256("aws4_request", kService); return kSigning; } /** * This method is cloned from AWS4Signer.java * @param date * @return */ private String getDateTimeStamp(Date date) { return dateTimeFormat.get().format(date); } /** * This method is cloned from AWS4Signer.java * @param date * @return */ private ThreadLocal<SimpleDateFormat> dateTimeFormat = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { final SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); dateTimeFormat.setTimeZone(new SimpleTimeZone(0, "UTC")); return dateTimeFormat; } }; /** * * @param timeOffset * @return */ protected Date getSignatureDate(int timeOffset) { Date dateValue = new Date(); if (timeOffset != 0) { long epochMillis = dateValue.getTime(); epochMillis -= timeOffset*1000; dateValue = new Date(epochMillis); } return dateValue; } //dynamodb.us-west-2.amazonaws.com public static String retriveRegion(List<String> headers){ return REGION; } // X-Amz-Target: AWSCognitoIdentityService.CreateIdentityPool public String retriveService(List<String> headers){ String xTargetHeader = retrieveHeader(headers, X_TARGET); String service = xTargetHeader.substring(0, xTargetHeader.indexOf('.')); return service; } public static String retrieveSignedHeaders(String authHeader){ _debug.println("authHeader for retrieve signed headers: \n" + authHeader); String signedHeaderString = null; StringTokenizer st = new StringTokenizer(authHeader, ","); while (st.hasMoreTokens()){ String auth = st.nextToken().trim(); // _debug.print("SignedHeader: \n\t" + authHeader); if (auth.startsWith("SignedHeaders=")){ // _debug.println("SignedHeaders: " + auth); signedHeaderString = auth.substring("SignedHeaders=".length()); _debug.println("signedHeaderString: " + signedHeaderString); return signedHeaderString; } } throw new HeaderUpdaterException("no signed headers are reretrieved from auth header!"); } /** * AWS4-HMAC-SHA256 Credential=AKIAJB4CDXCM47C3NGHQ/20140927/us-east-1/cognito-identity/aws4_request, SignedHeaders=content-encoding;content-type;host;user-agent;x-amz-date;x-amz-target, Signature=f5d9219160b2bd57fbe48e2972e2ee306dbbbca99322c1a948b1f425cf5da32e * @param headers * @return */ public static ArrayList<String> retrieveSignedHeaderList(String signedHeaders){ // _debug.debug("authNHeader: " + authNHeader); ArrayList<String> signedHeaderList = new ArrayList<String>(); StringTokenizer _st = new StringTokenizer(signedHeaders, ";"); while (_st.hasMoreElements()){ signedHeaderList.add(_st.nextToken().trim()); } _debug.printlnStringCollection(signedHeaderList); return signedHeaderList; } }