package org.jboss.resteasy.test.security.doseta;
import org.jboss.resteasy.annotations.security.doseta.After;
import org.jboss.resteasy.annotations.security.doseta.Signed;
import org.jboss.resteasy.annotations.security.doseta.Verify;
import org.jboss.resteasy.client.ClientRequest;
import org.jboss.resteasy.client.ClientResponse;
import org.jboss.resteasy.client.ProxyFactory;
import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.security.doseta.DKIMSignature;
import org.jboss.resteasy.security.doseta.DosetaKeyRepository;
import org.jboss.resteasy.security.doseta.KeyRepository;
import org.jboss.resteasy.security.doseta.UnauthorizedSignatureException;
import org.jboss.resteasy.security.doseta.Verification;
import org.jboss.resteasy.security.doseta.Verifier;
import org.jboss.resteasy.spi.MarshalledEntity;
import org.jboss.resteasy.test.BaseResourceTest;
import org.jboss.resteasy.test.TestPortProvider;
import org.jboss.resteasy.util.Base64;
import org.jboss.resteasy.util.GenericType;
import org.jboss.resteasy.util.ParameterParser;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import java.net.URL;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SigningTest extends BaseResourceTest
{
public static KeyPair keys;
public static DosetaKeyRepository repository;
public static PrivateKey badKey;
@Test
public void testMe() throws Exception
{
URL url = Thread.currentThread().getContextClassLoader().getResource("dns/zones");
System.out.println(url.getFile());
}
@BeforeClass
public static void setup() throws Exception
{
Logger.setLoggerType(Logger.LoggerType.JUL);
repository = new DosetaKeyRepository();
repository.setKeyStorePath("test.jks");
repository.setKeyStorePassword("password");
repository.setUseDns(false);
repository.start();
PrivateKey privateKey = repository.getKeyStore().getPrivateKey("test._domainKey.samplezone.org");
if (privateKey == null) throw new Exception("Private Key is null!!!");
PublicKey publicKey = repository.getKeyStore().getPublicKey("test._domainKey.samplezone.org");
keys = new KeyPair(publicKey, privateKey);
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
badKey = keyPair.getPrivate();
dispatcher.getDefaultContextObjects().put(KeyRepository.class, repository);
/*
deployment.getProviderFactory().registerProvider(DigitalSigningInterceptor.class);
deployment.getProviderFactory().registerProvider(DigitalSigningHeaderDecorator.class);
deployment.getProviderFactory().registerProvider(DigitalVerificationInterceptor.class);
deployment.getProviderFactory().registerProvider(DigitalVerificationHeaderDecorator.class);
*/
addPerRequestResource(SignedResource.class);
}
@Path("/signed")
public static interface SigningProxy
{
@GET
@Verify
@Produces("text/plain")
@Path("bad-signature")
public String bad();
@GET
@Verify
@Produces("text/plain")
public String hello();
@POST
@Consumes("text/plain")
@Signed(selector = "test", domain = "samplezone.org")
public void postSimple(String input);
}
@Path("/signed")
public static class SignedResource
{
@GET
@Produces("text/plain")
@Path("bad-signature")
public Response badSignature() throws Exception
{
DKIMSignature signature = new DKIMSignature();
signature.setDomain("samplezone.org");
signature.setSelector("test");
signature.sign(new HashMap(), "hello world".getBytes(), keys.getPrivate());
byte[] sig = {0x0f, 0x03};
String encodedBadSig = Base64.encodeBytes(sig);
ParameterParser parser = new ParameterParser();
String s = signature.toString();
String header = parser.setAttribute(s.toCharArray(), 0, s.length(), ';', "b", encodedBadSig);
signature.setSignature(sig);
return Response.ok("hello world").header(DKIMSignature.DKIM_SIGNATURE, header).build();
}
@GET
@Produces("text/plain")
@Path("bad-hash")
public Response badHash() throws Exception
{
DKIMSignature signature = new DKIMSignature();
signature.setDomain("samplezone.org");
signature.setSelector("test");
signature.sign(new HashMap(), "hello world".getBytes(), keys.getPrivate());
return Response.ok("hello").header(DKIMSignature.DKIM_SIGNATURE, signature.toString()).build();
}
@GET
@Produces("text/plain")
@Path("manual")
public Response getManual()
{
DKIMSignature signature = new DKIMSignature();
signature.setSelector("test");
signature.setDomain("samplezone.org");
Response.ResponseBuilder builder = Response.ok("hello");
builder.header(DKIMSignature.DKIM_SIGNATURE, signature);
return builder.build();
}
@GET
@Path("header")
@Produces("text/plain")
public Response withHeader()
{
Response.ResponseBuilder builder = Response.ok("hello world");
builder.header("custom", "value");
DKIMSignature signature = new DKIMSignature();
signature.setSelector("test");
signature.setDomain("samplezone.org");
signature.addHeader("custom");
builder.header(DKIMSignature.DKIM_SIGNATURE, signature);
return builder.build();
}
@GET
@Signed(selector = "test", domain = "samplezone.org")
@Produces("text/plain")
public String hello()
{
return "hello world";
}
@POST
@Consumes("text/plain")
@Verify
public void post(@HeaderParam(DKIMSignature.DKIM_SIGNATURE) DKIMSignature signature, String input)
{
Assert.assertNotNull(signature);
Assert.assertEquals(input, "hello world");
}
@POST
@Consumes("text/plain")
@Path("verify-manual")
public void verifyManual(@HeaderParam(DKIMSignature.DKIM_SIGNATURE) DKIMSignature signature, @Context HttpHeaders headers, MarshalledEntity<String> input) throws Exception
{
Assert.assertNotNull(signature);
Assert.assertEquals(input.getEntity(), "hello world");
signature.verify(headers.getRequestHeaders(), input.getMarshalledBytes(), keys.getPublic());
}
@GET
@Signed(selector = "test", domain = "samplezone.org",
timestamped = true)
@Produces("text/plain")
@Path("stamped")
public String getStamp()
{
return "hello world";
}
@GET
@Signed(selector = "test", domain = "samplezone.org",
expires = @After(seconds = 1))
@Produces("text/plain")
@Path("expires-short")
public String getExpiresShort()
{
return "hello world";
}
@GET
@Signed(selector = "test", domain = "samplezone.org",
expires = @After(minutes = 1))
@Produces("text/plain")
@Path("expires-minute")
public String getExpiresMinute()
{
return "hello world";
}
@GET
@Signed(selector = "test", domain = "samplezone.org",
expires = @After(hours = 1))
@Produces("text/plain")
@Path("expires-hour")
public String getExpiresHour()
{
return "hello world";
}
@GET
@Signed(selector = "test", domain = "samplezone.org",
expires = @After(days = 1))
@Produces("text/plain")
@Path("expires-day")
public String getExpiresDay()
{
return "hello world";
}
@GET
@Signed(selector = "test", domain = "samplezone.org",
expires = @After(months = 1))
@Produces("text/plain")
@Path("expires-month")
public String getExpiresMonth()
{
return "hello world";
}
@GET
@Signed(selector = "test", domain = "samplezone.org",
expires = @After(years = 1))
@Produces("text/plain")
@Path("expires-year")
public String getExpiresYear()
{
return "hello world";
}
}
@Test
public void testSigningManual() throws Exception
{
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed"));
ClientResponse<MarshalledEntity<String>> response = request.get(new GenericType<MarshalledEntity<String>>()
{
});
Assert.assertEquals(200, response.getStatus());
MarshalledEntity<String> marshalledEntity = response.getEntity();
Assert.assertEquals("hello world", marshalledEntity.getEntity());
String signatureHeader = response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE);
System.out.println(DKIMSignature.DKIM_SIGNATURE + ": " + signatureHeader);
for (String name : response.getHeaders().keySet())
{
System.out.println("Header: " + name);
}
Assert.assertNotNull(signatureHeader);
DKIMSignature contentSignature = new DKIMSignature(signatureHeader);
contentSignature.verify(response.getHeaders(), marshalledEntity.getMarshalledBytes(), keys.getPublic());
}
@Test
public void testBasicVerification() throws Exception
{
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed"));
DKIMSignature contentSignature = new DKIMSignature();
contentSignature.setDomain("samplezone.org");
contentSignature.setSelector("test");
contentSignature.setPrivateKey(keys.getPrivate());
request.header(DKIMSignature.DKIM_SIGNATURE, contentSignature);
request.body("text/plain", "hello world");
ClientResponse response = request.post();
Assert.assertEquals(204, response.getStatus());
}
@Test
public void testManualVerification() throws Exception
{
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed/verify-manual"));
DKIMSignature contentSignature = new DKIMSignature();
contentSignature.setDomain("samplezone.org");
contentSignature.setSelector("test");
contentSignature.setAttribute("code", "hello");
contentSignature.setPrivateKey(keys.getPrivate());
request.header(DKIMSignature.DKIM_SIGNATURE, contentSignature);
request.body("text/plain", "hello world");
ClientResponse response = request.post();
Assert.assertEquals(204, response.getStatus());
}
@Test
public void testBasicVerificationRepository() throws Exception
{
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed"));
DKIMSignature contentSignature = new DKIMSignature();
contentSignature.setSelector("test");
contentSignature.setDomain("samplezone.org");
request.getAttributes().put(KeyRepository.class.getName(), repository);
request.header(DKIMSignature.DKIM_SIGNATURE, contentSignature);
request.body("text/plain", "hello world");
ClientResponse response = request.post();
Assert.assertEquals(204, response.getStatus());
}
@Test
public void testBasicVerificationBadSignature() throws Exception
{
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed"));
DKIMSignature contentSignature = new DKIMSignature();
contentSignature.setSelector("test");
contentSignature.setDomain("samplezone.org");
contentSignature.setPrivateKey(badKey);
request.header(DKIMSignature.DKIM_SIGNATURE, contentSignature);
request.body("text/plain", "hello world");
ClientResponse response = request.post();
Assert.assertEquals(401, response.getStatus());
}
@Test
public void testBasicVerificationNoSignature() throws Exception
{
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed"));
request.body("text/plain", "hello world");
ClientResponse response = request.post();
Assert.assertEquals(401, response.getStatus());
}
@Test
public void testTimestampSignature() throws Exception
{
DKIMSignature signature = new DKIMSignature();
signature.setTimestamp();
signature.setSelector("test");
signature.setDomain("samplezone.org");
signature.sign(new HashMap(), "hello world".getBytes(), keys.getPrivate());
String sig = signature.toString();
System.out.println(DKIMSignature.DKIM_SIGNATURE + ": " + sig);
signature = new DKIMSignature(sig);
}
@Test
public void testTimestamp() throws Exception
{
Verifier verifier = new Verifier();
Verification verification = verifier.addNew();
verification.setRepository(repository);
verification.setStaleCheck(true);
verification.setStaleSeconds(100);
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed/stamped"));
ClientResponse<String> response = request.get(String.class);
response.getAttributes().put(Verifier.class.getName(), verifier);
System.out.println(response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE));
Assert.assertEquals(200, response.getStatus());
String output = response.getEntity();
}
@Test
public void testStaleTimestamp() throws Exception
{
Verifier verifier = new Verifier();
Verification verification = verifier.addNew();
verification.setRepository(repository);
verification.setStaleCheck(true);
verification.setStaleSeconds(1);
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed/stamped"));
ClientResponse<String> response = request.get(String.class);
response.getAttributes().put(Verifier.class.getName(), verifier);
System.out.println(response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE));
Assert.assertEquals(200, response.getStatus());
Thread.sleep(1500);
try
{
String output = response.getEntity();
throw new Exception("unreachable!");
}
catch (UnauthorizedSignatureException e)
{
Assert.assertEquals("Failed to verify signatures:\r\n Signature is stale", e.getMessage());
}
}
@Test
public void testExpiresHour() throws Exception
{
Verifier verifier = new Verifier();
Verification verification = verifier.addNew();
verification.setRepository(repository);
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed/expires-hour"));
ClientResponse<String> response = request.get(String.class);
response.getAttributes().put(Verifier.class.getName(), verifier);
System.out.println(response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE));
Assert.assertEquals(200, response.getStatus());
String output = response.getEntity();
}
@Test
public void testExpiresMinutes() throws Exception
{
Verifier verifier = new Verifier();
Verification verification = verifier.addNew();
verification.setRepository(repository);
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed/expires-minute"));
ClientResponse<String> response = request.get(String.class);
response.getAttributes().put(Verifier.class.getName(), verifier);
System.out.println(response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE));
Assert.assertEquals(200, response.getStatus());
String output = response.getEntity();
}
@Test
public void testExpiresDays() throws Exception
{
Verifier verifier = new Verifier();
Verification verification = verifier.addNew();
verification.setRepository(repository);
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed/expires-day"));
ClientResponse<String> response = request.get(String.class);
response.getAttributes().put(Verifier.class.getName(), verifier);
System.out.println(response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE));
Assert.assertEquals(200, response.getStatus());
String output = response.getEntity();
}
@Test
public void testExpiresMonths() throws Exception
{
Verifier verifier = new Verifier();
Verification verification = verifier.addNew();
verification.setRepository(repository);
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed/expires-month"));
ClientResponse<String> response = request.get(String.class);
response.getAttributes().put(Verifier.class.getName(), verifier);
System.out.println(response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE));
Assert.assertEquals(200, response.getStatus());
String output = response.getEntity();
}
@Test
public void testExpiresYears() throws Exception
{
Verifier verifier = new Verifier();
Verification verification = verifier.addNew();
verification.setRepository(repository);
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed/expires-year"));
ClientResponse<String> response = request.get(String.class);
response.getAttributes().put(Verifier.class.getName(), verifier);
System.out.println(response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE));
Assert.assertEquals(200, response.getStatus());
String output = response.getEntity();
}
@Test
public void testExpiresFail() throws Exception
{
Verifier verifier = new Verifier();
Verification verification = verifier.addNew();
verification.setRepository(repository);
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed/expires-short"));
ClientResponse<String> response = request.get(String.class);
response.getAttributes().put(Verifier.class.getName(), verifier);
System.out.println(response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE));
Assert.assertEquals(200, response.getStatus());
Thread.sleep(1500);
try
{
String output = response.getEntity();
throw new Exception("unreachable!");
}
catch (UnauthorizedSignatureException e)
{
Assert.assertEquals("Failed to verify signatures:\r\n Signature expired", e.getMessage());
}
}
@Test
public void testManualFail() throws Exception
{
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair keyPair = kpg.genKeyPair();
Verifier verifier = new Verifier();
Verification verification = verifier.addNew();
verification.setKey(keyPair.getPublic());
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed/manual"));
ClientResponse<String> response = request.get(String.class);
response.getAttributes().put(Verifier.class.getName(), verifier);
System.out.println(response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE));
Assert.assertNotNull(response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE));
Assert.assertEquals(200, response.getStatus());
try
{
String output = response.getEntity();
throw new Exception("unreachable!");
}
catch (UnauthorizedSignatureException e)
{
Assert.assertEquals("Failed to verify signatures:\r\n Failed to verify signature.", e.getMessage());
}
}
@Test
public void testManual() throws Exception
{
Verifier verifier = new Verifier();
Verification verification = verifier.addNew();
verification.setRepository(repository);
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed/manual"));
ClientResponse<String> response = request.get(String.class);
response.getAttributes().put(Verifier.class.getName(), verifier);
System.out.println(response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE));
Assert.assertNotNull(response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE));
Assert.assertEquals(200, response.getStatus());
String output = response.getEntity();
Assert.assertEquals("hello", output);
}
@Test
public void testManualWithHeader() throws Exception
{
Verifier verifier = new Verifier();
Verification verification = verifier.addNew();
verification.setRepository(repository);
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed/header"));
ClientResponse<String> response = request.get(String.class);
response.getAttributes().put(Verifier.class.getName(), verifier);
System.out.println(response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE));
Assert.assertNotNull(response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE));
Assert.assertEquals(200, response.getStatus());
String output = response.getEntity();
Assert.assertEquals("hello world", output);
}
@Test
public void testBadSignature() throws Exception
{
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed/bad-signature"));
ClientResponse<?> response = request.get(String.class);
Assert.assertEquals(200, response.getStatus());
String signatureHeader = response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE);
Assert.assertNotNull(signatureHeader);
System.out.println(DKIMSignature.DKIM_SIGNATURE + ": " + signatureHeader);
DKIMSignature contentSignature = new DKIMSignature(signatureHeader);
MarshalledEntity<String> entity = response.getEntity(new GenericType<MarshalledEntity<String>>()
{
});
boolean failedVerification = false;
try
{
contentSignature.verify(response.getHeaders(), entity.getMarshalledBytes(), keys.getPublic());
}
catch (SignatureException e)
{
failedVerification = true;
}
Assert.assertTrue(failedVerification);
}
@Test
public void testBadHash() throws Exception
{
ClientRequest request = new ClientRequest(TestPortProvider.generateURL("/signed/bad-hash"));
ClientResponse<?> response = request.get(String.class);
Assert.assertEquals(200, response.getStatus());
String signatureHeader = response.getHeaders().getFirst(DKIMSignature.DKIM_SIGNATURE);
Assert.assertNotNull(signatureHeader);
System.out.println(DKIMSignature.DKIM_SIGNATURE + ": " + signatureHeader);
DKIMSignature contentSignature = new DKIMSignature(signatureHeader);
MarshalledEntity<String> entity = response.getEntity(new GenericType<MarshalledEntity<String>>()
{
});
boolean failedVerification = false;
try
{
contentSignature.verify(response.getHeaders(), entity.getMarshalledBytes(), keys.getPublic());
}
catch (SignatureException e)
{
failedVerification = true;
}
Assert.assertTrue(failedVerification);
}
@Test
public void testProxy() throws Exception
{
Map<String, Object> attributes = new HashMap<String, Object>();
attributes.put(KeyRepository.class.getName(), repository);
SigningProxy proxy = ProxyFactory.create(SigningProxy.class, TestPortProvider.generateURL(""), attributes);
String output = proxy.hello();
proxy.postSimple("hello world");
}
@Test
public void testBadSignatureProxy() throws Exception
{
Map<String, Object> attributes = new HashMap<String, Object>();
attributes.put(KeyRepository.class.getName(), repository);
SigningProxy proxy = ProxyFactory.create(SigningProxy.class, TestPortProvider.generateURL(""), attributes);
try
{
String output = proxy.bad();
}
catch (UnauthorizedSignatureException e)
{
}
}
}