package org.jboss.seam.security.external.saml; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.GeneralSecurityException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import javax.xml.crypto.dsig.SignatureMethod; import org.jboss.seam.security.external.Base64; import org.jboss.seam.security.external.InvalidRequestException; /** * @author Marcel Kolsteren */ public class SamlSignatureUtilForRedirectBinding { public void sign(SamlRedirectMessage urlEncodedRedirectMessage, PrivateKey signingKey) throws IOException, GeneralSecurityException { urlEncodedRedirectMessage.setSignatureAlgorithm(getXMLSignatureAlgorithmURI(signingKey.getAlgorithm())); byte[] signature = computeSignature(urlEncodedRedirectMessage.createQueryString(), signingKey); String base64encodedSignature = Base64.encodeBytes(signature, Base64.DONT_BREAK_LINES); String urlEncodedSignature = URLEncoder.encode(base64encodedSignature, "UTF-8"); urlEncodedRedirectMessage.setSignature(urlEncodedSignature); } private byte[] computeSignature(String stringToBeSigned, PrivateKey signingKey) throws GeneralSecurityException { String algo = signingKey.getAlgorithm(); Signature sig = getSignature(algo); sig.initSign(signingKey); sig.update(stringToBeSigned.getBytes()); return sig.sign(); } public void validateSignature(SamlRedirectMessage urlEncodedRedirectMessage, PublicKey publicKey) throws InvalidRequestException { if (urlEncodedRedirectMessage.getSignature() == null) { throw new InvalidRequestException("Signature parameter is not present."); } String urlDecodedSignature; try { urlDecodedSignature = URLDecoder.decode(urlEncodedRedirectMessage.getSignature(), "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } byte[] base64DecodedSignature = Base64.decode(urlDecodedSignature); // Reconstruct the string that has been signed by the other party SamlRedirectMessage signedRedirectMessage = new SamlRedirectMessage(); signedRedirectMessage.setRequestOrResponse(urlEncodedRedirectMessage.getRequestOrResponse()); signedRedirectMessage.setSamlMessage(urlEncodedRedirectMessage.getSamlMessage()); signedRedirectMessage.setRelayState(urlEncodedRedirectMessage.getRelayState()); signedRedirectMessage.setSignatureAlgorithm(urlEncodedRedirectMessage.getSignatureAlgorithm()); signedRedirectMessage.setUrlEncoded(true); String signedString = signedRedirectMessage.createQueryString(); boolean isValid; try { isValid = validate(signedString.getBytes("UTF-8"), base64DecodedSignature, publicKey); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } if (!isValid) { throw new InvalidRequestException("Invalid signature."); } } private boolean validate(byte[] signedContent, byte[] signatureValue, PublicKey validatingKey) throws GeneralSecurityException { String algo = validatingKey.getAlgorithm(); Signature sig = getSignature(algo); sig.initVerify(validatingKey); sig.update(signedContent); return sig.verify(signatureValue); } private Signature getSignature(String algo) throws GeneralSecurityException { Signature sig = null; if ("DSA".equalsIgnoreCase(algo)) { sig = Signature.getInstance(SamlConstants.DSA_SIGNATURE_ALGORITHM); } else if ("RSA".equalsIgnoreCase(algo)) { sig = Signature.getInstance(SamlConstants.RSA_SIGNATURE_ALGORITHM); } else throw new RuntimeException("Unknown signature algorithm:" + algo); return sig; } private String getXMLSignatureAlgorithmURI(String algo) { String xmlSignatureAlgo = null; if ("DSA".equalsIgnoreCase(algo)) { xmlSignatureAlgo = SignatureMethod.DSA_SHA1; } else if ("RSA".equalsIgnoreCase(algo)) { xmlSignatureAlgo = SignatureMethod.RSA_SHA1; } return xmlSignatureAlgo; } }