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.CryptoTools;
import org.europa.together.business.JsonWebToken;
import org.europa.together.business.Logger;
import org.europa.together.domain.ConfigurationDO;
import org.europa.together.domain.HashAlgorithm;
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;

/**
 * Implementation of JSON Web Token.
 */
@Repository
@Transactional
public class NimbusJwt implements JsonWebToken {

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

    private static final int INT_30 = 30;

    private String sharedSecret = "";
    private String issuer = "";
    private String subject = "";
    //1000 * 60 * 10 := 10 minutes = 600000
    private int expieredTime = 0;

    @Autowired
    private ConfigurationDAO configurationDAO;
    @Autowired
    private CryptoTools cryptoTools;

    /**
     * Constructor.
     *
     * @throws DAOException case of error
     */
    public NimbusJwt() throws DAOException {
        LOGGER.log("instance class", LogLevel.INFO);
    }

    @Override
    public String buildHMAC512SignedJws(final String jsonPayload)
            throws JsonProcessingException {
        String jws = "";
        try {
            proccessDbConfiguration();

            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 {
            proccessDbConfiguration();

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

            Date currentTime = new Date();
            JWTClaimsSet jwtClaims = new JWTClaimsSet.Builder()
                    .issuer(issuer)
                    .subject(subject)
                    .audience(audience)
                    .expirationTime(new Date(new Date().getTime() + expieredTime))
                    .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 buildHMAC512SignedJwt(final List<String> audience)
            throws JsonProcessingException {
        return this.buildHMAC512SignedJwt(this.issuer, this.subject, audience);
    }

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

            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 JsonProcessingException(
                        "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 {
            proccessDbConfiguration();

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

            //TODO: extract for validation
            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 void proccessDbConfiguration() throws DAOException {
        List<ConfigurationDO> configurationEntries = configurationDAO
                .getAllConfigurationSetEntries(Constraints.MODULE_NAME, CONFIG_VERSION, CONFIG_SET);
        LOGGER.log("Process JWT Configuration [" + configurationEntries.size()
                + "] (" + configurationEntries.toString() + ")", LogLevel.DEBUG);

        for (ConfigurationDO entry : configurationEntries) {
            String value;
            if (StringUtils.isEmpty(entry.getValue())) {
                value = entry.getDefaultValue();
            } else {
                value = entry.getValue();
            }

            if (entry.getKey()
                    .equals(cryptoTools.calculateHash("json.sharedSecret",
                            HashAlgorithm.SHA256))) {
                this.sharedSecret = value;
                LOGGER.log("SharedSecret: " + value, LogLevel.DEBUG);
            } else if (entry.getKey()
                    .equals(cryptoTools.calculateHash("json.issuer",
                            HashAlgorithm.SHA256))) {
                this.issuer = value;
                LOGGER.log("Issuer: " + value, LogLevel.DEBUG);
            } else if (entry.getKey()
                    .equals(cryptoTools.calculateHash("json.subject",
                            HashAlgorithm.SHA256))) {
                this.subject = value;
                LOGGER.log("Subiject: " + value, LogLevel.DEBUG);
            } else if (entry.getKey()
                    .equals(cryptoTools.calculateHash("json.expiration.time",
                            HashAlgorithm.SHA256))) {
                this.expieredTime = Integer.parseInt(value);
                LOGGER.log("Timeout: " + value, LogLevel.DEBUG);
            }
        }
        LOGGER.log("Config: \n"
                + "\t SharedSecret: " + this.sharedSecret + "\n"
                + "\t Issuer: " + this.issuer + "\n"
                + "\t Subiject: " + this.subject + "\n"
                + "\t Timeout: " + this.expieredTime, LogLevel.DEBUG);
    }

}