Author: sergeyb
Date: Thu Sep 8 15:32:45 2011
New Revision: 1166749
URL: http://svn.apache.org/viewvc?rev=1166749&view=rev
Log:
[CXF-3588] Some improvements to do with validating saml tokens
Modified:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlInHandler.java
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/SecurityContextProviderImpl.java
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java
cxf/trunk/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/saml/SamlCallbackHandler.java
Modified:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlInHandler.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlInHandler.java?rev=1166749&r1=1166748&r2=1166749&view=diff
==============================================================================
---
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlInHandler.java
(original)
+++
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlInHandler.java
Thu Sep 8 15:32:45 2011
@@ -25,7 +25,6 @@ import java.io.InputStreamReader;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
-import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
@@ -34,7 +33,7 @@ import javax.ws.rs.core.Response;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-
+import org.w3c.dom.Node;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.helpers.DOMUtils;
@@ -96,8 +95,9 @@ public abstract class AbstractSamlInHand
protected void validateToken(Message message, Element tokenElement) {
try {
AssertionWrapper assertion = new AssertionWrapper(tokenElement);
+
+ RequestData data = new RequestData();
if (assertion.isSigned()) {
- RequestData data = new RequestData();
WSSConfig cfg = WSSConfig.getNewInstance();
data.setWssConfig(cfg);
data.setCallbackHandler(SecurityUtils.getCallbackHandler(message,
this.getClass()));
@@ -112,27 +112,15 @@ public abstract class AbstractSamlInHand
message.getContextualProperty(WSHandlerConstants.ENABLE_REVOCATION)));
assertion.verifySignature(data, null);
assertion.parseHOKSubject(data, null);
+ }
+ if (samlValidator != null) {
Credential credential = new Credential();
credential.setAssertion(assertion);
- if (samlValidator != null) {
- samlValidator.validate(credential, data);
- }
-
- Certificate[] tlsCerts = getTLSCertificates(message);
- if (!checkHolderOfKey(message, assertion, tlsCerts)) {
- throwFault("Holder Of Key claim fails", null);
- }
- if (!checkSenderVouches(message, assertion, tlsCerts)) {
- throwFault("Sender vouchers claim fails", null);
- }
- if (!checkBearer(assertion, tlsCerts)) {
- throwFault("Bearer claim fails", null);
- }
- } else if (getTLSCertificates(message) == null) {
- // alternatively ensure that the unsigned assertion inherits
the signature
- // from the xml-sig envelope which this assertion must be
contained in
- throwFault("Unsigned Assertion can only be validated with
two-way TLS", null);
+ samlValidator.validate(credential, data);
}
+
+
+ checkSubjectConfirmationData(message, assertion);
setSecurityContext(message, assertion);
} catch (Exception ex) {
@@ -140,6 +128,19 @@ public abstract class AbstractSamlInHand
}
}
+ protected void checkSubjectConfirmationData(Message message,
AssertionWrapper assertion) {
+ Certificate[] tlsCerts = getTLSCertificates(message);
+ if (!checkHolderOfKey(message, assertion, tlsCerts)) {
+ throwFault("Holder Of Key claim fails", null);
+ }
+ if (!checkSenderVouches(message, assertion, tlsCerts)) {
+ throwFault("Sender vouchers claim fails", null);
+ }
+ if (!checkBearer(assertion, tlsCerts)) {
+ throwFault("Bearer claim fails", null);
+ }
+ }
+
protected void setSecurityContext(Message message, AssertionWrapper
wrapper) {
if (scProvider != null) {
SecurityContext sc = scProvider.getSecurityContext(message,
wrapper);
@@ -164,7 +165,7 @@ public abstract class AbstractSamlInHand
* Check the sender-vouches requirements against the received assertion.
The SAML
* Assertion and the request body must be signed by the same signature.
*/
- private boolean checkSenderVouches(
+ protected boolean checkSenderVouches(
Message message,
AssertionWrapper assertionWrapper,
Certificate[] tlsCerts
@@ -182,16 +183,24 @@ public abstract class AbstractSamlInHand
List<String> confirmationMethods =
assertionWrapper.getConfirmationMethods();
for (String confirmationMethod : confirmationMethods) {
if (OpenSAMLUtil.isMethodSenderVouches(confirmationMethod)) {
- XMLSignature sig = message.getContent(XMLSignature.class);
- if (sig == null) {
- return false;
- }
- try {
- byte[] sigValue1 = sig.getSignatureValue();
- byte[] sigValue2 = assertionWrapper.getSignatureValue();
- return Arrays.equals(sigValue1, sigValue2);
- } catch (Exception ex) {
- return false;
+
+ Element signedElement = message.getContent(Element.class);
+ Node assertionParent =
assertionWrapper.getElement().getParentNode();
+
+ // if we have a shared parent signed node then we can assume
both
+ // this SAML assertion and the main payload have been signed
by the same
+ // signature
+ if (assertionParent != signedElement) {
+ // if not then try to compare if the same cert/key was
used to sign SAML token
+ // and the payload
+ XMLSignature signature =
message.getContent(XMLSignature.class);
+ if (signature == null) {
+ return false;
+ }
+ SAMLKeyInfo subjectKeyInfo =
assertionWrapper.getSignatureKeyInfo();
+ if (!compareCredentials(subjectKeyInfo, signature,
tlsCerts)) {
+ return false;
+ }
}
}
}
@@ -200,7 +209,7 @@ public abstract class AbstractSamlInHand
- public boolean checkHolderOfKey(Message message,
+ protected boolean checkHolderOfKey(Message message,
AssertionWrapper assertionWrapper,
Certificate[] tlsCerts) {
List<String> confirmationMethods =
assertionWrapper.getConfirmationMethods();
@@ -267,13 +276,14 @@ public abstract class AbstractSamlInHand
return false;
}
- private boolean checkBearer(AssertionWrapper assertionWrapper,
Certificate[] tlsCerts) {
+ protected boolean checkBearer(AssertionWrapper assertionWrapper,
Certificate[] tlsCerts) {
List<String> confirmationMethods =
assertionWrapper.getConfirmationMethods();
for (String confirmationMethod : confirmationMethods) {
- if (isMethodBearer(confirmationMethod)) {
- // do some more validation - time based, etc
- return true;
- }
+ boolean isBearer = isMethodBearer(confirmationMethod);
+ if (isBearer && !assertionWrapper.isSigned() && (tlsCerts == null
|| tlsCerts.length == 0)) {
+ return false;
+ }
+ // do some more validation - time based, etc
}
return true;
}
Modified:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/SecurityContextProviderImpl.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/SecurityContextProviderImpl.java?rev=1166749&r1=1166748&r2=1166749&view=diff
==============================================================================
---
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/SecurityContextProviderImpl.java
(original)
+++
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/SecurityContextProviderImpl.java
Thu Sep 8 15:32:45 2011
@@ -59,6 +59,6 @@ public class SecurityContextProviderImpl
// parse/decipher subject name id, or check attributes like
// givenName, email, firstName, etc
- // this can be overidden, but consider also introducing dedicated
handlers
+ // this can be overridden, but consider also introducing dedicated
handlers
}
}
Modified:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java?rev=1166749&r1=1166748&r2=1166749&view=diff
==============================================================================
---
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java
(original)
+++
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java
Thu Sep 8 15:32:45 2011
@@ -67,8 +67,8 @@ public class XmlSigInHandler extends Abs
}
Element root = doc.getDocumentElement();
- Element sigElement = getSignatureElement(root);
- if (sigElement == null) {
+ Element signatureElement = getSignatureElement(root);
+ if (signatureElement == null) {
throwFault("XML Signature is not available", null);
}
@@ -89,7 +89,7 @@ public class XmlSigInHandler extends Abs
boolean valid = false;
Reference ref = null;
try {
- XMLSignature signature = new XMLSignature(sigElement, "");
+ XMLSignature signature = new XMLSignature(signatureElement, "");
// See also WSS4J SAMLUtil.getCredentialFromKeyInfo
KeyInfo keyInfo = signature.getKeyInfo();
@@ -104,13 +104,14 @@ public class XmlSigInHandler extends Abs
}
// is this call redundant given that signature.checkSignatureValue
uses References ?
ref = getReference(signature);
- validateReference(root, ref);
+ Element signedElement = validateReference(root, ref);
// validate trust
new TrustValidator().validateTrust(crypto, cert,
keyInfo.getPublicKey());
if (persistSignature) {
- message.put(XMLSignature.class, signature);
+ message.setContent(XMLSignature.class, signature);
+ message.setContent(Element.class, signedElement);
}
} catch (Exception ex) {
throwFault("Signature validation failed", ex);
@@ -122,7 +123,7 @@ public class XmlSigInHandler extends Abs
if (!isEnveloping(root)) {
Element signedEl = getSignedElement(root, ref);
signedEl.removeAttribute("ID");
- root.removeChild(sigElement);
+ root.removeChild(signatureElement);
} else {
Element actualBody = getActualBody(root);
Document newDoc = DOMUtils.createDocument();
@@ -180,34 +181,30 @@ public class XmlSigInHandler extends Abs
return null;
}
- protected void validateReference(Element root, Reference ref) {
- boolean detached = false;
- if (!isEnveloping(root)) {
- String rootId = root.getAttribute("ID");
- String refId = ref.getURI();
- if (refId.length() == 0 && rootId.length() == 0) {
- // or fragment must be expected ?
- return;
- }
- if (!refId.startsWith("#") || refId.length() <= 1) {
- throwFault("Only local Signature References are supported",
null);
- }
-
- Element signedEl = getSignedElement(root, ref);
- if (signedEl != null) {
- detached = signedEl != root;
- } else {
- throwFault("Signature Reference ID is invalid", null);
- }
+ protected Element validateReference(Element root, Reference ref) {
+ boolean enveloped = false;
+
+ String refId = ref.getURI();
+
+ if (!refId.startsWith("#") || refId.length() <= 1) {
+ throwFault("Only local Signature References are supported", null);
}
+ Element signedEl = getSignedElement(root, ref);
+ if (signedEl != null) {
+ enveloped = signedEl == root;
+ } else {
+ throwFault("Signature Reference ID is invalid", null);
+ }
+
+
Transforms transforms = null;
try {
transforms = ref.getTransforms();
} catch (XMLSecurityException ex) {
throwFault("Signature transforms can not be obtained", ex);
}
- if (!isEnveloping(root) && !detached) {
+ if (enveloped) {
boolean isEnveloped = false;
for (int i = 0; i < transforms.getLength(); i++) {
try {
@@ -224,6 +221,7 @@ public class XmlSigInHandler extends Abs
throwFault("Only enveloped signatures are currently
supported", null);
}
}
+ return signedEl;
}
private Element getSignedElement(Element root, Reference ref) {
Modified:
cxf/trunk/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/saml/SamlCallbackHandler.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/saml/SamlCallbackHandler.java?rev=1166749&r1=1166748&r2=1166749&view=diff
==============================================================================
---
cxf/trunk/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/saml/SamlCallbackHandler.java
(original)
+++
cxf/trunk/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/saml/SamlCallbackHandler.java
Thu Sep 8 15:32:45 2011
@@ -20,12 +20,19 @@
package org.apache.cxf.systest.jaxrs.security.saml;
import java.io.IOException;
+import java.security.cert.X509Certificate;
import java.util.Collections;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+import org.apache.cxf.rs.security.common.CryptoLoader;
+import org.apache.cxf.rs.security.common.SecurityUtils;
+import org.apache.cxf.ws.security.SecurityConstants;
+import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.saml.ext.SAMLCallback;
import org.apache.ws.security.saml.ext.bean.AttributeBean;
import org.apache.ws.security.saml.ext.bean.AttributeStatementBean;
@@ -33,6 +40,7 @@ import org.apache.ws.security.saml.ext.b
import org.apache.ws.security.saml.ext.bean.AuthDecisionStatementBean.Decision;
import org.apache.ws.security.saml.ext.bean.AuthenticationStatementBean;
import org.apache.ws.security.saml.ext.bean.ConditionsBean;
+import org.apache.ws.security.saml.ext.bean.KeyInfoBean;
import org.apache.ws.security.saml.ext.bean.SubjectBean;
import org.apache.ws.security.saml.ext.builder.SAML1Constants;
import org.apache.ws.security.saml.ext.builder.SAML2Constants;
@@ -78,7 +86,25 @@ public class SamlCallbackHandler impleme
new SubjectBean(
subjectName, subjectQualifier, confirmationMethod
);
- // SubjectConfirmationData - not possible to set yet
+ if (SAML2Constants.CONF_HOLDER_KEY.equals(confirmationMethod))
{
+
+ Message m = PhaseInterceptorChain.getCurrentMessage();
+ try {
+ CryptoLoader loader = new CryptoLoader();
+ Crypto crypto = loader.getCrypto(m,
+
SecurityConstants.SIGNATURE_CRYPTO,
+
SecurityConstants.SIGNATURE_PROPERTIES);
+ X509Certificate cert =
+ SecurityUtils.getCertificates(crypto,
+ SecurityUtils.getUserName(m, crypto,
"ws-security.signature.username"))[0];
+
+ KeyInfoBean keyInfo = new KeyInfoBean();
+ keyInfo.setCertificate(cert);
+ subjectBean.setKeyInfo(keyInfo);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
callback.setSubject(subjectBean);
ConditionsBean conditions = new ConditionsBean();