From d0309843cf6775c215bb132283116b6442b082d6 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Thu, 11 Aug 2022 22:02:16 +0200 Subject: refact(core): move JoseUtils into 'eaaf-utils' module --- .../eaaf/modules/auth/sl20/utils/JoseUtils.java | 374 --------------------- .../modules/auth/sl20/utils/JsonSecurityUtils.java | 3 +- .../sl20/utils/AbstractJsonSecurityUtilsTest.java | 1 + .../modules/auth/sl20/utils/JoseUtilsTest.java | 83 ----- 4 files changed, 3 insertions(+), 458 deletions(-) delete mode 100644 eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtils.java delete mode 100644 eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtilsTest.java (limited to 'eaaf_modules/eaaf_module_auth_sl20') diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtils.java b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtils.java deleted file mode 100644 index 5b221bbe..00000000 --- a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtils.java +++ /dev/null @@ -1,374 +0,0 @@ -package at.gv.egiz.eaaf.modules.auth.sl20.utils; - -import java.io.IOException; -import java.security.Key; -import java.security.KeyFactory; -import java.security.KeyStore; -import java.security.NoSuchAlgorithmException; -import java.security.Provider; -import java.security.PublicKey; -import java.security.cert.CertificateEncodingException; -import java.security.cert.X509Certificate; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.security.interfaces.RSAPrivateKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import javax.annotation.Nonnull; - -import org.apache.commons.lang3.StringUtils; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.jose4j.jca.ProviderContext; -import org.jose4j.jwa.AlgorithmConstraints; -import org.jose4j.jws.AlgorithmIdentifiers; -import org.jose4j.jws.JsonWebSignature; -import org.jose4j.jwx.Headers; -import org.jose4j.jwx.JsonWebStructure; -import org.jose4j.keys.resolvers.X509VerificationKeyResolver; -import org.jose4j.lang.JoseException; -import org.springframework.util.Base64Utils; - -import at.gv.egiz.eaaf.core.exception.EaafKeyUsageException; -import at.gv.egiz.eaaf.core.exceptions.EaafException; -import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreUtils; -import at.gv.egiz.eaaf.core.impl.data.Pair; -import at.gv.egiz.eaaf.core.impl.utils.X509Utils; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * {@link JoseUtils} provides static methods JWS and JWE processing. - * - * @author tlenz - * - */ -@Slf4j -public class JoseUtils { - - private static final Provider provider = new BouncyCastleProvider(); - - /** - * Create a JWS signature. - * - *

- * Use {@link org.jose4j.jws.AlgorithmIdentifiers.RSA_PSS_USING_SHA256} in case - * of a RSA based key and - * {@link org.jose4j.jws.AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256} - * in case of an ECC based key. - *

- * - * @param keyStore KeyStore that should be used - * @param keyAlias Alias of the private key - * @param keyPassword Password to access the key - * @param payLoad PayLoad to sign - * @param addFullCertChain If true the full certificate chain will be - * added, otherwise only the - * X509CertSha256Fingerprint is added into JOSE - * header - * @param friendlyNameForLogging FriendlyName for the used KeyStore for logging - * purposes only - * @return Signed PayLoad in serialized form - * @throws EaafException In case of a key-access or key-usage error - * @throws JoseException In case of a JOSE error - */ - public static String createSignature(@Nonnull Pair keyStore, - @Nonnull final String keyAlias, @Nonnull final char[] keyPassword, - @Nonnull final String payLoad, boolean addFullCertChain, - @Nonnull String friendlyNameForLogging) throws EaafException, JoseException { - return createSignature(keyStore, keyAlias, keyPassword, payLoad, addFullCertChain, Collections.emptyMap(), - AlgorithmIdentifiers.RSA_PSS_USING_SHA256, AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256, - friendlyNameForLogging); - - } - - /** - * Create a JWS signature. - * - *

- * Use {@link org.jose4j.jws.AlgorithmIdentifiers.RSA_PSS_USING_SHA256} in case - * of a RSA based key and - * {@link org.jose4j.jws.AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256} - * in case of an ECC based key. - *

- * - * @param keyStore KeyStore that should be used - * @param keyAlias Alias of the private key - * @param keyPassword Password to access the key - * @param payLoad PayLoad to sign - * @param addFullCertChain If true the full certificate chain will be - * added, otherwise only the - * X509CertSha256Fingerprint is added into JOSE - * header - * @param joseHeaders HeaderName and HeaderValue that should be set - * into JOSE header - * @param friendlyNameForLogging FriendlyName for the used KeyStore for logging - * purposes only - * @return Signed PayLoad in serialized form - * @throws EaafException In case of a key-access or key-usage error - * @throws JoseException In case of a JOSE error - */ - public static String createSignature(@Nonnull Pair keyStore, - @Nonnull final String keyAlias, @Nonnull final char[] keyPassword, - @Nonnull final String payLoad, boolean addFullCertChain, - @Nonnull final Map joseHeaders, - @Nonnull String friendlyNameForLogging) throws EaafException, JoseException { - return createSignature(keyStore, keyAlias, keyPassword, payLoad, addFullCertChain, joseHeaders, - AlgorithmIdentifiers.RSA_PSS_USING_SHA256, AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256, - friendlyNameForLogging); - - } - - /** - * Create a JWS signature. - * - * @param keyStore KeyStore that should be used - * @param keyAlias Alias of the private key - * @param keyPassword Password to access the key - * @param payLoad PayLoad to sign - * @param addFullCertChain If true the full certificate chain will be - * added, otherwise only the - * X509CertSha256Fingerprint is added into JOSE - * header - * @param joseHeaders HeaderName and HeaderValue that should be set - * into JOSE header - * @param rsaAlgToUse Signing algorithm that should be used in case - * of a signing key based on RSA - * @param eccAlgToUse Signing algorithm that should be used in case - * of a signing key based on ECC - * @param friendlyNameForLogging FriendlyName for the used KeyStore for logging - * purposes only - * @return Signed PayLoad in serialized form - * @throws EaafException In case of a key-access or key-usage error - * @throws JoseException In case of a JOSE error - */ - public static String createSignature(@Nonnull Pair keyStore, - @Nonnull final String keyAlias, @Nonnull final char[] keyPassword, - @Nonnull final String payLoad, boolean addFullCertChain, - @Nonnull final Map joseHeaders, - @Nonnull final String rsaAlgToUse, @Nonnull final String eccAlgToUse, - @Nonnull String friendlyNameForLogging) throws EaafException, JoseException { - - final JsonWebSignature jws = new JsonWebSignature(); - - // set payload - jws.setPayload(payLoad); - - // set JOSE headers - for (final Entry el : joseHeaders.entrySet()) { - log.trace("Set JOSE header: {} with value: {} into JWS", el.getKey(), el.getValue()); - jws.setHeader(el.getKey(), el.getValue()); - - } - - // set signing information - final Pair signingCred = EaafKeyStoreUtils.getPrivateKeyAndCertificates( - keyStore.getFirst(), keyAlias, keyPassword, true, friendlyNameForLogging); - - // set verification key - jws.setKey(convertToBcKeyIfRequired(signingCred.getFirst())); - - jws.setAlgorithmHeaderValue(getKeyOperationAlgorithmFromCredential( - jws.getKey(), rsaAlgToUse, eccAlgToUse, friendlyNameForLogging)); - - // set special provider if required - if (keyStore.getSecond() != null) { - log.trace("Injecting special Java Security Provider: {}", keyStore.getSecond().getName()); - final ProviderContext providerCtx = new ProviderContext(); - providerCtx.getSuppliedKeyProviderContext().setSignatureProvider(keyStore.getSecond().getName()); - providerCtx.getGeneralProviderContext().setGeneralProvider(BouncyCastleProvider.PROVIDER_NAME); - jws.setProviderContext(providerCtx); - - } else { - final ProviderContext providerCtx = new ProviderContext(); - providerCtx.getGeneralProviderContext().setGeneralProvider(BouncyCastleProvider.PROVIDER_NAME); - jws.setProviderContext(providerCtx); - - } - - if (addFullCertChain) { - jws.setCertificateChainHeaderValue(signingCred.getSecond()); - - } - - jws.setX509CertSha256ThumbprintHeaderValue(signingCred.getSecond()[0]); - - return jws.getCompactSerialization(); - - } - - /** - * Verify a JOSE signature. - * - * @param serializedContent Serialized content that should be verified - * @param trustedCerts Trusted certificates that should be used for - * verification - * @param constraints {@link AlgorithmConstraints} for verification - * @return {@link JwsResult} object - * @throws JoseException In case of a signature verification error - * @throws IOException In case of a general error - */ - public static JwsResult validateSignature(@Nonnull final String serializedContent, - @Nonnull final List trustedCerts, @Nonnull final AlgorithmConstraints constraints) - throws JoseException, IOException { - final JsonWebSignature jws = new JsonWebSignature(); - // set payload - jws.setCompactSerialization(serializedContent); - - // 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(); - - } else { - log.info("Can NOT find JOSE certificate in truststore."); - if (log.isDebugEnabled()) { - try { - log.debug("Cert: {}", Base64Utils.encodeToString(sortedX5cCerts.get(0).getEncoded())); - - } catch (final CertificateEncodingException e) { - log.warn("Can not create DEBUG output", e); - - } - } - } - - } 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"); - - } - - if (selectedKey == null) { - throw new JoseException("Can NOT select verification key for JWS. Signature verification FAILED"); - - } - - //set BouncyCastleProvider as default provider - final ProviderContext providerCtx = new ProviderContext(); - providerCtx.getGeneralProviderContext().setGeneralProvider(BouncyCastleProvider.PROVIDER_NAME); - jws.setProviderContext(providerCtx); - - // set verification key - jws.setKey(convertToBcKeyIfRequired(selectedKey)); - - // load payLoad - return new JwsResult( - jws.verifySignature(), - jws.getUnverifiedPayload(), - jws.getHeaders(), - x5cCerts); - - } - - - /** - * Convert an ECC public-key into BouncyCastle implementation. - * - *

IAIK JCE / Eccelerate ECC Keys are not compatible to JWS impl.

- * @param input Key - * @return input Key, or BC ECC-Key in case of a ECC Key - */ - public static Key convertToBcKeyIfRequired(Key input) { - try { - if (input instanceof ECPublicKey - && "iaik.security.ec.common.ECPublicKey".equals(input.getClass().getName())) { - - //convert Key to BouncyCastle KeyImplemenation because there is an - //incompatibility with IAIK EC Keys and JWS signature-verfification implementation - PublicKey publicKey = KeyFactory.getInstance( - input.getAlgorithm(), provider).generatePublic( - new X509EncodedKeySpec(input.getEncoded())); - return publicKey; - - } else if (input instanceof ECPrivateKey - && "iaik.security.ec.common.ECPrivateKey".equals(input.getClass().getName())) { - //convert Key to BouncyCastle KeyImplemenation because there is an - //incompatibility with IAIK EC Keys and JWS signature-creation implementation - Key privateKey = KeyFactory.getInstance( - input.getAlgorithm(), provider).generatePrivate( - new PKCS8EncodedKeySpec(input.getEncoded())); - - return privateKey; - - } - - } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { - log.warn("Can NOT convert {} to {}. The verification may FAIL.", - input.getClass().getName(), PublicKey.class.getName(), e); - - } - - return input; - - } - - /** - * Select signature algorithm for a given credential. - * - * @param key {@link X509Credential} that will be used for - * key operations - * @param rsaSigAlgorithm RSA based algorithm that should be used in case - * of RSA credential - * @param ecSigAlgorithm EC based algorithm that should be used in case - * of RSA credential - * @param friendlyNameForLogging KeyStore friendlyName for logging purposes - * @return either the RSA based algorithm or the EC based algorithm - * @throws EaafKeyUsageException In case of an unsupported private-key type - */ - private static String getKeyOperationAlgorithmFromCredential(Key key, - String rsaSigAlgorithm, String ecSigAlgorithm, String friendlyNameForLogging) - throws EaafKeyUsageException { - if (key instanceof RSAPrivateKey) { - return rsaSigAlgorithm; - - } else if (key instanceof ECPrivateKey) { - return ecSigAlgorithm; - - } else { - log.warn("Could NOT select the cryptographic algorithm from Private-Key type"); - throw new EaafKeyUsageException(EaafKeyUsageException.ERROR_CODE_01, - friendlyNameForLogging, - "Can not select cryptographic algorithm"); - - } - - } - - private JoseUtils() { - - } - - @Getter - @AllArgsConstructor - public static class JwsResult { - final boolean valid; - final String payLoad; - final Headers fullJoseHeader; - final List x5cCerts; - - } -} 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 58e3e41c..690a07dd 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 @@ -42,14 +42,15 @@ import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreUtils; import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.utils.JoseUtils; import at.gv.egiz.eaaf.core.impl.utils.X509Utils; +import at.gv.egiz.eaaf.core.impl.utils.JoseUtils.JwsResult; import at.gv.egiz.eaaf.modules.auth.sl20.Constants; import at.gv.egiz.eaaf.modules.auth.sl20.data.VerificationResult; import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SL20Exception; import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SL20SecurityException; import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SlCommandoBuildException; import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SlCommandoParserException; -import at.gv.egiz.eaaf.modules.auth.sl20.utils.JoseUtils.JwsResult; @Service public class JsonSecurityUtils implements IJoseTools { diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/AbstractJsonSecurityUtilsTest.java b/eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/AbstractJsonSecurityUtilsTest.java index ae4284d5..18ac843f 100644 --- a/eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/AbstractJsonSecurityUtilsTest.java +++ b/eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/AbstractJsonSecurityUtilsTest.java @@ -36,6 +36,7 @@ import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreUtils; import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.utils.JoseUtils; import at.gv.egiz.eaaf.core.test.dummy.DummyAuthConfigMap; import at.gv.egiz.eaaf.modules.auth.sl20.data.VerificationResult; import iaik.security.ec.provider.ECCelerate; diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtilsTest.java b/eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtilsTest.java deleted file mode 100644 index b5a7639e..00000000 --- a/eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtilsTest.java +++ /dev/null @@ -1,83 +0,0 @@ -package at.gv.egiz.eaaf.modules.auth.sl20.utils; - -import java.io.IOException; -import java.security.NoSuchProviderException; -import java.security.Security; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.apache.commons.io.IOUtils; -import org.jose4j.jwa.AlgorithmConstraints; -import org.jose4j.jwa.AlgorithmConstraints.ConstraintType; -import org.jose4j.jws.AlgorithmIdentifiers; -import org.jose4j.lang.JoseException; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.BlockJUnit4ClassRunner; - -import at.gv.egiz.eaaf.modules.auth.sl20.utils.JoseUtils.JwsResult; -import iaik.security.ec.provider.ECCelerate; -import iaik.security.provider.IAIK; - -@RunWith(BlockJUnit4ClassRunner.class) -public class JoseUtilsTest { - - private static final List BINDING_AUTH_ALGORITHM_WHITELIST_SIGNING = Collections.unmodifiableList( - Arrays.asList( - AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256, - AlgorithmIdentifiers.ECDSA_USING_P521_CURVE_AND_SHA512, - AlgorithmIdentifiers.RSA_PSS_USING_SHA256, - AlgorithmIdentifiers.RSA_PSS_USING_SHA512)); - - /** - *jUnit test class initializer. - */ - @BeforeClass - public static final void classInitializer() { - IAIK.addAsProvider(); - ECCelerate.addAsProvider(); - - } - - /** - * jUnit test class cleaner. - */ - @AfterClass - public static final void classFinisher() { - Security.removeProvider(IAIK.getInstance().getName()); - Security.removeProvider(ECCelerate.getInstance().getName()); - - } - - @Test - public void testBindingAuthBlock() throws JoseException, IOException, CertificateException, NoSuchProviderException { - - final String serializedContent = IOUtils.toString(JoseUtils.class.getResourceAsStream( - "/data/bindingAuth1.jws"), "UTF-8"); - - final iaik.x509.X509Certificate trustedCert = new iaik.x509.X509Certificate(JoseUtils.class - .getResourceAsStream("/data/bindingAuth1.crt")); - - final List trustedCerts = Arrays.asList(trustedCert); - final AlgorithmConstraints constraints = new AlgorithmConstraints(ConstraintType.PERMIT, - BINDING_AUTH_ALGORITHM_WHITELIST_SIGNING - .toArray(new String[BINDING_AUTH_ALGORITHM_WHITELIST_SIGNING.size()])); - - final JwsResult result = JoseUtils.validateSignature(serializedContent, trustedCerts, constraints); - - Assert.assertNotNull("JWS verify result", result); - Assert.assertTrue("JWS not valid", result.isValid()); - Assert.assertNotNull("JWS payload", result.getPayLoad()); - Assert.assertNotNull("JWS Headers", result.getFullJoseHeader()); - Assert.assertNotNull("JWS Signercerts", result.getX5cCerts()); - Assert.assertEquals("Signercerts size", 1, result.getX5cCerts().size()); - Assert.assertArrayEquals("Signercerts", trustedCert.getEncoded(), result.getX5cCerts().get(0).getEncoded()); - - } -} -- cgit v1.2.3