You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@wookie.apache.org by sc...@apache.org on 2014/02/18 21:19:13 UTC
svn commit: r1569518 - in /wookie/trunk/wookie-server/src:
main/java/org/apache/wookie/server/security/Hmac.java
main/java/org/apache/wookie/server/security/NonceCache.java
test/java/org/apache/wookie/tests/server/security/HmacTest.java
Author: scottbw
Date: Tue Feb 18 20:19:13 2014
New Revision: 1569518
URL: http://svn.apache.org/r1569518
Log:
Created a HMAC authorization provider for Wookie REST APIs (see WOOKIE-279, and linked discussion with Tien). This will be used in place of "plaintext" API keys in Wookie 2.x
Added:
wookie/trunk/wookie-server/src/main/java/org/apache/wookie/server/security/Hmac.java
wookie/trunk/wookie-server/src/main/java/org/apache/wookie/server/security/NonceCache.java
wookie/trunk/wookie-server/src/test/java/org/apache/wookie/tests/server/security/HmacTest.java
Added: wookie/trunk/wookie-server/src/main/java/org/apache/wookie/server/security/Hmac.java
URL: http://svn.apache.org/viewvc/wookie/trunk/wookie-server/src/main/java/org/apache/wookie/server/security/Hmac.java?rev=1569518&view=auto
==============================================================================
--- wookie/trunk/wookie-server/src/main/java/org/apache/wookie/server/security/Hmac.java (added)
+++ wookie/trunk/wookie-server/src/main/java/org/apache/wookie/server/security/Hmac.java Tue Feb 18 20:19:13 2014
@@ -0,0 +1,293 @@
+/*
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.wookie.server.security;
+
+import java.security.SignatureException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.text.ParsePosition;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.commons.codec.binary.Base64;
+
+/**
+ * Utilities for creating and validating HMAC authentication for REST API calls
+ *
+ * We are using a HMAC scheme very similar to that used by Amazon Web Services.
+ * The canonical form for each request is computed from the HTTP verb, host, URI
+ * and an alpha-sorted array of parameters, including a timestamp and a nonce.
+ *
+ * This canonical string is then signed with the shared secret for the application
+ * using HMAC-SHA256.
+ *
+ * The signature is placed in the Authorization header of the request, preceded by
+ * the public key of the requesting application (the "API key" used in previous APIs)
+ *
+ */
+public class Hmac {
+
+ /**
+ * Date formatter for ISO datetime
+ */
+ private static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"){
+ private static final long serialVersionUID = 7465240007718011363L;
+ public Date parse(String source,ParsePosition pos) {
+ return super.parse(source.replaceFirst(":(?=[0-9]{2}$)",""),pos);
+ }
+ };
+
+ /**
+ * The hashing algorithm to use
+ */
+ private static final String HMAC_ALGORITHM = "HmacSHA256";
+
+ /**
+ * The amount of clock skew we allow for requests - if the timestamp on a
+ * request is older than this, we reject it
+ */
+ private static final long CLOCK_SKEW_ALLOWANCE = 180000L; // allow three minutes for clock skew
+
+ /**
+ * Gets the public key associated with the request
+ * @param request
+ * @return the public key or null if there is no key
+ */
+ public static String getPublicKey(HttpServletRequest request){
+ String header = request.getHeader("Authorization");
+ if (header == null || header.trim().length() == 0 || header.split(" ").length != 2) return null;
+ return header.split(" ")[0];
+ }
+
+ /**
+ * Gets the signature associated with the request
+ * @param request
+ * @return the signture or null if there is no key
+ */
+ public static String getSignature(HttpServletRequest request){
+ String header = request.getHeader("Authorization");
+ if (header == null || header.trim().length() == 0 || header.split(" ").length != 2) return null;
+ return header.split(" ")[1];
+ }
+
+ /**
+ * Validates a signed request
+ */
+ public static boolean isValidSignedRequest(HttpServletRequest request){
+
+ //
+ // Get the header
+ //
+ String auth = request.getHeader("Authorization");
+
+ //
+ // If no auth header, not valid.
+ //
+ if (auth == null) return false;
+
+ //
+ // Split the header into the api key and signature
+ // If either part is missing, or there are additional
+ // parts, the request is not valid.
+ //
+ String apiKey = getPublicKey(request);
+ String signature = getSignature(request);
+ if (apiKey == null || signature == null) return false;
+
+ //
+ // Validate the api public key exists
+ //
+ if (!ApiKeys.getInstance().validate(apiKey)) return false;
+
+ //
+ // Get the API key secret
+ //
+ String secret = ApiKeys.getInstance().getApiKey(apiKey).getEmail();
+
+ //
+ // Check the timestamp. If no timestamp is
+ // provided, the request is not valid
+ //
+ String timestamp = request.getParameter("timestamp");
+ if (timestamp == null) return false;
+
+ //
+ // Parse the timestamp. If its not a valid
+ // datetime, the request is not valid
+ //
+ Date timestampDate = null;
+ try {
+ timestampDate = dateFormat.parse(timestamp);
+ } catch (ParseException e1) {
+ return false;
+ }
+
+ //
+ // Compute the window of validity for the timestamp,
+ // equivalent to now minus an allowance for clock
+ // skew
+ //
+ long now = System.currentTimeMillis();
+ long window = now - CLOCK_SKEW_ALLOWANCE;
+
+ //
+ // Check that the timestamp provided falls within the
+ // allowed range. This is to prevent replay attacks
+ //
+ if ((timestampDate.getTime()) < window){
+ return false;
+ }
+
+ //
+ // Get the nonce used. If there is no nonce, the
+ // request is not valid
+ //
+ String nonce = request.getParameter("nonce");
+ if (nonce == null || nonce.trim().length() == 0) return false;
+
+ //
+ // Check the nonce hasn't been reused lately
+ //
+ if (!NonceCache.getInstance().isValid(nonce)) return false;
+
+ //
+ // Get the canonical request string to validate
+ //
+ String canonicalRequest = toCanonicalForm(request);
+
+ //
+ // Calculate the signature that should have been used
+ //
+ String correctSignature = null;
+ try {
+ correctSignature = getHmac(canonicalRequest, secret);
+ } catch (SignatureException e) {
+ return false;
+ }
+
+ //
+ // Compare the signature hashes. If they match
+ // then the request is valid
+ //
+ if (correctSignature.equals(signature)){
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ /**
+ * Converts a Java Date object to an ISO-formatted date string
+ * @param date
+ * @return the ISO string
+ */
+ public static String getFormattedDate(Date date){
+ return dateFormat.format(date);
+ }
+
+ /**
+ * Converts a HTTP request into its canonical form used for signing
+ */
+ private static String toCanonicalForm(HttpServletRequest request){
+ return getCanonicalRequest(request.getMethod(), request.getHeader("Host"), request.getRequestURI(), getCanonicalParameters(request.getParameterMap()));
+ }
+
+ /**
+ * Gets a canonical request to sign
+ * @param verb the HTTP verb, e.g. POST
+ * @param host the host, e.g. wookie.apache.org
+ * @param uri the URI, e.g. /wookie
+ * @param query the canonical parameters for the request - see getCanonicalParameters
+ * @return the canonical string representation of the request
+ */
+ public static String getCanonicalRequest(String verb, String host, String uri, String query){
+ String canonical = "";
+ verb = verb.toUpperCase();
+ host = host.toLowerCase();
+ uri = uri.toLowerCase();
+ query = query.toLowerCase();
+ canonical = verb + "\n" + host + "\n" + uri + "\n" + query;
+ return canonical;
+ }
+
+ /**
+ * Sorts parameters and returns a querystring in canonical form usable for signing
+ * @param parameterMap
+ * @return a String with all parameters sorted and represented correctly
+ */
+ public static String getCanonicalParameters(@SuppressWarnings("rawtypes") Map parameterMap){
+ String query = "?";
+ ArrayList<String> parameterNames = new ArrayList<String>();
+ @SuppressWarnings("rawtypes")
+ Iterator it = parameterMap.keySet().iterator();
+ while (it.hasNext()) parameterNames.add((String) it.next());
+ Collections.sort(parameterNames);
+ for (String name:parameterNames){
+ if (!query.equals("?")) query += "&";
+ Object value = parameterMap.get(name);
+ if (value instanceof String[]){
+ query += name.toLowerCase() + "=" + ((String[])value)[0].toLowerCase();
+ } else {
+ query += name.toLowerCase() +"="+((String)value).toLowerCase();
+ }
+
+ }
+ return query;
+ }
+
+
+ /**
+ * Computes a HMAC signature.
+ * @param data The data to be signed.
+ * @param key The signing key.
+ * @return The HMAC signature.
+ * @throws java.security.SignatureException when signature generation fails
+ */
+ public static String getHmac(String data, String key)
+ throws java.security.SignatureException
+ {
+ String hmac64;
+ try {
+
+ // get an hmac_sha1 key from the raw key bytes
+ SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_ALGORITHM);
+
+ // get an hmac_sha1 Mac instance and initialize with the signing key
+ Mac mac = Mac.getInstance(HMAC_ALGORITHM);
+ mac.init(signingKey);
+
+ // compute the hmac on input data bytes
+ byte[] hmac = mac.doFinal(data.getBytes());
+
+ // base64-encode the hmac
+ hmac64 = Base64.encodeBase64String(hmac);
+
+ } catch (Exception e) {
+ throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
+ }
+ return hmac64;
+ }
+
+}
Added: wookie/trunk/wookie-server/src/main/java/org/apache/wookie/server/security/NonceCache.java
URL: http://svn.apache.org/viewvc/wookie/trunk/wookie-server/src/main/java/org/apache/wookie/server/security/NonceCache.java?rev=1569518&view=auto
==============================================================================
--- wookie/trunk/wookie-server/src/main/java/org/apache/wookie/server/security/NonceCache.java (added)
+++ wookie/trunk/wookie-server/src/main/java/org/apache/wookie/server/security/NonceCache.java Tue Feb 18 20:19:13 2014
@@ -0,0 +1,85 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.wookie.server.security;
+
+import java.util.LinkedList;
+
+/**
+ * A simple cache for nonces so we can guard against
+ * replay attacks reusing nonces.
+ */
+public class NonceCache {
+
+ /**
+ * The singleton instance
+ */
+ private static NonceCache instance;
+
+ /**
+ * The cache, a simple linked list
+ */
+ private LinkedList<String> cache;
+
+ /**
+ * The number of nonces to hold in the cache
+ */
+ private static final int CACHE_SIZE = 100;
+
+ /**
+ * Private constructor for singleton
+ */
+ private NonceCache(){
+ cache = new LinkedList<String>();
+ }
+
+ /**
+ * Get single instance
+ * @return the NonceCache
+ */
+ public static NonceCache getInstance(){
+ if (instance == null) instance = new NonceCache();
+ return instance;
+ }
+
+ /**
+ * Checks whether a nonce is valid - whether it
+ * has been recently used.
+ * @param nonce
+ * @return true if the nonce is valid, false if it is recycled
+ */
+ public boolean isValid(String nonce){
+
+ //
+ // Has it been used?
+ //
+ if (cache.contains(nonce)) return false;
+
+ //
+ // If not, add it to the end of the cache
+ //
+ cache.addLast(nonce);
+
+ //
+ // If the cache has grown to its max size, pop
+ // off the first nonce
+ //
+ if (cache.size() > CACHE_SIZE) cache.removeFirst();
+ return true;
+ }
+
+
+
+}
Added: wookie/trunk/wookie-server/src/test/java/org/apache/wookie/tests/server/security/HmacTest.java
URL: http://svn.apache.org/viewvc/wookie/trunk/wookie-server/src/test/java/org/apache/wookie/tests/server/security/HmacTest.java?rev=1569518&view=auto
==============================================================================
--- wookie/trunk/wookie-server/src/test/java/org/apache/wookie/tests/server/security/HmacTest.java (added)
+++ wookie/trunk/wookie-server/src/test/java/org/apache/wookie/tests/server/security/HmacTest.java Tue Feb 18 20:19:13 2014
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wookie.tests.server.security;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.security.SignatureException;
+import java.util.Calendar;
+import java.util.Date;
+
+import org.apache.wookie.server.security.Hmac;
+import org.apache.wookie.tests.helpers.MockHttpServletRequest;
+import org.apache.wookie.w3c.util.RandomGUID;
+import org.junit.Test;
+
+public class HmacTest {
+
+ @Test
+ public void basicSignedRequest() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(System.currentTimeMillis())));
+ request.addParameter("nonce", new RandomGUID().toString());
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "TEST "+signature);
+ assertTrue(Hmac.isValidSignedRequest(request));
+ }
+
+ @Test
+ public void signedRequestWithMultipleParameters() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "one two".split(" "));
+ request.addParameter("nonce", new RandomGUID().toString());
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(System.currentTimeMillis())));
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "TEST "+signature);
+ assertTrue(Hmac.isValidSignedRequest(request));
+ }
+
+ @Test
+ public void basicSignedRequestWithCasing() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "WOOKIE.APACHE.org");
+ request.setRequestURI("/");
+ request.setMethod("PoST");
+ request.addParameter("TEST", "test-value");
+ request.addParameter("nonce", new RandomGUID().toString());
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(System.currentTimeMillis())));
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "TEST "+signature);
+ assertTrue(Hmac.isValidSignedRequest(request));
+ }
+
+ @Test
+ public void signedRequestWithNoTimestamp() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("nonce", new RandomGUID().toString());
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "TEST "+signature);
+ assertFalse(Hmac.isValidSignedRequest(request));
+ }
+
+ @Test
+ public void signedRestPostRequest() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/http://notsupported");
+ request.setMethod("POST");
+ request.addParameter("nonce", new RandomGUID().toString());
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(System.currentTimeMillis())));
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", request.getRequestURI(), query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "TEST "+signature);
+ assertTrue(Hmac.isValidSignedRequest(request));
+ }
+
+ @Test
+ public void signedRequestTwoMinutesOld() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/http://notsupported");
+ request.setMethod("POST");
+ request.addParameter("nonce", new RandomGUID().toString());
+ long time = Calendar.getInstance().getTimeInMillis()-120000L; // set to 2 mins ago
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(time)));
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", request.getRequestURI(), query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "TEST "+signature);
+ assertTrue(Hmac.isValidSignedRequest(request));
+ }
+
+ @Test
+ public void signedRequestOneHourOld() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("nonce", new RandomGUID().toString());
+ long time = System.currentTimeMillis()-3600000L; // set to one hour ago
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(time)));
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "TEST "+signature);
+ assertFalse(Hmac.isValidSignedRequest(request));
+ }
+
+ // Set authz header with signature but no key
+ @Test
+ public void signedRequestWithNoApiKey() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("nonce", new RandomGUID().toString());
+ long time = System.currentTimeMillis();
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(time)));
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", signature);
+ assertFalse(Hmac.isValidSignedRequest(request));
+ }
+
+ // Set authz header with extra info
+ @Test
+ public void signedRequestWithExtraInfo() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("nonce", new RandomGUID().toString());
+ long time = System.currentTimeMillis();
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(time)));
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "TEST "+signature+" EXTRASTUFF");
+ assertFalse(Hmac.isValidSignedRequest(request));
+ }
+
+ // Set authz header with key but no signaure
+ @Test
+ public void unsignedRequestWithApiKey() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("nonce", new RandomGUID().toString());
+ long time = System.currentTimeMillis();
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(time)));
+ request.addHeader("Authorization", "TEST");
+ assertFalse(Hmac.isValidSignedRequest(request));
+ }
+
+ // Set authz header with key but no signaure
+ @Test
+ public void unsignedRequestEmptyHeader() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("nonce", new RandomGUID().toString());
+ long time = System.currentTimeMillis();
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(time)));
+ request.addHeader("Authorization", "");
+ assertFalse(Hmac.isValidSignedRequest(request));
+ }
+
+ // Wrong secret for key used to sign request
+ @Test
+ public void signedRequestSignedWithBadSecret() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("nonce", new RandomGUID().toString());
+ long time = System.currentTimeMillis();
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(time)));
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ String signature = Hmac.getHmac(reqString, "wrongkey");
+ request.addHeader("Authorization", "TEST "+ signature);
+ assertFalse(Hmac.isValidSignedRequest(request));
+ }
+
+ @Test
+ public void signedRequestSignedWithBadSignature() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("nonce", new RandomGUID().toString());
+ long time = System.currentTimeMillis();
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(time)));
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "TEST "+ signature+"9");
+ assertFalse(Hmac.isValidSignedRequest(request));
+ }
+
+ @Test
+ public void signedRequestSignedWithBadApiKey() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("nonce", new RandomGUID().toString());
+ long time = System.currentTimeMillis();
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(time)));
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "BANANA "+ signature);
+ assertFalse(Hmac.isValidSignedRequest(request));
+ }
+
+ @Test
+ public void signedRequestWithBadTimeStamp() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("nonce", new RandomGUID().toString());
+ request.addParameter("timestamp", "99999999999999");
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "TEST "+ signature);
+ assertFalse(Hmac.isValidSignedRequest(request));
+ }
+
+ @Test
+ public void unsignedRequest() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("nonce", new RandomGUID().toString());
+ long time = System.currentTimeMillis();
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(time)));
+ assertFalse(Hmac.isValidSignedRequest(request));
+ }
+
+ @Test
+ public void signedRequestWithNoNonce() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ long time = System.currentTimeMillis();
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(time)));
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "TEST "+ signature);
+ assertFalse(Hmac.isValidSignedRequest(request));
+ }
+
+ @Test
+ public void signedRequestWithReusedNonce() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("nonce", "banana");
+ long time = System.currentTimeMillis();
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(time)));
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "TEST "+ signature);
+ assertTrue(Hmac.isValidSignedRequest(request));
+
+ request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("nonce", "banana");
+ time = System.currentTimeMillis();
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(time)));
+ query = Hmac.getCanonicalParameters(request.getParameterMap());
+ reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "TEST "+ signature);
+ assertFalse(Hmac.isValidSignedRequest(request));
+ }
+
+ @Test
+ public void signedRequestEmptyNonce() throws SignatureException{
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Host", "wookie.apache.org");
+ request.setRequestURI("/");
+ request.setMethod("POST");
+ request.addParameter("test", "test-value");
+ request.addParameter("timestamp", Hmac.getFormattedDate(new Date(System.currentTimeMillis())));
+ request.addParameter("nonce", " ");
+ String query = Hmac.getCanonicalParameters(request.getParameterMap());
+ String reqString = Hmac.getCanonicalRequest("POST", "wookie.apache.org", "/", query);
+ String signature = Hmac.getHmac(reqString, "test@127.0.0.1");
+ request.addHeader("Authorization", "TEST "+signature);
+ assertFalse(Hmac.isValidSignedRequest(request));
+ }
+
+}