NimbusJwt.java

package org.europa.together.application;

import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.Payload;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import java.util.Date;
import java.util.List;
import org.europa.together.business.ConfigurationDAO;
import org.europa.together.business.JsonWebToken;
import org.europa.together.business.Logger;
import org.europa.together.domain.ConfigurationDO;
import org.europa.together.domain.LogLevel;
import org.europa.together.exceptions.DAOException;
import org.europa.together.exceptions.JsonProcessingException;
import org.europa.together.utils.Constraints;
import org.europa.together.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

/**
 *
 * @author ed
 */
@Repository
@Transactional
public class NimbusJwt implements JsonWebToken {

    private static final long serialVersionUID = 16L;
    private static final Logger LOGGER = new LogbackLogger(NimbusJwt.class);

    private String sharedSecret = "";

    @Autowired
    private ConfigurationDAO configurationDAO;

    public NimbusJwt() throws DAOException {
        LOGGER.log("instance class", LogLevel.INFO);
    }

    @Override
    public String buildHMAC512SignedJws(final String jsonPayload)
            throws JsonProcessingException {
        String jws = "";
        try {
            fetchSharedSecret();
            if (!validateLengthOfSharedSecret(sharedSecret)) {
                String msg = "The length of the secret is less than 128 characters.";
                throw new JsonProcessingException(msg);
            }

            JWSSigner signer = new MACSigner(sharedSecret);
            JWSHeader jswHeader = new JWSHeader(JWSAlgorithm.HS512);

            Payload payload = new Payload(jsonPayload);

            JWSObject jwsObject = new JWSObject(jswHeader, payload);
            jwsObject.sign(signer);

            jws = jwsObject.serialize();
        } catch (Exception ex) {
            throw new JsonProcessingException(ex.getClass().getTypeName()
                    + " " + ex.getMessage());
        }
        return jws;
    }

    @Override
    public String buildHMAC512SignedJwt(final String issuer, final String subject,
            final List<String> audience)
            throws JsonProcessingException {
        String jwt = "";
        try {
            fetchSharedSecret();
            if (!validateLengthOfSharedSecret(sharedSecret)) {
                String msg = "The length of the secret is less than 128 characters.";
                throw new JsonProcessingException(msg);
            }

            JWSSigner signer = new MACSigner(sharedSecret);
            JWSHeader jswHeader = new JWSHeader(JWSAlgorithm.HS512);

            Date currentTime = new Date();
            //TODO: make json.expiration.time configurable: 1000 * 60 * 10 := 10 minutes
            JWTClaimsSet jwtClaims = new JWTClaimsSet.Builder()
                    .issuer(issuer)
                    .subject(subject)
                    .audience(audience)
                    .expirationTime(new Date(new Date().getTime() + 600000))
                    .notBeforeTime(currentTime)
                    .issueTime(currentTime)
                    .jwtID(StringUtils.generateUUID())
                    .build();

            SignedJWT signedJWT = new SignedJWT(jswHeader, jwtClaims);
            signedJWT.sign(signer);
            jwt = signedJWT.serialize();
        } catch (Exception ex) {
            throw new JsonProcessingException(ex.getClass().getTypeName()
                    + " " + ex.getMessage());
        }
        return jwt;
    }

    @Override
    public String parseHMAC512SingedJws(final String jws)
            throws JsonProcessingException {
        String payload = "";
        try {
            fetchSharedSecret();

            JWSObject jwsObject = JWSObject.parse(jws);
            JWSVerifier verifier = new MACVerifier(sharedSecret);
            payload = jwsObject.getPayload().toString();

            if (!jwsObject.verify(verifier)) {
                LOGGER.log("JWS is not valid.", LogLevel.ERROR);
                LOGGER.log("\t payload: " + payload, LogLevel.ERROR);
                throw new Exception("JWS is not valid. - for security reasons rejected!");
            }

        } catch (Exception ex) {
            throw new JsonProcessingException(ex.getClass().getTypeName()
                    + " " + ex.getMessage());
        }
        return payload;
    }

    @Override
    public String parseHMAC512SingedJwt(final String jwt)
            throws JsonProcessingException {
        String payload = "";
        try {
            fetchSharedSecret();

            SignedJWT signedJWT = SignedJWT.parse(jwt);
            JWSVerifier verifier = new MACVerifier(sharedSecret);
            payload = signedJWT.getPayload().toString();

            if (!signedJWT.verify(verifier)) {
                LOGGER.log("JWT is not valid.", LogLevel.ERROR);
                LOGGER.log("\t payload: " + payload, LogLevel.ERROR);
                throw new Exception("JWT is not valid. - for security reasons rejected!");
            }

        } catch (Exception ex) {
            throw new JsonProcessingException(ex.getClass().getTypeName()
                    + " " + ex.getMessage());
        }
        return payload;
    }

    //--------------------------------------------------------------------------
    private boolean validateLengthOfSharedSecret(final String secret) {
        boolean success = false;
        // 512 bits -> 64 byte = 128 chars
        if (secret.length() >= Constraints.INT_128) {
            success = true;
        }
        return success;
    }

    private void fetchSharedSecret() throws DAOException {
        List<ConfigurationDO> configuration = configurationDAO
                .getAllConfigurationSetEntries("core", VERSION, "json");
        this.sharedSecret = configuration.get(0).getValue();

        LOGGER.log("SEC: " + configuration.toString().substring(0, 30), LogLevel.DEBUG);
    }

}