From f6273721193b5a36e71d48e5388b24103468a175 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Thu, 14 Nov 2019 13:35:19 +0100 Subject: refactor JoseTools to support JWS signature verification by using one truststore --- .../eaaf/modules/auth/sl20/utils/IJOSETools.java | 45 +++++- .../modules/auth/sl20/utils/JsonSecurityUtils.java | 162 ++++++++++++--------- 2 files changed, 136 insertions(+), 71 deletions(-) diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/IJOSETools.java b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/IJOSETools.java index 35e6de4f..b124ada7 100644 --- a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/IJOSETools.java +++ b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/IJOSETools.java @@ -1,6 +1,15 @@ package at.gv.egiz.eaaf.modules.auth.sl20.utils; +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.cert.X509Certificate; +import java.util.List; + +import javax.annotation.Nonnull; + +import org.jose4j.jwa.AlgorithmConstraints; +import org.jose4j.lang.JoseException; import com.fasterxml.jackson.databind.JsonNode; @@ -20,14 +29,44 @@ public interface IJOSETools { public String createSignature(String payLoad) throws SLCommandoBuildException; /** - * Validates a JWS signature + * Validates a signed SL2.0 message * * @param serializedContent * @return * @throws SLCommandoParserException * @throws SL20Exception */ - public VerificationResult validateSignature(String serializedContent) throws SL20Exception; + @Nonnull + public VerificationResult validateSignature(@Nonnull String serializedContent) throws SL20Exception; + + /** + * Validate a JWS signature + * + * @param serializedContent JWS in serialized form + * @param trustedCerts trusted X509 certificates + * @param constraints signature verification constraints + * @return Signature-verification result + * @throws JoseException + * @throws IOException + */ + @Nonnull + public VerificationResult validateSignature(@Nonnull String serializedContent, @Nonnull List trustedCerts, + @Nonnull AlgorithmConstraints constraints) throws JoseException, IOException; + + /** + * Validate a JWS signature + * + * @param serializedContent JWS in serialized form + * @param trustStore with trusted X509 certificates + * @param algconstraints signature verification constraints + * @return Signature-verification result + * @throws JoseException + * @throws IOException + * @throws KeyStoreException + */ + @Nonnull + public VerificationResult validateSignature(@Nonnull String serializedContent, @Nonnull KeyStore trustStore, + @Nonnull AlgorithmConstraints algconstraints) throws JoseException, IOException, KeyStoreException; /** * Get the encryption certificate for SL2.0 End-to-End encryption @@ -44,5 +83,5 @@ public interface IJOSETools { * @throws SL20Exception */ public JsonNode decryptPayload(String compactSerialization) throws SL20Exception; - + } diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JsonSecurityUtils.java b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JsonSecurityUtils.java index 33873f43..c07c6081 100644 --- a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JsonSecurityUtils.java +++ b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JsonSecurityUtils.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.net.MalformedURLException; import java.security.Key; import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; @@ -13,6 +14,7 @@ import java.util.Collections; import java.util.Enumeration; import java.util.List; +import javax.annotation.Nonnull; import javax.annotation.PostConstruct; import org.apache.commons.lang3.StringUtils; @@ -28,6 +30,7 @@ import org.jose4j.lang.JoseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import org.springframework.util.Base64Utils; @@ -57,7 +60,7 @@ public class JsonSecurityUtils implements IJOSETools{ private Key encPrivKey = null; private X509Certificate[] encCertChain = null; - private final List trustedCerts = new ArrayList(); + private List trustedCerts = new ArrayList(); private static JsonMapper mapper = new JsonMapper(); @@ -100,22 +103,10 @@ public class JsonSecurityUtils implements IJOSETools{ log.warn("No encryption key for SL2.0 found. End-to-End encryption is not used. Reason: " + e.getMessage(), e); } - + //load trusted certificates - final Enumeration aliases = keyStore.aliases(); - while(aliases.hasMoreElements()) { - final String el = aliases.nextElement(); - log.trace("Process TrustStoreEntry: " + el); - if (keyStore.isCertificateEntry(el)) { - final Certificate cert = keyStore.getCertificate(el); - if (cert != null && cert instanceof X509Certificate) - trustedCerts.add((X509Certificate) cert); - else - log.info("Can not process entry: " + el + ". Reason: " + cert.toString()); - - } - } - + trustedCerts = readCertsFromKeyStore(keyStore); + //some short validation if (signPrivKey == null || !(signPrivKey instanceof PrivateKey)) { log.info("Can NOT open privateKey for SL2.0 signing. KeyStore=" + getKeyStoreFilePath()); @@ -141,7 +132,6 @@ public class JsonSecurityUtils implements IJOSETools{ } - @Override public String createSignature(String payLoad) throws SLCommandoBuildException { try { @@ -172,75 +162,90 @@ public class JsonSecurityUtils implements IJOSETools{ } @Override - public VerificationResult validateSignature(String serializedContent) throws SL20Exception { - try { - final JsonWebSignature jws = new JsonWebSignature(); - //set payload - jws.setCompactSerialization(serializedContent); - - //set security constrains - jws.setAlgorithmConstraints(new AlgorithmConstraints(ConstraintType.WHITELIST, - SL20Constants.SL20_ALGORITHM_WHITELIST_SIGNING.toArray(new String[SL20Constants.SL20_ALGORITHM_WHITELIST_SIGNING.size()]))); + public VerificationResult validateSignature(String serializedContent, KeyStore trustStore, + AlgorithmConstraints algconstraints) throws JoseException, IOException, KeyStoreException { + final List trustedCertificates = readCertsFromKeyStore(trustStore); + return validateSignature(serializedContent, trustedCertificates , algconstraints); + + } + + @Override + @NonNull + public VerificationResult validateSignature(@Nonnull String serializedContent, @Nonnull List trustedCerts, @Nonnull AlgorithmConstraints constraints) throws JoseException, IOException { + final JsonWebSignature jws = new JsonWebSignature(); + //set payload + jws.setCompactSerialization(serializedContent); - //load signinc certs - Key selectedKey = null; - final List x5cCerts = jws.getCertificateChainHeaderValue(); - final String x5t256 = jws.getX509CertSha256ThumbprintHeaderValue(); - if (x5cCerts != null) { - log.debug("Found x509 certificate in JOSE header ... "); + //set security constrains + jws.setAlgorithmConstraints(constraints); + + //load signinc certs + Key selectedKey = null; + final List x5cCerts = jws.getCertificateChainHeaderValue(); + final String x5t256 = jws.getX509CertSha256ThumbprintHeaderValue(); + if (x5cCerts != null) { + log.debug("Found x509 certificate in JOSE header ... "); log.trace("Sorting received X509 certificates ... "); - final List sortedX5cCerts = X509Utils.sortCertificates(x5cCerts); - - if (trustedCerts.contains(sortedX5cCerts.get(0))) { - selectedKey = sortedX5cCerts.get(0).getPublicKey(); + final List sortedX5cCerts = X509Utils.sortCertificates(x5cCerts); + + if (trustedCerts.contains(sortedX5cCerts.get(0))) { + selectedKey = sortedX5cCerts.get(0).getPublicKey(); + + } else { + log.info("Can NOT find JOSE certificate in truststore."); + try { + log.debug("Cert: " + Base64Utils.encodeToString(sortedX5cCerts.get(0).getEncoded())); - } else { - log.info("Can NOT find JOSE certificate in truststore."); - log.debug("JOSE certificate: " + sortedX5cCerts.get(0).toString()); - try { - log.debug("Cert: " + Base64Utils.encodeToString(sortedX5cCerts.get(0).getEncoded())); - } catch (final CertificateEncodingException e) { - e.printStackTrace(); - } + } catch (final CertificateEncodingException e) { + e.printStackTrace(); } - } else if (StringUtils.isNotEmpty(x5t256)) { - log.debug("Found x5t256 fingerprint in JOSE header .... "); - final X509VerificationKeyResolver x509VerificationKeyResolver = new X509VerificationKeyResolver(trustedCerts); - selectedKey = x509VerificationKeyResolver.resolveKey(jws, Collections.emptyList()); + } + + } else if (StringUtils.isNotEmpty(x5t256)) { + log.debug("Found x5t256 fingerprint in JOSE header .... "); + final X509VerificationKeyResolver x509VerificationKeyResolver = new X509VerificationKeyResolver(trustedCerts); + selectedKey = x509VerificationKeyResolver.resolveKey(jws, Collections.emptyList()); + + } else { + throw new JoseException("JWS contains NO signature certificate or NO certificate fingerprint"); + + } - } else { - log.info("Signed SL2.0 response contains NO signature certificate or NO certificate fingerprint"); - throw new SLCommandoParserException("Signed SL2.0 response contains NO signature certificate or NO certificate fingerprint"); + if (selectedKey == null) { + throw new JoseException("Can NOT select verification key for JWS. Signature verification FAILED"); + + } - } - - if (selectedKey == null) { - log.info("Can NOT select verification key for JWS. Signature verification FAILED."); - throw new SLCommandoParserException("Can NOT select verification key for JWS. Signature verification FAILED"); + //set verification key + jws.setKey(selectedKey); - } + //load payLoad + return new VerificationResult(mapper.getMapper().readTree(jws.getPayload()), null, jws.verifySignature()) ; + + + } + + @Override + @Nonnull + public VerificationResult validateSignature(@Nonnull String serializedContent) throws SL20Exception { + try { + final AlgorithmConstraints algConstraints = new AlgorithmConstraints(ConstraintType.WHITELIST, + SL20Constants.SL20_ALGORITHM_WHITELIST_SIGNING.toArray(new String[SL20Constants.SL20_ALGORITHM_WHITELIST_SIGNING.size()])); - //set verification key - jws.setKey(selectedKey); + final VerificationResult result = validateSignature(serializedContent, trustedCerts, algConstraints); - //validate signature - final boolean valid = jws.verifySignature(); - if (!valid) { + if (!result.isValidSigned()) { log.info("JWS signature invalide. Stopping authentication process ..."); log.debug("Received JWS msg: " + serializedContent); throw new SL20SecurityException("JWS signature invalide."); } - - //load payLoad log.debug("SL2.0 commando signature validation sucessfull"); - final JsonNode sl20Req = mapper.getMapper().readTree(jws.getPayload()); - - return new VerificationResult(sl20Req, null, valid) ; - + return result; + } catch (JoseException | JsonParseException e) { log.warn("SL2.0 commando signature validation FAILED", e); throw new SL20SecurityException(new Object[]{e.getMessage()}, e); @@ -391,4 +396,25 @@ public class JsonSecurityUtils implements IJOSETools{ return value; } + @Nonnull + private List readCertsFromKeyStore(@Nonnull KeyStore keyStore) throws KeyStoreException { + final List result = new ArrayList<>(); + + final Enumeration aliases = keyStore.aliases(); + while(aliases.hasMoreElements()) { + final String el = aliases.nextElement(); + log.trace("Process TrustStoreEntry: " + el); + if (keyStore.isCertificateEntry(el)) { + final Certificate cert = keyStore.getCertificate(el); + if (cert != null && cert instanceof X509Certificate) + result.add((X509Certificate) cert); + else + log.info("Can not process entry: " + el + ". Reason: " + cert.toString()); + + } + } + + return Collections.unmodifiableList(result); + } + } -- cgit v1.2.3