diff --git a/pom.xml b/pom.xml index 62809a7..9624365 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ org.tomitribe oss-parent - 2 + 5 tomitribe-http-signatures @@ -42,6 +42,12 @@ + + org.apache.tomcat + tomcat-servlet-api + 8.5.5 + provided + junit junit diff --git a/src/main/java/org/tomitribe/auth/signatures/HeaderReader.java b/src/main/java/org/tomitribe/auth/signatures/HeaderReader.java new file mode 100644 index 0000000..334a5c4 --- /dev/null +++ b/src/main/java/org/tomitribe/auth/signatures/HeaderReader.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.tomitribe.auth.signatures; + +import javax.servlet.http.HttpServletRequest; +import java.util.TreeMap; + +public interface HeaderReader { + String read(String header); + + class Servlet implements HeaderReader { + private final HttpServletRequest request; + + public Servlet(final HttpServletRequest request) { + this.request = request; + } + + @Override + public String read(final String header) { + return request.getHeader(header); + } + } + + class Map implements HeaderReader { + private final java.util.Map delegate; + + public Map(final java.util.Map source) { + this.delegate = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + this.delegate.putAll(source); + } + + @Override + public String read(final String header) { + return delegate.get(header); + } + } +} diff --git a/src/main/java/org/tomitribe/auth/signatures/Signatures.java b/src/main/java/org/tomitribe/auth/signatures/Signatures.java index 0f77a2c..85521fa 100644 --- a/src/main/java/org/tomitribe/auth/signatures/Signatures.java +++ b/src/main/java/org/tomitribe/auth/signatures/Signatures.java @@ -17,25 +17,24 @@ package org.tomitribe.auth.signatures; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; +import java.util.Locale; public enum Signatures { ; - public static String createSigningString(final List required, String method, final String uri, Map headers) { + private static final String REQUEST_TARGET = "(request-target)"; + + public static String createSigningString(final List required, String method, final String uri, final HeaderReader headers) { method = lowercase(method); - headers = lowercase(headers); final List list = new ArrayList(required.size()); - for (final String key : required) { - if ("(request-target)".equals(key)) { + if (REQUEST_TARGET.equals(key)) { list.add(Join.join(" ", "(request-target):", method, uri)); } else { - final String value = headers.get(key); + final String value = headers.read(key); if (value == null) throw new MissingRequiredHeaderException(key); list.add(key + ": " + value); @@ -45,16 +44,7 @@ public static String createSigningString(final List required, String met return Join.join("\n", list); } - private static Map lowercase(final Map headers) { - final Map map = new HashMap(); - for (final Map.Entry entry : headers.entrySet()) { - map.put(entry.getKey().toLowerCase(), entry.getValue()); - } - - return map; - } - private static String lowercase(final String spec) { - return spec.toLowerCase(); + return spec.toLowerCase(Locale.ENGLISH); } } diff --git a/src/main/java/org/tomitribe/auth/signatures/Signer.java b/src/main/java/org/tomitribe/auth/signatures/Signer.java index 3426f3f..45f505f 100644 --- a/src/main/java/org/tomitribe/auth/signatures/Signer.java +++ b/src/main/java/org/tomitribe/auth/signatures/Signer.java @@ -75,16 +75,18 @@ public Signer(final Key key, final Signature signature, final Provider provider) sign.sign("validation".getBytes()); } catch (final RuntimeException e) { - - throw (RuntimeException) e; - + throw e; } catch (final Exception e) { - throw new IllegalStateException("Can't initialise the Signer using the provided algorithm and key", e); } } + @Deprecated // use sign(String, String, HeaderReader headers) public Signature sign(final String method, final String uri, final Map headers) throws IOException { + return sign(method, uri, new HeaderReader.Map(headers)); + } + + public Signature sign(final String method, final String uri, final HeaderReader headers) throws IOException { final String signingString = createSigningString(method, uri, headers); @@ -97,7 +99,12 @@ public Signature sign(final String method, final String uri, final Map headers) throws IOException { + return createSigningString(method, uri, new HeaderReader.Map(headers)); + } + + public String createSigningString(final String method, final String uri, final HeaderReader headers) throws IOException { return Signatures.createSigningString(signature.getHeaders(), method, uri, headers); } diff --git a/src/test/java/org/tomitribe/auth/signatures/SignerTest.java b/src/test/java/org/tomitribe/auth/signatures/SignerTest.java index 21c9895..15aabd7 100644 --- a/src/test/java/org/tomitribe/auth/signatures/SignerTest.java +++ b/src/test/java/org/tomitribe/auth/signatures/SignerTest.java @@ -90,7 +90,7 @@ public void testSign() throws Exception { headers.put("Digest", "SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE="); headers.put("Accept", "*/*"); headers.put("Content-Length", "18"); - final Signature signed = signer.sign(method, uri, headers); + final Signature signed = signer.sign(method, uri, new HeaderReader.Map(headers)); assertEquals("yT/NrPI9mKB5R7FTLRyFWvB+QLQOEAvbGmauC0tI+Jg=", signed.getSignature()); } @@ -104,7 +104,7 @@ public void testSign() throws Exception { headers.put("Digest", "SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE="); headers.put("Accept", "*/*"); headers.put("Content-Length", "18"); - final Signature signed = signer.sign(method, uri, headers); + final Signature signed = signer.sign(method, uri, new HeaderReader.Map(headers)); assertEquals("DPIsA/PWeYjySmfjw2P2SLJXZj1szDOei/Hh8nTcaPo=", signed.getSignature()); } @@ -118,7 +118,7 @@ public void testSign() throws Exception { headers.put("Digest", "SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu8DBPE="); headers.put("Accept", "*/*"); headers.put("Content-Length", "18"); - final Signature signed = signer.sign(method, uri, headers); + final Signature signed = signer.sign(method, uri, new HeaderReader.Map(headers)); assertEquals("DPIsA/PWeYjySmfjw2P2SLJXZj1szDOei/Hh8nTcaPo=", signed.getSignature()); } @@ -132,7 +132,7 @@ public void testSign() throws Exception { headers.put("Digest", "SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu8DBPE="); headers.put("Accept", "*/*"); headers.put("Content-Length", "18"); - final Signature signed = signer.sign(method, uri, headers); + final Signature signed = signer.sign(method, uri, new HeaderReader.Map(headers)); assertEquals("IWTDxmOoEJI67YxY3eDIRzxrsAtlYYCuGZxKlkUSYdA=", signed.getSignature()); } } @@ -148,7 +148,7 @@ public void defaultHeaderList() throws Exception { final Map headers = new HashMap(); headers.put("Date", "Tue, 07 Jun 2014 20:51:35 GMT"); - final Signature signed = signer.sign("GET", "/foo/Bar", headers); + final Signature signed = signer.sign("GET", "/foo/Bar", new HeaderReader.Map(headers)); assertEquals("WbB9VXuVdRt1LKQ5mDuT+tiaChn8R7WhdAWAY1lhKZQ=", signed.getSignature()); } @@ -156,7 +156,7 @@ public void defaultHeaderList() throws Exception { final Map headers = new HashMap(); headers.put("Date", "Tue, 07 Jun 2014 20:51:36 GMT"); - final Signature signed = signer.sign("GET", "/foo/Bar", headers); + final Signature signed = signer.sign("GET", "/foo/Bar", new HeaderReader.Map(headers)); assertEquals("kRkh0bV1wKZSXBgexUB+zlPU88/za5K/gk/F0Aikg7Q=", signed.getSignature()); } @@ -168,7 +168,7 @@ public void defaultHeaderList() throws Exception { headers.put("Accept", "*/*"); headers.put("Content-Length", "18"); - final Signature signed = signer.sign("GET", "/foo/Bar", headers); + final Signature signed = signer.sign("GET", "/foo/Bar", new HeaderReader.Map(headers)); assertEquals("kRkh0bV1wKZSXBgexUB+zlPU88/za5K/gk/F0Aikg7Q=", signed.getSignature()); } } @@ -181,7 +181,7 @@ public void missingDefaultHeader() throws Exception { final Signer signer = new Signer(key, signature); final Map headers = new HashMap(); - signer.sign("GET", "/foo/Bar", headers); + signer.sign("GET", "/foo/Bar", new HeaderReader.Map(headers)); } @Test(expected = MissingRequiredHeaderException.class)