summaryrefslogtreecommitdiff
path: root/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/Saml2Utils.java
diff options
context:
space:
mode:
Diffstat (limited to 'eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/Saml2Utils.java')
-rw-r--r--eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/Saml2Utils.java235
1 files changed, 210 insertions, 25 deletions
diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/Saml2Utils.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/Saml2Utils.java
index 1a282b55..8b1b041b 100644
--- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/Saml2Utils.java
+++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/utils/Saml2Utils.java
@@ -20,8 +20,13 @@
package at.gv.egiz.eaaf.modules.pvp2.impl.utils;
import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPrivateKey;
import java.util.List;
+import javax.annotation.Nonnull;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -31,35 +36,60 @@ import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.Validator;
+import at.gv.egiz.eaaf.core.impl.utils.Random;
+import at.gv.egiz.eaaf.modules.pvp2.PvpConstants;
+import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential;
+import at.gv.egiz.eaaf.modules.pvp2.api.reqattr.EaafRequestedAttribute;
+import at.gv.egiz.eaaf.modules.pvp2.exception.SamlSigningException;
+
+import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
-import org.opensaml.common.xml.SAMLSchemaBuilder;
-import org.opensaml.saml2.core.Attribute;
-import org.opensaml.saml2.core.Status;
-import org.opensaml.saml2.core.StatusCode;
-import org.opensaml.saml2.metadata.AssertionConsumerService;
-import org.opensaml.saml2.metadata.SPSSODescriptor;
-import org.opensaml.ws.soap.soap11.Body;
-import org.opensaml.ws.soap.soap11.Envelope;
-import org.opensaml.xml.XMLObject;
-import org.opensaml.xml.XMLObjectBuilderFactory;
-import org.opensaml.xml.io.Marshaller;
-import org.opensaml.xml.io.MarshallingException;
-import org.opensaml.xml.schema.XSString;
-import org.opensaml.xml.schema.impl.XSStringBuilder;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.XMLObjectBuilderFactory;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.io.Marshaller;
+import org.opensaml.core.xml.io.MarshallingException;
+import org.opensaml.core.xml.schema.XSString;
+import org.opensaml.core.xml.schema.impl.XSStringBuilder;
+import org.opensaml.saml.common.SAMLObjectContentReference;
+import org.opensaml.saml.common.xml.SAMLSchemaBuilder;
+import org.opensaml.saml.common.xml.SAMLSchemaBuilder.SAML1Version;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.Status;
+import org.opensaml.saml.saml2.core.StatusCode;
+import org.opensaml.saml.saml2.metadata.AssertionConsumerService;
+import org.opensaml.saml.saml2.metadata.SPSSODescriptor;
+import org.opensaml.security.SecurityException;
+import org.opensaml.security.x509.X509Credential;
+import org.opensaml.soap.soap11.Body;
+import org.opensaml.soap.soap11.Envelope;
+import org.opensaml.xmlsec.SecurityConfigurationSupport;
+import org.opensaml.xmlsec.SignatureSigningConfiguration;
+import org.opensaml.xmlsec.keyinfo.KeyInfoGenerator;
+import org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorFactory;
+import org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager;
+import org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;
+import org.opensaml.xmlsec.keyinfo.impl.BasicKeyInfoGeneratorFactory;
+import org.opensaml.xmlsec.signature.KeyInfo;
+import org.opensaml.xmlsec.signature.SignableXMLObject;
+import org.opensaml.xmlsec.signature.Signature;
+import org.opensaml.xmlsec.signature.support.ContentReference;
+import org.opensaml.xmlsec.signature.support.SignatureConstants;
+import org.opensaml.xmlsec.signature.support.SignatureException;
+import org.opensaml.xmlsec.signature.support.Signer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
-import at.gv.egiz.eaaf.core.impl.utils.Random;
-import at.gv.egiz.eaaf.modules.pvp2.PvpConstants;
-import at.gv.egiz.eaaf.modules.pvp2.api.reqattr.EaafRequestedAttribute;
-
public class Saml2Utils {
private static final Logger log = LoggerFactory.getLogger(Saml2Utils.class);
private static DocumentBuilder builder;
+ private static SAMLSchemaBuilder schemaBuilder;
static {
+ schemaBuilder = new SAMLSchemaBuilder(SAML1Version.SAML_11);
+
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
@@ -73,16 +103,144 @@ public class Saml2Utils {
}
/**
+ * Sign a OpenSAML 3.x object with a {@link X509Credential}.
+ * <br>
+ * <p>This method used {@link PvpConstants.DEFAULT_SIGNING_METHODE_RSA}
+ * or {@link PvpConstants.DEFAULT_SIGNING_METHODE_EC} as algorithm</p>
+ *
+ * @param <T> {@link SignableXMLObject}
+ * @param toSign object that should be signed
+ * @param signingCredential Credentials that should be used for signing
+ * @param injectCertificate true, if certificate should be part of the signature
+ * @return Signed object
+ * @throws SamlSigningException In case of a signing error
+ */
+ public static <T extends SignableXMLObject> T signSamlObject(@Nonnull T toSign,
+ @Nonnull EaafX509Credential signingCredential, boolean injectCertificate) throws SamlSigningException {
+
+ try {
+ final String usedSigAlg = getSignatureAlgorithm(signingCredential,
+ PvpConstants.DEFAULT_SIGNING_METHODE_RSA,
+ PvpConstants.DEFAULT_SIGNING_METHODE_EC);
+
+ final Signature signature = createSignature(signingCredential, usedSigAlg, injectCertificate);
+ toSign.setSignature(signature);
+
+ final String digestAlgorithm = getDigestAlgorithm(usedSigAlg);
+ final List<ContentReference> contentReferences = signature.getContentReferences();
+ if (!CollectionUtils.isEmpty(contentReferences)) {
+ ((SAMLObjectContentReference) contentReferences.get(0)).setDigestAlgorithm(digestAlgorithm);
+
+ } else {
+ log.error("Unable to set DigestMethodAlgorithm - algorithm {} not set", digestAlgorithm);
+
+ }
+
+ log.trace("Marshall samlToken.");
+ XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(toSign).marshall(toSign);
+
+ log.trace("Sign samlToken.");
+ Signer.signObject(signature);
+
+ return toSign;
+
+ } catch (final SignatureException | MarshallingException | SecurityException e) {
+ throw new SamlSigningException("internal.pvp.96",
+ new Object[] {signingCredential.getEntityId(), e.getMessage()}, e);
+
+ }
+
+ }
+
+ /**
+ * Select signature algorithm for a given credential.
+ *
+ * @param credentials {@link X509Credential} that will be used for signing
+ * @param rsaSigAlgorithm RSA based signing algorithm that should be used in case of RSA credential
+ * @param ecSigAlgorithm EC based signing algorithm that should be used in case of RSA credential
+ * @return either the rsaSigAlgorithm or the ecSigAlgorithm
+ * @throws SamlSigningException In case of an unsupported credential
+ */
+ public static String getSignatureAlgorithm(X509Credential credentials,
+ String rsaSigAlgorithm, String ecSigAlgorithm) throws SamlSigningException {
+ final PrivateKey privatekey = credentials.getPrivateKey();
+ if (privatekey instanceof RSAPrivateKey) {
+ return rsaSigAlgorithm;
+
+ } else if (privatekey instanceof ECPrivateKey) {
+ return ecSigAlgorithm;
+
+ } else {
+ log.warn("Could NOT evaluate the Private-Key type from " + credentials.getEntityId()
+ + " credential.");
+ throw new SamlSigningException("internal.pvp.97",
+ new Object[] {credentials.getEntityId(), privatekey.getClass().getName()});
+
+ }
+ }
+
+ /**
+ * Select a digest algorithm for a already selected signing algorithm.
+ *
+ * @param signatureAlgorithmName Signing algorithm that will be used
+ * @return Digest algorithm identifier
+ */
+ public static String getDigestAlgorithm(String signatureAlgorithmName) {
+ if (StringUtils.isBlank(signatureAlgorithmName)) {
+ return PvpConstants.DEFAULT_DIGESTMETHODE;
+ }
+
+ final String canonicalAlgorithm = signatureAlgorithmName.trim();
+ final String digestAlgorithm = PvpConstants.SIGNATURE_TO_DIGEST_ALGORITHM_MAP.get(canonicalAlgorithm);
+ if (null != digestAlgorithm) {
+ return digestAlgorithm;
+
+ }
+
+ log.warn("Signing algorithm: {} does not contain a known digist algorithm. Use: {} as default",
+ signatureAlgorithmName, PvpConstants.DEFAULT_DIGESTMETHODE);
+ return PvpConstants.DEFAULT_DIGESTMETHODE;
+
+ }
+
+ /**
+ * Get a {@link KeyInfoGenerator} that injects key information into XML signature.
+ *
+ * @param credential @link X509Credential} that will be used for signing
+ * @param injectCertificate Set <code>true</code> if the certificate should be added to KeyInfo
+ * @return Generator for a XML signature key-information
+ */
+ public static KeyInfoGenerator getKeyInfoGenerator(X509Credential credential, boolean injectCertificate) {
+ //OpenSAML3 only support RSA and DSA for direct key injection
+ KeyInfoGeneratorFactory keyInfoGenFac = null;
+ if (injectCertificate || credential.getPublicKey() instanceof ECPublicKey) {
+ final SignatureSigningConfiguration secConfiguration = SecurityConfigurationSupport
+ .getGlobalSignatureSigningConfiguration();
+ final NamedKeyInfoGeneratorManager keyInfoManager = secConfiguration.getKeyInfoGeneratorManager();
+ final KeyInfoGeneratorManager keyInfoGenManager = keyInfoManager.getDefaultManager();
+ keyInfoGenFac = keyInfoGenManager.getFactory(credential);
+
+ } else {
+ keyInfoGenFac = createKeyInfoWithoutCertificate(credential);
+
+ }
+
+ return keyInfoGenFac.newInstance();
+
+ }
+
+
+ /**
* Create a SAML2 object.
*
- * @param <T> SAML2 object class
+ * @param <T> SAML2 object class
* @param clazz object class
* @return SAML2 object
*/
public static <T> T createSamlObject(final Class<T> clazz) {
try {
final XMLObjectBuilderFactory builderFactory =
- org.opensaml.xml.Configuration.getBuilderFactory();
+ XMLObjectProviderRegistrySupport.getBuilderFactory();
final QName defaultElementName =
(QName) clazz.getDeclaredField("DEFAULT_ELEMENT_NAME").get(null);
@@ -119,7 +277,7 @@ public class Saml2Utils {
throws IOException, MarshallingException, TransformerException {
final Document document = builder.newDocument();
final Marshaller out =
- org.opensaml.xml.Configuration.getMarshallerFactory().getMarshaller(object);
+ XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);
out.marshall(object, document);
return document;
}
@@ -132,7 +290,7 @@ public class Saml2Utils {
public static Status getSuccessStatus() {
final Status status = Saml2Utils.createSamlObject(Status.class);
final StatusCode statusCode = Saml2Utils.createSamlObject(StatusCode.class);
- statusCode.setValue(StatusCode.SUCCESS_URI);
+ statusCode.setValue(StatusCode.SUCCESS);
status.setStatusCode(statusCode);
return status;
}
@@ -165,7 +323,7 @@ public class Saml2Utils {
* @return
*/
public static Envelope buildSoap11Envelope(final XMLObject payload) {
- final XMLObjectBuilderFactory bf = org.opensaml.xml.Configuration.getBuilderFactory();
+ final XMLObjectBuilderFactory bf = XMLObjectProviderRegistrySupport.getBuilderFactory();
final Envelope envelope = (Envelope) bf.getBuilder(Envelope.DEFAULT_ELEMENT_NAME)
.buildObject(Envelope.DEFAULT_ELEMENT_NAME);
final Body body =
@@ -211,7 +369,8 @@ public class Saml2Utils {
*/
public static void schemeValidation(final XMLObject xmlObject) throws Exception {
try {
- final Schema test = SAMLSchemaBuilder.getSAML11Schema();
+
+ final Schema test = schemaBuilder.getSAMLSchema();
final Validator val = test.newValidator();
final DOMSource source = new DOMSource(xmlObject.getDOM());
val.validate(source);
@@ -227,11 +386,37 @@ public class Saml2Utils {
private static XMLObject createAttributeValue(final QName attributeValueType,
final String value) {
- final XSStringBuilder stringBuilder = (XSStringBuilder) org.opensaml.xml.Configuration
+ final XSStringBuilder stringBuilder = (XSStringBuilder) XMLObjectProviderRegistrySupport
.getBuilderFactory().getBuilder(XSString.TYPE_NAME);
final XSString stringValue = stringBuilder.buildObject(attributeValueType, XSString.TYPE_NAME);
stringValue.setValue(value);
return stringValue;
}
+
+ private static Signature createSignature(X509Credential signingCredential,
+ String usedSigAlg, boolean injectCertificate)
+ throws SecurityException, SamlSigningException {
+ log.trace("Generating OpenSAML signature object ... ");
+ final Signature signature = (Signature) XMLObjectProviderRegistrySupport.getBuilderFactory()
+ .getBuilder(Signature.DEFAULT_ELEMENT_NAME)
+ .buildObject(Signature.DEFAULT_ELEMENT_NAME);
+ signature.setSigningCredential(signingCredential);
+ signature.setSignatureAlgorithm(usedSigAlg);
+ final KeyInfo keyInfo = getKeyInfoGenerator(signingCredential, injectCertificate).generate(signingCredential);
+ signature.setKeyInfo(keyInfo);
+ signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
+ return signature;
+
+ }
+
+ private static KeyInfoGeneratorFactory createKeyInfoWithoutCertificate(X509Credential credential) {
+ final KeyInfoGeneratorFactory keyInfoGenFac = new BasicKeyInfoGeneratorFactory();
+ ((BasicKeyInfoGeneratorFactory)keyInfoGenFac).setEmitPublicKeyValue(true);
+ ((BasicKeyInfoGeneratorFactory)keyInfoGenFac).setEmitEntityIDAsKeyName(true);
+ ((BasicKeyInfoGeneratorFactory)keyInfoGenFac).setEmitKeyNames(true);
+ ((BasicKeyInfoGeneratorFactory)keyInfoGenFac).setEmitPublicDEREncodedKeyValue(true);
+ return keyInfoGenFac;
+ }
+
}