aboutsummaryrefslogtreecommitdiff
path: root/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules
diff options
context:
space:
mode:
authorlalber <lukas.alber@iaik.tugraz.at>2020-10-20 07:47:44 +0200
committerlalber <lukas.alber@iaik.tugraz.at>2020-10-20 07:47:44 +0200
commitf696b4085fd10b3b9d627437f439b222903e13e1 (patch)
tree16ab04251dab734e2ce0614a4c71668f3b68ae81 /eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules
parent8ff69c460d5081feb3ece7757b3673ae81cde7da (diff)
downloadNational_eIDAS_Gateway-f696b4085fd10b3b9d627437f439b222903e13e1.tar.gz
National_eIDAS_Gateway-f696b4085fd10b3b9d627437f439b222903e13e1.tar.bz2
National_eIDAS_Gateway-f696b4085fd10b3b9d627437f439b222903e13e1.zip
stuck on how to test
Diffstat (limited to 'eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules')
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrClient.java119
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateIdentityLinkTask.java141
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/JoseUtils.java305
3 files changed, 492 insertions, 73 deletions
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrClient.java
index 763d8dab..5a551649 100644
--- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrClient.java
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrClient.java
@@ -33,9 +33,7 @@ import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import javax.annotation.PostConstruct;
import javax.net.ssl.KeyManager;
@@ -56,7 +54,10 @@ import javax.xml.ws.BindingProvider;
import javax.xml.ws.Dispatch;
import javax.xml.ws.handler.Handler;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.StopWatch;
import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
@@ -79,15 +80,7 @@ import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
import at.gv.egiz.eaaf.core.impl.utils.DomUtils;
import at.gv.egiz.eaaf.core.impl.utils.FileUtils;
import at.gv.egiz.eaaf.core.impl.utils.KeyStoreUtils;
-import szrservices.GetBPK;
-import szrservices.GetBPKResponse;
-import szrservices.GetIdentityLinkEidas;
-import szrservices.GetIdentityLinkEidasResponse;
-import szrservices.IdentityLinkType;
-import szrservices.ObjectFactory;
-import szrservices.PersonInfoType;
-import szrservices.SZR;
-import szrservices.SZRException_Exception;
+import szrservices.*;
@Service("SZRClientForeIDAS")
public class SzrClient {
@@ -109,9 +102,11 @@ public class SzrClient {
private String szrUrl = null;
private QName qname = null;
+ final ObjectMapper mapper = new ObjectMapper();
+
/**
* Get IdentityLink of a person.
- *
+ *
* @param personInfo Person identification information
* @return IdentityLink
* @throws SzrCommunicationException In case of a SZR error
@@ -165,7 +160,7 @@ public class SzrClient {
} catch (final Exception e) {
log.warn("SZR communication FAILED. Reason: " + e.getMessage(), e);
- throw new SzrCommunicationException("ernb.02", new Object[] { e.getMessage() }, e);
+ throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e);
}
@@ -173,19 +168,19 @@ public class SzrClient {
/**
* Get bPK of person.
- *
+ *
* @param personInfo Person identification information
- * @param target requested bPK target
- * @param vkz Verfahrenskennzeichen
+ * @param target requested bPK target
+ * @param vkz Verfahrenskennzeichen
* @return bPK for this person
* @throws SzrCommunicationException In case of a SZR error
*/
- public String getBpk(PersonInfoType personInfo, String target, String vkz)
+ public List<String> getBpk(PersonInfoType personInfo, String target, String vkz)
throws SzrCommunicationException {
try {
final GetBPK parameters = new GetBPK();
parameters.setPersonInfo(personInfo);
- parameters.setBereichsKennung(target);
+ parameters.getBereichsKennung().add(target);
parameters.setVKZ(vkz);
final GetBPKResponse result = this.szr.getBPK(parameters);
@@ -193,16 +188,98 @@ public class SzrClient {
} catch (final SZRException_Exception e) {
log.warn("SZR communication FAILED. Reason: " + e.getMessage(), e);
- throw new SzrCommunicationException("ernb.02", new Object[] { e.getMessage() }, e);
+ throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e);
}
}
+ /**
+ * Request a encryped baseId from SRZ.
+ *
+ * @param personInfo Minimum dataset of person
+ * @return encrypted baseId
+ * @throws SzrCommunicationException In case of a SZR error
+ */
+ public String getEncryptedStammzahl(final PersonInfoType personInfo)
+ throws SzrCommunicationException {
+
+ final String resp;
+ try {
+ resp = this.szr.getStammzahlEncrypted(personInfo, false);
+ } catch (SZRException_Exception e) {
+ throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e);
+ }
+
+ if (resp == null || StringUtils.isEmpty(resp)) {
+ throw new SzrCommunicationException("ernb.01", new Object[]{"Stammzahl response empty"}); // TODO error handling
+ }
+
+ return resp;
+
+ }
+
+
+ /**
+ * Signs content.
+ *
+ * @param vsz ? TODO
+ * @param bindingPubKey binding PublikKey as PKCS1# (ASN.1) container
+ * @param eidStatus Status of the E-ID
+ * @return bPK for this person
+ * @throws SzrCommunicationException In case of a SZR error
+ */
+ public String getBcBind(final String vsz, final String bindingPubKey, final String eidStatus)
+ throws SzrCommunicationException {
+
+ final String ATTR_NAME_VSZ = "urn:eidgvat:attributes.vsz.value";
+ final String ATTR_NAME_PUBKEYS = "urn:eidgvat:attributes.user.pubkeys";
+ final String ATTR_NAME_STATUS = "urn:eidgvat:attributes.eid.status";
+ final String KEY_BC_BIND = "bcBindReq";
+ final String JOSE_HEADER_USERCERTPINNING_TYPE = "urn:at.gv.eid:bindtype";
+ final String JOSE_HEADER_USERCERTPINNING_EIDASBIND = "urn:at.gv.eid:eidasBind";
+
+ final Map<String, Object> bcBindMap = new HashMap<>();
+ bcBindMap.put(ATTR_NAME_VSZ, vsz);
+ bcBindMap.put(ATTR_NAME_STATUS, eidStatus);
+ bcBindMap.put(ATTR_NAME_PUBKEYS, Arrays.asList(bindingPubKey));
+
+ try {
+ final String serializedBcBind = mapper.writeValueAsString(bcBindMap);
+ final SignContent req = new SignContent();
+ final SignContentEntry bcBindInfo = new SignContentEntry();
+ bcBindInfo.setKey(KEY_BC_BIND);
+ bcBindInfo.setValue(serializedBcBind);
+ req.getIn().add(bcBindInfo);
+ req.setAppendCert(false);
+ final JwsHeaderParam bcBindJoseHeader = new JwsHeaderParam();
+ bcBindJoseHeader.setKey(JOSE_HEADER_USERCERTPINNING_TYPE);
+ bcBindJoseHeader.setValue(JOSE_HEADER_USERCERTPINNING_EIDASBIND);
+ req.getJWSHeaderParam().add(bcBindJoseHeader);
+
+ log.trace("Requesting SZR to sign bcBind datastructure ... ");
+ final SignContentResponseType resp = szr.signContent(req.isAppendCert(), req.getJWSHeaderParam(), req.getIn());
+ log.trace("Receive SZR response on bcBind siging operation ");
+
+ if (resp == null
+ || resp.getOut().isEmpty()
+ || resp.getOut().get(0).getValue() == null) {
+ throw new SzrCommunicationException("ernb.01", new Object[]{"BcBind response empty"}); //TODO check error handling
+ }
+
+ return resp.getOut().get(0).getValue();
+
+ } catch (final JsonProcessingException | SZRException_Exception e) {
+ log.warn("Requesting bcBind by using SZR FAILED. Reason: {}", e.getMessage(), null, e);
+ throw new SzrCommunicationException("ernb.02",
+ new Object[]{e.getMessage()}, e);
+ }
+ }
+
@PostConstruct
private void initialize() {
log.info("Starting SZR-Client initialization .... ");
- final URL url = SzrClient.class.getResource("/szr_client/SZR-1.1.WSDL");
+ final URL url = SzrClient.class.getResource("/szr_client/SZR_v4.0.wsdl");
final boolean useTestSzr = basicConfig.getBasicConfigurationBoolean(
Constants.CONIG_PROPS_EIDAS_SZRCLIENT_USETESTSERVICE,
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateIdentityLinkTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateIdentityLinkTask.java
index 88c3515b..e4a22cbc 100644
--- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateIdentityLinkTask.java
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateIdentityLinkTask.java
@@ -19,28 +19,10 @@
* file for details on the various modules and licenses.
* The "NOTICE" text file is part of the distribution. Any derivative works
* that you distribute must include a readable copy of the "NOTICE" text file.
-*/
+ */
package at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.lang3.StringUtils;
-import org.joda.time.DateTime;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
import at.asitplus.eidas.specific.connector.MsConnectorEventCodes;
import at.asitplus.eidas.specific.connector.MsEidasNodeConstants;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
@@ -60,6 +42,8 @@ import at.gv.egiz.eaaf.core.api.idp.auth.data.IIdentityLink;
import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
import at.gv.egiz.eaaf.core.exceptions.EaafException;
import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
+import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory;
+import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration;
import at.gv.egiz.eaaf.core.impl.data.Pair;
import at.gv.egiz.eaaf.core.impl.idp.auth.builder.BpkBuilder;
import at.gv.egiz.eaaf.core.impl.idp.auth.data.AuthProcessDataWrapper;
@@ -67,20 +51,38 @@ import at.gv.egiz.eaaf.core.impl.idp.auth.data.SimpleIdentityLinkAssertionParser
import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;
import at.gv.egiz.eaaf.core.impl.utils.DomUtils;
import at.gv.egiz.eaaf.core.impl.utils.XPathUtils;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import eu.eidas.auth.commons.attribute.AttributeDefinition;
import eu.eidas.auth.commons.attribute.AttributeValue;
import eu.eidas.auth.commons.light.ILightResponse;
import eu.eidas.auth.commons.protocol.eidas.impl.PostalAddress;
import lombok.extern.slf4j.Slf4j;
+import lombok.val;
+import org.apache.commons.lang3.StringUtils;
+import org.joda.time.DateTime;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
import szrservices.IdentityLinkType;
import szrservices.PersonInfoType;
import szrservices.TravelDocumentType;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.InputStream;
+import java.security.KeyStoreException;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
/**
* Task that creates the IdentityLink for an eIDAS authenticated person.
- *
- * @author tlenz
*
+ * @author tlenz
*/
@Slf4j
@Component("CreateIdentityLinkTask")
@@ -95,7 +97,7 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {
/*
* (non-Javadoc)
- *
+ *
* @see at.gv.egovernment.moa.id.process.springweb.MoaIdTask#execute(at.gv.
* egovernment.moa.id.process.api.ExecutionContext,
* javax.servlet.http.HttpServletRequest,
@@ -103,7 +105,7 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {
*/
@Override
public void execute(ExecutionContext executionContext,
- HttpServletRequest request, HttpServletResponse response)
+ HttpServletRequest request, HttpServletResponse response)
throws TaskExecutionException {
try {
final AuthProcessDataWrapper authProcessData = pendingReq.getSessionData(AuthProcessDataWrapper.class);
@@ -217,43 +219,64 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {
}
}
- final IdentityLinkType result = szrClient.getIdentityLinkInRawMode(personInfo);
+ String eidMode = pendingReq.getServiceProviderConfiguration().getConfigurationValue(MsEidasNodeConstants.PROP_CONFIG_SP_EID_MODE, "old");
+ if (eidMode.equals("new")) {
- final Element idlFromSzr = (Element) result.getAssertion();
- identityLink = new SimpleIdentityLinkAssertionParser(idlFromSzr).parseIdentityLink();
+ String vsz = szrClient.getEncryptedStammzahl(personInfo);
- // write ERnB inputdata into revisionlog
- if (basicConfig.getBasicConfigurationBoolean(
- Constants.CONIG_PROPS_EIDAS_SZRCLIENT_WORKAROUND_REVISIONLOGDATASTORE_ACTIVE, false)) {
- revisionsLogger.logEvent(pendingReq,
- MsConnectorEventCodes.SZR_ERNB_EIDAS_RAW_ID,
- (String) simpleAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER));
- revisionsLogger.logEvent(pendingReq,
- MsConnectorEventCodes.SZR_ERNB_EIDAS_ERNB_ID, eidData.getPseudonym());
+ // build Keystore
+ String pK64 = getPkFromKeystore();
+ // setzte Keystore in config ?path? lade rein
+ // key pair art siehe jose utils
- }
- // get bPK from SZR
- if (basicConfig.getBasicConfigurationBoolean(
- Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_USESRZFORBPKGENERATION, true)) {
- bpk = szrClient.getBpk(
- personInfo,
- pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier(),
- basicConfig.getBasicConfiguration(
- Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_VKZ,
- "no VKZ defined"));
+ String signedEidasBind = szrClient.getBcBind(vsz, pK64, "urn:eidgvat:eid.status.eidas"); //eidstatus TODO as config?
+
+ //build AuthBlock JWS
+ ObjectMapper mapper = new ObjectMapper();
+ String jwsPayload = mapper.writeValueAsString(pendingReq.getUniqueTransactionIdentifier());
+
+// JoseUtils.createSignature(new Pair<>(ks, ks.getProvider()), "connectorkeypair", passord.chararray(), jwsPayload, false, ); //TODO joseutils kopiern
} else {
- log.debug("Calculating bPK from baseId ... ");
- new BpkBuilder();
- final Pair<String, String> bpkCalc = BpkBuilder.generateAreaSpecificPersonIdentifier(
- identityLink.getIdentificationValue(),
- identityLink.getIdentificationType(),
- pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier());
- bpk = bpkCalc.getFirst();
- }
+ final IdentityLinkType result = szrClient.getIdentityLinkInRawMode(personInfo);
+ final Element idlFromSzr = (Element) result.getAssertion();
+ identityLink = new SimpleIdentityLinkAssertionParser(idlFromSzr).parseIdentityLink();
+
+ // write ERnB inputdata into revisionlog
+ if (basicConfig.getBasicConfigurationBoolean(
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_WORKAROUND_REVISIONLOGDATASTORE_ACTIVE, false)) {
+ revisionsLogger.logEvent(pendingReq,
+ MsConnectorEventCodes.SZR_ERNB_EIDAS_RAW_ID,
+ (String) simpleAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER));
+ revisionsLogger.logEvent(pendingReq,
+ MsConnectorEventCodes.SZR_ERNB_EIDAS_ERNB_ID, eidData.getPseudonym());
+
+ }
+
+ // get bPK from SZR
+ if (basicConfig.getBasicConfigurationBoolean(
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_USESRZFORBPKGENERATION, true)) {
+ bpk = szrClient.getBpk(
+ personInfo,
+ pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier(),
+ basicConfig.getBasicConfiguration(
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_VKZ,
+ "no VKZ defined")).get(0);
+
+ } else {
+ log.debug("Calculating bPK from baseId ... ");
+ new BpkBuilder();
+ final Pair<String, String> bpkCalc = BpkBuilder.generateAreaSpecificPersonIdentifier(
+ identityLink.getIdentificationValue(),
+ identityLink.getIdentificationType(),
+ pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier());
+ bpk = bpkCalc.getFirst();
+
+ }
+ }
}
if (identityLink == null) {
@@ -307,6 +330,20 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {
}
}
+ private String getPkFromKeystore() throws EaafException, KeyStoreException {
+ EaafKeyStoreFactory keyStoreFactory = new EaafKeyStoreFactory();
+ KeyStoreConfiguration configuration = new KeyStoreConfiguration();
+
+ final String current = new java.io.File(".").toURI().toString();
+ configuration.setSoftKeyStoreFilePath(current + "src/test/resources/keystore/teststore.jks");
+
+ configuration.setSoftKeyStorePassword("f/+saJBc3a}*/T^s");
+ configuration.setKeyStoreType(KeyStoreConfiguration.KeyStoreType.JKS);
+ val ks = keyStoreFactory.buildNewKeyStore(configuration);
+ val publicKey = ks.getFirst().getCertificate("connectorkeypair").getPublicKey();
+ return Base64.getEncoder().encodeToString(publicKey.getEncoded());
+ }
+
private String extendBpkByPrefix(String bpk, String type) {
String bpkType = null;
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/JoseUtils.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/JoseUtils.java
new file mode 100644
index 00000000..e81c4c92
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/JoseUtils.java
@@ -0,0 +1,305 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.utils;
+
+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;
+import org.apache.commons.lang3.StringUtils;
+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 javax.annotation.Nonnull;
+import java.io.IOException;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * {@link JoseUtils} provides static methods JWS and JWE processing.
+ *
+ * @author tlenz
+ *
+ */
+@Slf4j
+public class JoseUtils {
+
+ /**
+ * Create a JWS signature.
+ *
+ * <p>
+ * Use {@link AlgorithmIdentifiers.RSA_PSS_USING_SHA256} in case
+ * of a RSA based key and
+ * {@link AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256}
+ * in case of an ECC based key.
+ * </p>
+ *
+ * @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, Provider> 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.
+ *
+ * <p>
+ * Use {@link AlgorithmIdentifiers.RSA_PSS_USING_SHA256} in case
+ * of a RSA based key and
+ * {@link AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256}
+ * in case of an ECC based key.
+ * </p>
+ *
+ * @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, Provider> keyStore,
+ @Nonnull final String keyAlias, @Nonnull final char[] keyPassword,
+ @Nonnull final String payLoad, boolean addFullCertChain,
+ @Nonnull final Map<String, String> 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, Provider> keyStore,
+ @Nonnull final String keyAlias, @Nonnull final char[] keyPassword,
+ @Nonnull final String payLoad, boolean addFullCertChain,
+ @Nonnull final Map<String, String> 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<String, String> 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<Key, X509Certificate[]> signingCred = EaafKeyStoreUtils.getPrivateKeyAndCertificates(
+ keyStore.getFirst(), keyAlias, keyPassword, true, friendlyNameForLogging);
+ jws.setKey(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());
+ 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<X509Certificate> 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<X509Certificate> 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<X509Certificate> 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.<JsonWebStructure>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 verification key
+ jws.setKey(selectedKey);
+
+ // load payLoad
+ return new JwsResult(
+ jws.verifySignature(),
+ jws.getUnverifiedPayload(),
+ jws.getHeaders(),
+ x5cCerts);
+
+ }
+
+ /**
+ * 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<X509Certificate> x5cCerts;
+
+ }
+}