Suponiendo una llamada webservice que contenga una Assertion SAML firmada, como verificamos esa firma? CXF provee "interceptores" para incluir código antes y después de una invocación a webservice.
Primero debemos configurar (con Spring en este caso) la invocación:
<jaxws:client id="clientWS" serviceClass="es.mycompany.MyService" address="http://localhost:8080/app/services/myservice">
<jaxws:dataBinding>
<ref bean="aegisBean" />
</jaxws:dataBinding>
<jaxws:inInterceptors>
<ref bean="clientWS.InInterceptor" />
</jaxws:inInterceptors>
</jaxws:client>
<bean id="clientWS.InInterceptor" class="es.mycompany.MyInterceptor">
<constructor-arg>
<map>
<entry key="action" value="Timestamp Signature"/>
<entry key="passwordCallbackClass" value="es.mycompany.MyPasswordCallback"/>
<entry key="signaturePropFile" value="keystore.properties"/>
</map>
</constructor-arg>
</bean>Luego creamos nuestro fichero de propiedades del keystore ("keystore.properties"):
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.alias=MyAlias org.apache.ws.security.crypto.merlin.keystore.password=MyPassword org.apache.ws.security.crypto.merlin.file=keystore.jks
Y finalmente la clase encargada de dar las contraseñas de acceso a los certificados. Esta clase es claramente mejorable, pero sirve como ejemplo:
public class MyPasswordCallback implements CallbackHandler {
private Map passwords = new HashMap();
public STSPasswordCallback() {
passwords.put("MyAlias", "MyPassword");
}
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
String pass = passwords.get(pc.getIdentifier());
if (pass != null) {
pc.setPassword(pass);
return;
}
}
}
}
Teniendo todo configurado, solo nos queda el código del interceptor, que a su vez se apoya en una clase de utilidades:
public class MyInterceptor extends WSS4JInInterceptor {
private String issuer = "http://www.mycompany.com";
public MyInterceptor() {
super();
}
public MyInterceptor(Map properties) {
super(properties);
}
protected void doReceiverAction(int doAction, RequestData reqData) throws WSSecurityException {
// Get SOAP Header
SOAPMessage message = ((org.apache.cxf.binding.soap.SoapMessage) reqData.getMsgContext()).getContent(javax.xml.soap.SOAPMessage.class);
SOAPHeader soapHeader = message.getSOAPHeader();
// Get Assertion XML
XPathUtils xu = new XPathUtils();
Element xmlAssertion = (Element) xu.getValueNode("//saml2:Assertion", soapHeader);
// Unmarshall
Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(xmlAssertion);
Assertion assertion = (Assertion) unmarshaller.unmarshall(xmlAssertion);
// Get Handler properties
String sigPropFile = getString(WSHandlerConstants.SIG_PROP_FILE, reqData.getMsgContext());
String callback = getString(WSHandlerConstants.PW_CALLBACK_CLASS, reqData.getMsgContext());
// Verify
SAMLUtils.verifySignature(assertion, issuer, sigPropFile, callback);
super.doReceiverAction(doAction, reqData);
}
public void handleMessage(SoapMessage message) throws Fault {
super.handleMessage(message);
}
}
public class SAMLUtils {
public static void verifySignature(Assertion assertion, String issuer, String sigPropFile, String callback) throws WSSecurityException {
X509Credential cred = getX509Credential(sigPropFile, callback);
StaticCredentialResolver credResolver = new StaticCredentialResolver(cred);
KeyInfoCredentialResolver kiResolver = SecurityHelper.buildBasicInlineKeyInfoResolver();
ExplicitKeySignatureTrustEngine trustEngine = new ExplicitKeySignatureTrustEngine(credResolver, kiResolver);
CriteriaSet criteriaSet = new CriteriaSet();
criteriaSet.add(new EntityIDCriteria(issuer));
criteriaSet.add(new UsageCriteria(UsageType.SIGNING));
criteriaSet.add(new MetadataCriteria(IDPSSODescriptor.DEFAULT_ELEMENT_NAME, SAMLConstants.SAML20P_NS));
try {
trustEngine.validate(assertion.getSignature(), criteriaSet);
} catch (SecurityException e) {
throw new WSSecurityException(e.getMessage());
}
}
public static X509Credential getX509Credential(String sigPropFile, String callback) throws WSSecurityException {
PrivateKey privateKey = null;
X509Certificate[] certs = null;
Crypto crypto = crypto = CryptoFactory.getInstance(sigPropFile);
try {
CallbackHandler cbhandler = getPasswordCallBack(callback);
WSPasswordCallback cb = new WSPasswordCallback(crypto.getDefaultX509Alias(), WSPasswordCallback.SIGNATURE);
cbhandler.handle(new Callback[] { cb });
privateKey = crypto.getPrivateKey(crypto.getDefaultX509Alias(), cb.getPassword());
certs = crypto.getCertificates(crypto.getDefaultX509Alias());
} catch (Exception e) {
throw new WSSecurityException(e.getMessage());
}
if (certs.length != 1) {
throw new WSSecurityException("Couldn't get the (" + crypto.getDefaultX509Alias() + ") signature certificate.");
}
X509Credential cred = SecurityHelper.getSimpleCredential(certs[0], privateKey);
return cred;
}
public static CallbackHandler getPasswordCallBack(String callback) throws WSSecurityException {
CallbackHandler cbHandler = null;
try {
Class cbClass = Loader.loadClass(callback);
cbHandler = (CallbackHandler) cbClass.newInstance();
} catch (Exception e) {
throw new WSSecurityException("WSHandler: cannot create instance of password callback: " + callback, e);
}
return cbHandler;
}
} Como ven, todo el trabajo lo hace la línea trustEngine.validate(assertion.getSignature(), criteriaSet);, solo hay que llegar hasta ella con los datos adecuados.

No hay comentarios:
Publicar un comentario