|
|
|
@ -20,17 +20,18 @@ import org.apache.http.impl.client.CloseableHttpClient;
|
|
|
|
|
import org.apache.http.impl.client.HttpClientBuilder;
|
|
|
|
|
import org.apache.http.impl.client.HttpClients;
|
|
|
|
|
import org.apache.http.ssl.SSLContextBuilder;
|
|
|
|
|
import org.apache.xml.security.c14n.Canonicalizer;
|
|
|
|
|
import org.apache.xml.security.signature.XMLSignature;
|
|
|
|
|
import org.opensaml.core.config.ConfigurationService;
|
|
|
|
|
import org.opensaml.core.config.InitializationException;
|
|
|
|
|
import org.opensaml.core.config.InitializationService;
|
|
|
|
|
import org.opensaml.core.criterion.EntityIdCriterion;
|
|
|
|
|
import org.opensaml.core.xml.XMLObject;
|
|
|
|
|
import org.opensaml.core.xml.XMLObjectBuilderFactory;
|
|
|
|
|
import org.opensaml.core.xml.XMLObjectBuilder;
|
|
|
|
|
import org.opensaml.core.xml.config.XMLObjectProviderRegistry;
|
|
|
|
|
import org.opensaml.core.xml.io.*;
|
|
|
|
|
import org.opensaml.core.xml.schema.*;
|
|
|
|
|
import org.opensaml.saml.common.SAMLObject;
|
|
|
|
|
import org.opensaml.saml.common.SAMLObjectBuilder;
|
|
|
|
|
import org.opensaml.saml.common.SAMLVersion;
|
|
|
|
|
import org.opensaml.saml.common.xml.SAMLConstants;
|
|
|
|
|
import org.opensaml.saml.criterion.EntityRoleCriterion;
|
|
|
|
@ -47,7 +48,7 @@ import org.opensaml.security.credential.UsageType;
|
|
|
|
|
import org.opensaml.security.criteria.UsageCriterion;
|
|
|
|
|
import org.opensaml.security.x509.BasicX509Credential;
|
|
|
|
|
import org.opensaml.security.x509.X509Credential;
|
|
|
|
|
import org.opensaml.soap.common.SOAPObjectBuilder;
|
|
|
|
|
import org.opensaml.security.x509.impl.KeyStoreX509CredentialAdapter;
|
|
|
|
|
import org.opensaml.soap.soap11.Body;
|
|
|
|
|
import org.opensaml.soap.soap11.Envelope;
|
|
|
|
|
import org.opensaml.xml.util.Base64;
|
|
|
|
@ -55,7 +56,11 @@ import org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;
|
|
|
|
|
import org.opensaml.xmlsec.encryption.EncryptedKey;
|
|
|
|
|
import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
|
|
|
|
|
import org.opensaml.xmlsec.keyinfo.impl.StaticKeyInfoCredentialResolver;
|
|
|
|
|
import org.opensaml.xmlsec.signature.KeyInfo;
|
|
|
|
|
import org.opensaml.xmlsec.signature.Signature;
|
|
|
|
|
import org.opensaml.xmlsec.signature.X509Data;
|
|
|
|
|
import org.opensaml.xmlsec.signature.support.SignatureValidator;
|
|
|
|
|
import org.opensaml.xmlsec.signature.support.Signer;
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
import org.w3c.dom.Document;
|
|
|
|
@ -67,6 +72,7 @@ import org.w3c.dom.ls.LSSerializer;
|
|
|
|
|
import org.xml.sax.SAXException;
|
|
|
|
|
|
|
|
|
|
import javax.crypto.SecretKey;
|
|
|
|
|
import javax.xml.namespace.QName;
|
|
|
|
|
import javax.xml.parsers.DocumentBuilder;
|
|
|
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
|
|
|
|
import javax.xml.parsers.ParserConfigurationException;
|
|
|
|
@ -78,6 +84,7 @@ import java.io.*;
|
|
|
|
|
import java.net.UnknownHostException;
|
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
|
import java.security.*;
|
|
|
|
|
import java.security.cert.CertificateEncodingException;
|
|
|
|
|
import java.security.cert.X509Certificate;
|
|
|
|
|
import java.time.Instant;
|
|
|
|
|
import java.util.*;
|
|
|
|
@ -144,6 +151,17 @@ public class Saml2SSOUtils {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static XMLObject buildXMLObject(QName objectQName) throws Exception {
|
|
|
|
|
|
|
|
|
|
doBootstrap();
|
|
|
|
|
XMLObjectBuilder builder = registry.getBuilderFactory().getBuilder(objectQName);
|
|
|
|
|
if (builder == null) {
|
|
|
|
|
throw new Exception("Unable to retrieve builder for object QName " + objectQName);
|
|
|
|
|
}
|
|
|
|
|
return builder.buildObject(objectQName.getNamespaceURI(), objectQName.getLocalPart(), objectQName.getPrefix());
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static String getAttributeName(Attribute attribute, Saml2ConfigurableProvider.SAML2UsingFormat usingFormat){
|
|
|
|
|
String friendlyName = attribute.getFriendlyName();
|
|
|
|
|
String name = attribute.getName();
|
|
|
|
@ -231,7 +249,7 @@ public class Saml2SSOUtils {
|
|
|
|
|
|
|
|
|
|
doBootstrap();
|
|
|
|
|
if (artifactString != null){
|
|
|
|
|
ArtifactResolve artifactResolve = generateArtifactResolveReq(artifactString, saml2Provider.getSpEntityId());
|
|
|
|
|
ArtifactResolve artifactResolve = generateArtifactResolveReq(artifactString, saml2Provider);
|
|
|
|
|
ArtifactResponse artifactResponse = sendArtifactResolveRequest(artifactResolve, saml2Provider.getIdpArtifactUrl());
|
|
|
|
|
Response saml2Response = (Response)artifactResponse.getMessage();
|
|
|
|
|
return processSSOResponse(saml2Response, saml2Provider);
|
|
|
|
@ -242,34 +260,27 @@ public class Saml2SSOUtils {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static ArtifactResolve generateArtifactResolveReq(String samlArtReceived, String spEntityId) {
|
|
|
|
|
private static ArtifactResolve generateArtifactResolveReq(String samlArtReceived, Saml2ConfigurableProvider saml2Provider) throws Exception {
|
|
|
|
|
|
|
|
|
|
ArtifactResolve artifactResolve = createArtifactResolveObject(samlArtReceived, spEntityId);
|
|
|
|
|
// if (config.isEnableArtifactResolveSigning()) {
|
|
|
|
|
// artifactResolve = signArtifactResolveReq(artifactResolve);
|
|
|
|
|
// }
|
|
|
|
|
ArtifactResolve artifactResolve = createArtifactResolveObject(samlArtReceived, saml2Provider.getSpEntityId());
|
|
|
|
|
if (saml2Provider.isSignatureRequired()) {
|
|
|
|
|
signArtifactResolveReq(artifactResolve, saml2Provider);
|
|
|
|
|
}
|
|
|
|
|
return artifactResolve;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static ArtifactResolve createArtifactResolveObject(String samlArtReceived, String spEntityId) {
|
|
|
|
|
|
|
|
|
|
XMLObjectBuilderFactory builderFactory = registry.getBuilderFactory();
|
|
|
|
|
private static ArtifactResolve createArtifactResolveObject(String samlArtReceived, String spEntityId) throws Exception {
|
|
|
|
|
|
|
|
|
|
SAMLObjectBuilder<ArtifactResolve> artifactResolveBuilder =
|
|
|
|
|
(SAMLObjectBuilder<ArtifactResolve>) builderFactory.getBuilder(ArtifactResolve.DEFAULT_ELEMENT_NAME);
|
|
|
|
|
ArtifactResolve artifactResolve = artifactResolveBuilder.buildObject();
|
|
|
|
|
ArtifactResolve artifactResolve = (ArtifactResolve)buildXMLObject(ArtifactResolve.DEFAULT_ELEMENT_NAME);
|
|
|
|
|
artifactResolve.setVersion(SAMLVersion.VERSION_20);
|
|
|
|
|
artifactResolve.setID(UUID.randomUUID().toString());
|
|
|
|
|
artifactResolve.setIssueInstant(Instant.now());
|
|
|
|
|
|
|
|
|
|
SAMLObjectBuilder<Artifact> artifactBuilder =
|
|
|
|
|
(SAMLObjectBuilder<Artifact>) builderFactory.getBuilder(Artifact.DEFAULT_ELEMENT_NAME);
|
|
|
|
|
Artifact artifact = artifactBuilder.buildObject();
|
|
|
|
|
Artifact artifact = (Artifact)buildXMLObject(Artifact.DEFAULT_ELEMENT_NAME);
|
|
|
|
|
artifact.setValue(samlArtReceived);
|
|
|
|
|
|
|
|
|
|
SAMLObjectBuilder<Issuer> issuerBuilder = (SAMLObjectBuilder<Issuer>) builderFactory.getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
|
|
|
|
|
Issuer issuer = issuerBuilder.buildObject();
|
|
|
|
|
Issuer issuer = (Issuer)buildXMLObject(Issuer.DEFAULT_ELEMENT_NAME);
|
|
|
|
|
issuer.setValue(spEntityId);
|
|
|
|
|
|
|
|
|
|
artifactResolve.setIssuer(issuer);
|
|
|
|
@ -279,6 +290,57 @@ public class Saml2SSOUtils {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void signArtifactResolveReq(ArtifactResolve artifactResolve, Saml2ConfigurableProvider saml2Provider) throws Exception {
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
KeyStore ks = KeyStore.getInstance("JKS");
|
|
|
|
|
String archivePassword = saml2Provider.getSignatureKeyStorePassword();
|
|
|
|
|
char[] pwdArray = (archivePassword != null) ? archivePassword.toCharArray() : "changeit".toCharArray();
|
|
|
|
|
ks.load(new FileInputStream(saml2Provider.getSignaturePath()), pwdArray);
|
|
|
|
|
X509Credential cred = new KeyStoreX509CredentialAdapter(ks, saml2Provider.getSignatureKeyAlias(), saml2Provider.getSignatureKeyPassword().toCharArray());
|
|
|
|
|
Signature signature = setSignatureRaw(XMLSignature.ALGO_ID_SIGNATURE_RSA, cred);
|
|
|
|
|
artifactResolve.setSignature(signature);
|
|
|
|
|
|
|
|
|
|
List<Signature> signatureList = new ArrayList<>();
|
|
|
|
|
signatureList.add(signature);
|
|
|
|
|
|
|
|
|
|
MarshallerFactory marshallerFactory = registry.getMarshallerFactory();
|
|
|
|
|
Marshaller marshaller = marshallerFactory.getMarshaller(artifactResolve);
|
|
|
|
|
|
|
|
|
|
marshaller.marshall(artifactResolve);
|
|
|
|
|
|
|
|
|
|
org.apache.xml.security.Init.init();
|
|
|
|
|
Signer.signObjects(signatureList);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
throw new Exception("Error while signing the SAML Request message", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static Signature setSignatureRaw(String signatureAlgorithm, X509Credential cred) throws Exception {
|
|
|
|
|
|
|
|
|
|
Signature signature = (Signature)buildXMLObject(Signature.DEFAULT_ELEMENT_NAME);
|
|
|
|
|
signature.setSigningCredential(cred);
|
|
|
|
|
signature.setSignatureAlgorithm(signatureAlgorithm);
|
|
|
|
|
signature.setCanonicalizationAlgorithm(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
KeyInfo keyInfo = (KeyInfo)buildXMLObject(KeyInfo.DEFAULT_ELEMENT_NAME);
|
|
|
|
|
X509Data data = (X509Data)buildXMLObject(X509Data.DEFAULT_ELEMENT_NAME);
|
|
|
|
|
org.opensaml.xmlsec.signature.X509Certificate cert =
|
|
|
|
|
(org.opensaml.xmlsec.signature.X509Certificate) buildXMLObject(
|
|
|
|
|
org.opensaml.xmlsec.signature.X509Certificate.DEFAULT_ELEMENT_NAME);
|
|
|
|
|
String value = org.apache.commons.codec.binary.Base64.encodeBase64String(cred.getEntityCertificate().getEncoded());
|
|
|
|
|
cert.setValue(value);
|
|
|
|
|
data.getX509Certificates().add(cert);
|
|
|
|
|
keyInfo.getX509Datas().add(data);
|
|
|
|
|
signature.setKeyInfo(keyInfo);
|
|
|
|
|
return signature;
|
|
|
|
|
|
|
|
|
|
} catch (CertificateEncodingException e) {
|
|
|
|
|
throw new Exception("Error getting certificate", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static ArtifactResponse sendArtifactResolveRequest(ArtifactResolve artifactResolve, String idpArtifactUrl) throws Exception {
|
|
|
|
|
|
|
|
|
|
Envelope envelope = buildSOAPMessage(artifactResolve);
|
|
|
|
@ -297,17 +359,10 @@ public class Saml2SSOUtils {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static Envelope buildSOAPMessage(SAMLObject samlMessage) {
|
|
|
|
|
|
|
|
|
|
XMLObjectBuilderFactory builderFactory = registry.getBuilderFactory();
|
|
|
|
|
private static Envelope buildSOAPMessage(SAMLObject samlMessage) throws Exception {
|
|
|
|
|
|
|
|
|
|
SOAPObjectBuilder<Envelope> envBuilder = (SOAPObjectBuilder<Envelope>) builderFactory.getBuilder(
|
|
|
|
|
Envelope.DEFAULT_ELEMENT_NAME);
|
|
|
|
|
Envelope envelope = envBuilder.buildObject();
|
|
|
|
|
|
|
|
|
|
SOAPObjectBuilder<Body> bodyBuilder = (SOAPObjectBuilder<Body>) builderFactory.getBuilder(
|
|
|
|
|
Body.DEFAULT_ELEMENT_NAME);
|
|
|
|
|
Body body = bodyBuilder.buildObject();
|
|
|
|
|
Envelope envelope = (Envelope)buildXMLObject(Envelope.DEFAULT_ELEMENT_NAME);
|
|
|
|
|
Body body = (Body)buildXMLObject(Body.DEFAULT_ELEMENT_NAME);
|
|
|
|
|
body.getUnknownXMLObjects().add(samlMessage);
|
|
|
|
|
envelope.setBody(body);
|
|
|
|
|
return envelope;
|
|
|
|
@ -459,10 +514,16 @@ public class Saml2SSOUtils {
|
|
|
|
|
doBootstrap();
|
|
|
|
|
if (saml2SSOResponse != null) {
|
|
|
|
|
byte[] decodedResponse = Base64.decode(saml2SSOResponse);
|
|
|
|
|
ByteArrayInputStream bytesIn = new ByteArrayInputStream(decodedResponse);
|
|
|
|
|
InflaterInputStream inflater = new InflaterInputStream(bytesIn, new Inflater(true));
|
|
|
|
|
String response = new BufferedReader(new InputStreamReader(inflater, StandardCharsets.UTF_8))
|
|
|
|
|
.lines().collect(Collectors.joining("\n"));
|
|
|
|
|
String response;
|
|
|
|
|
if(!saml2Provider.getBinding().equals("Post")){
|
|
|
|
|
ByteArrayInputStream bytesIn = new ByteArrayInputStream(decodedResponse);
|
|
|
|
|
InflaterInputStream inflater = new InflaterInputStream(bytesIn, new Inflater(true));
|
|
|
|
|
response = new BufferedReader(new InputStreamReader(inflater, StandardCharsets.UTF_8))
|
|
|
|
|
.lines().collect(Collectors.joining("\n"));
|
|
|
|
|
}
|
|
|
|
|
else{
|
|
|
|
|
response = new String(decodedResponse);
|
|
|
|
|
}
|
|
|
|
|
Response saml2Response = (Response) Saml2SSOUtils.unmarshall(response);
|
|
|
|
|
return processSSOResponse(saml2Response, saml2Provider);
|
|
|
|
|
|
|
|
|
|