package org.appverse.web.framework.backend.frontfacade.rest.authentication; import com.nimbusds.jose.*; import com.nimbusds.jose.crypto.RSASSASigner; import org.appverse.web.framework.backend.frontfacade.rest.authentication.business.CertService; import org.appverse.web.framework.backend.frontfacade.rest.authentication.business.impl.live.CertServiceImpl; import org.appverse.web.framework.backend.frontfacade.rest.authentication.filter.JWSAuthenticationProcessingFilter; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.glassfish.jersey.message.MessageBodyWorkers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.ws.rs.client.ClientRequestContext; import javax.ws.rs.client.ClientRequestFilter; import javax.ws.rs.client.ClientResponseContext; import javax.ws.rs.client.ClientResponseFilter; import javax.ws.rs.core.*; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.security.Key; import java.security.KeyStore; import java.security.interfaces.RSAPrivateKey; @Component @Provider public class JWSJerseyFilter implements ClientRequestFilter, ClientResponseFilter { private static Logger logger = LoggerFactory.getLogger(JWSJerseyFilter.class); private final CertService certService = new CertServiceImpl(logger); @Context private MessageBodyWorkers workers; @Override //RequestFilter public void filter(final ClientRequestContext requestContext) throws IOException { // TODO Auto-generated method stub try { /* //It doesn't work // use ServiceLocatorClientProvider to extract HK2 ServiceLocator from request final ServiceLocator locator = ServiceLocatorClientProvider .getServiceLocator(requestContext); // and ask for MyInjectedService: CertService certService = locator.getService(CertService.class); */ KeyStore keyStore = certService.getKeyStore("certificates/client/client.pfx", "PKCS12", "export"); String keyPassword = "export"; Key key = keyStore.getKey("client.restservices.gbs.db.com", keyPassword.toCharArray()); //Create RSA-signer with the private key JWSSigner signer = new RSASSASigner((RSAPrivateKey) key); signer.setProvider(new BouncyCastleProvider()); /* // RSA signatures require a public and private RSA key pair, // the public key must be made known to the JWS recipient in // order to verify the signatures */ // Prepare JWS object with simple string as payload String reqUri = requestContext.getUri().toString(); if (logger.isDebugEnabled()) logger.debug("URI:: " + reqUri); // buffer into which myBean will be serialized ByteArrayOutputStream baos = new ByteArrayOutputStream(); Object object = requestContext.getEntity(); Payload objectPay = null; if (object != null) { Class<Object> type = (Class<Object>) requestContext.getEntityClass(); GenericType<Object> genericType = new GenericType<Object>(type) { }; // get most appropriate MBW final MessageBodyWriter<Object> messageBodyWriter = workers.getMessageBodyWriter(type, type, new Annotation[] {}, MediaType.APPLICATION_JSON_TYPE); try { // use the MBW to serialize myBean into baos messageBodyWriter.writeTo(object, object.getClass(), genericType.getType(), new Annotation[] {}, MediaType.APPLICATION_JSON_TYPE, new MultivaluedHashMap<String, Object>(), baos); } catch (IOException e) { throw new RuntimeException( "Error while serializing MyBean.", e); } String stringJsonOutput = baos.toString(); if (logger.isDebugEnabled()) logger.debug("Entity.toString():: " + stringJsonOutput); //There is a short limitation for http headers. It depends on server. //As message payload grows, payload in header is growing too, so we must set a limit. //It means that we are only signing, validating and checking message integrity of first 1024 characters //Same logic is applied in server side if (stringJsonOutput != null && stringJsonOutput.length() > JWSAuthenticationProcessingFilter.PAYLOAD_HEADER_MAX_SIZE) stringJsonOutput = stringJsonOutput.substring(0, JWSAuthenticationProcessingFilter.PAYLOAD_HEADER_MAX_SIZE); objectPay = new Payload(stringJsonOutput); } //If request entity is null (usually GET methods) use URI as payload else objectPay = new Payload(reqUri); JWSObject jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.RS256), objectPay); // Compute the RSA signature jwsObject.sign(signer); String s = jwsObject.serialize(); if (logger.isDebugEnabled()) logger.debug("serialized compact form: " + s); requestContext.getHeaders().add("Authorization", "Bearer " + s); } catch (Exception e) { logger.error("Error signing message", e); requestContext.abortWith( Response.status(Response.Status.BAD_REQUEST).entity("Error signing message") .build()); } } @Override //ResponseFilter public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext) throws IOException { // TODO Auto-generated method stub } private class InterceptorStream extends OutputStream { private final StringBuilder b; private final OutputStream inner; private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); InterceptorStream(final StringBuilder b, final OutputStream inner) { this.b = b; this.inner = inner; } StringBuilder getStringBuilder() { // write entity to the builder final byte[] entity = baos.toByteArray(); b.append(new String(entity, 0, entity.length)); b.append('\n'); return b; } @Override public void write(final int i) throws IOException { baos.write(i); inner.write(i); } } }