Modified: axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAMLUtils.java URL: http://svn.apache.org/viewvc/axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAMLUtils.java?rev=1295060&r1=1295059&r2=1295060&view=diff ============================================================================== --- axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAMLUtils.java (original) +++ axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAMLUtils.java Wed Feb 29 10:45:37 2012 @@ -1,30 +1,778 @@ package org.apache.rahas.impl.util; -import org.apache.rahas.impl.SAMLTokenIssuerConfig; -import org.apache.ws.security.components.crypto.Crypto; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.rahas.RahasConstants; +import org.apache.rahas.RahasData; +import org.apache.rahas.TrustException; +import org.apache.rahas.impl.TokenIssuerUtil; +import org.apache.ws.security.WSConstants; import org.apache.ws.security.WSSecurityException; +import org.apache.ws.security.components.crypto.Crypto; +import org.apache.ws.security.message.WSSecEncryptedKey; +import org.apache.ws.security.util.Base64; +import org.apache.xml.security.signature.XMLSignature; +import org.apache.xml.security.utils.EncryptionConstants; +import org.joda.time.DateTime; +import org.opensaml.Configuration; +import org.opensaml.saml1.core.*; +import org.opensaml.ws.wssecurity.KeyIdentifier; +import org.opensaml.ws.wssecurity.SecurityTokenReference; +import org.opensaml.ws.wssecurity.WSSecurityConstants; +import org.opensaml.xml.XMLObject; +import org.opensaml.xml.XMLObjectBuilder; +import org.opensaml.xml.encryption.CipherData; +import org.opensaml.xml.encryption.CipherValue; +import org.opensaml.xml.encryption.EncryptedKey; +import org.opensaml.xml.encryption.EncryptionMethod; +import org.opensaml.xml.io.MarshallingException; +import org.opensaml.xml.schema.XSString; +import org.opensaml.xml.schema.impl.XSStringBuilder; +import org.opensaml.xml.security.SecurityHelper; +import org.opensaml.xml.security.credential.Credential; +import org.opensaml.xml.signature.*; +import org.opensaml.xml.signature.KeyInfo; +import org.opensaml.xml.signature.X509Data; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import javax.xml.namespace.QName; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; -import java.util.Collection; -import java.util.ArrayList; +import java.util.*; +/** + * Utility class for SAML 1 assertions. Responsible for manipulating all SAML1 specific objects + * like Assertion, ConfirmationMethod etc ... + */ public class SAMLUtils { + private static final Log log = LogFactory.getLog(SAMLUtils.class); - public static Collection<X509Certificate> getCertChainCollection(X509Certificate[] issuerCerts){ - - ArrayList<X509Certificate> certCollection = new ArrayList<X509Certificate>(); + public static Collection<X509Certificate> getCertChainCollection(X509Certificate[] issuerCerts) { + ArrayList<X509Certificate> certCollection = new ArrayList<X509Certificate>(); if (issuerCerts == null) { return certCollection; } else { - for (X509Certificate cert : issuerCerts) { - certCollection.add(cert); - } + Collections.addAll(certCollection, issuerCerts); } return certCollection; + } + + /** + * Builds the requested XMLObject. + * + * @param objectQName name of the XMLObject + * @return the build XMLObject + * @throws org.apache.rahas.TrustException If unable to find the appropriate builder. + */ + public static XMLObject buildXMLObject(QName objectQName) throws TrustException { + XMLObjectBuilder builder = Configuration.getBuilderFactory().getBuilder(objectQName); + if (builder == null) { + log.debug("Unable to find OpenSAML builder for object " + objectQName); + throw new TrustException("builderNotFound",new Object[]{objectQName}); + } + return builder.buildObject(objectQName.getNamespaceURI(), objectQName.getLocalPart(), objectQName.getPrefix()); + } + + /** + * Builds an assertion from an XML element. + * @param assertionElement The XML element. + * @return An Assertion object. + */ + public static Assertion buildAssertion(Element assertionElement) { + + return (Assertion) Configuration.getBuilderFactory(). + getBuilder(Assertion.DEFAULT_ELEMENT_NAME).buildObject(assertionElement); + + } + +/** + * Signs the SAML assertion. The steps to sign SAML assertion is as follows, + * <ol> + * <li>Get certificate for issuer alias</li> + * <li>Extract private key</li> + * <li>Create {@link org.opensaml.xml.security.credential.Credential} object</li> + * <li>Create {@link org.opensaml.xml.signature.Signature} object</li> + * <li>Set Signature object in Assertion</li> + * <li>Prepare signing environment - SecurityHelper.prepareSignatureParams</li> + * <li>Perform signing action - Signer.signObject</li> + * </ol> + * @param assertion The assertion to be signed. + * @param crypto Certificate and private key data are stored in Crypto object + * @param issuerKeyAlias Key alias + * @param issuerKeyPassword Key password + * @throws TrustException If an error occurred while signing the assertion. + */ + public static void signAssertion(Assertion assertion, Crypto crypto, + String issuerKeyAlias, String issuerKeyPassword) + throws TrustException { + + X509Certificate[] issuerCerts; + try { + issuerCerts = crypto + .getCertificates(issuerKeyAlias); + } catch (WSSecurityException e) { + log.debug("Unable to get issuer certificate for issuer alias " + issuerKeyAlias, e); + throw new TrustException("issuerCertificateNotFound", new Object[]{issuerKeyAlias}, e); + } + + if (issuerCerts == null || issuerCerts.length == 0) { + log.debug("Unable to get issuer certificate for issuer alias " + issuerKeyAlias); + throw new TrustException("issuerCertificateNotFound", new Object[]{issuerKeyAlias}); + } + + String signatureAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_RSA; + + PublicKey issuerPublicKey = issuerCerts[0].getPublicKey(); + + String publicKeyAlgorithm = issuerPublicKey.getAlgorithm(); + if (publicKeyAlgorithm.equalsIgnoreCase("DSA")) { + signatureAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_DSA; + } + + PrivateKey issuerPrivateKey; + try { + issuerPrivateKey = crypto.getPrivateKey( + issuerKeyAlias, issuerKeyPassword); + } catch (Exception e) { + log.debug("Unable to get issuer private key for issuer alias " + issuerKeyAlias); + throw new TrustException("issuerPrivateKeyNotFound", new Object[]{issuerKeyAlias}); + } + + Credential signingCredential = SecurityHelper.getSimpleCredential(issuerPublicKey, issuerPrivateKey); + + Signature signature = (Signature) SAMLUtils.buildXMLObject(Signature.DEFAULT_ELEMENT_NAME); + signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); + signature.setSigningCredential(signingCredential); + signature.setSignatureAlgorithm(signatureAlgorithm); + + X509Data x509Data = createX509Data(issuerCerts[0]); + KeyInfo keyInfo = createKeyInfo(x509Data); + + signature.setKeyInfo(keyInfo); + assertion.setSignature(signature); + + try { + + Document document = CommonUtil.getOMDOMDocument(); + + Configuration.getMarshallerFactory().getMarshaller(assertion).marshall(assertion, document); + } catch (MarshallingException e) { + log.debug("Error while marshalling assertion ", e); + throw new TrustException("errorMarshallingAssertion", e); + } + + try { + Signer.signObject(signature); + } catch (SignatureException e) { + log.debug("Error signing SAML Assertion. An error occurred while signing SAML Assertion with alias " + + issuerKeyAlias, e); + throw new TrustException("errorSigningAssertion", e); + } + } + + /** + * Get subject confirmation method of the given SAML 1.1 Assertion. + * This is used in rampart-core. + * @param assertion SAML 1.1 Assertion + * @return subject confirmation method + */ + public static String getSAML11SubjectConfirmationMethod(Assertion assertion) { + String subjectConfirmationMethod = RahasConstants.SAML11_SUBJECT_CONFIRMATION_HOK; + // iterate the statements and get the subject confirmation method. + List<Statement> statements = assertion.getStatements(); + + // TODO check whether there is an efficient method of doing this + if (!statements.isEmpty()) { + SubjectStatement subjectStatement = (SubjectStatement) statements.get(0); + Subject subject = subjectStatement.getSubject(); + + if (subject != null) { + SubjectConfirmation subjectConfirmation = subject.getSubjectConfirmation(); + + if (subjectConfirmation != null) { + List<ConfirmationMethod> confirmationMethods = subjectConfirmation.getConfirmationMethods(); + + if (!confirmationMethods.isEmpty()) { + subjectConfirmationMethod = confirmationMethods.get(0).getConfirmationMethod(); + } + } + } + } + + + return subjectConfirmationMethod; + } + + /** + * Create named identifier. + * @param principalName Name of the subject. + * @param format Format of the subject, whether it is an email, uid etc ... + * @return The NamedIdentifier object. + * @throws org.apache.rahas.TrustException If unable to find the builder. + */ + public static NameIdentifier createNamedIdentifier(String principalName, String format) throws TrustException{ + + NameIdentifier nameId = (NameIdentifier)SAMLUtils.buildXMLObject(NameIdentifier.DEFAULT_ELEMENT_NAME); + nameId.setNameIdentifier(principalName); + nameId.setFormat(format); + + return nameId; + } + + /** + * Creates the subject confirmation method. + * Relevant XML element would look like as follows, + * <saml:ConfirmationMethod> + * urn:oasis:names:tc:SAML:1.0:cm:holder-of-key + * </saml:ConfirmationMethod> + * @param confirmationMethod Name of the actual confirmation method. Could be + * holder-of-key - "urn:oasis:names:tc:SAML:1.0:cm:holder-of-key" + * sender-vouches - "urn:oasis:names:tc:SAML:1.0:cm:sender-vouches" + * bearer - TODO + * @return Returns the opensaml representation of the ConfirmationMethod. + * @throws TrustException If unable to find appropriate XMLObject builder for confirmation QName. + */ + public static ConfirmationMethod createSubjectConfirmationMethod(final String confirmationMethod) + throws TrustException { + ConfirmationMethod confirmationMethodObject + = (ConfirmationMethod)SAMLUtils.buildXMLObject(ConfirmationMethod.DEFAULT_ELEMENT_NAME); + confirmationMethodObject.setConfirmationMethod(confirmationMethod); + + return confirmationMethodObject; + } + + /** + * Creates opensaml SubjectConfirmation representation. The relevant XML would looks as follows, + * <saml:SubjectConfirmation> + * <saml:ConfirmationMethod> + * urn:oasis:names:tc:SAML:1.0:cm:sender-vouches + * </saml:ConfirmationMethod> + * </saml:SubjectConfirmation> + * @param confirmationMethod The subject confirmation method. Bearer, Sender-Vouches or Holder-Of-Key. + * @param keyInfoContent The KeyInfo content. According to SPEC (SAML 1.1) this could be null. + * @return OpenSAML representation of SubjectConfirmation. + * @throws TrustException If unable to find any of the XML builders. + */ + public static SubjectConfirmation createSubjectConfirmation(final String confirmationMethod, + KeyInfo keyInfoContent) throws TrustException { + + SubjectConfirmation subjectConfirmation + = (SubjectConfirmation)SAMLUtils.buildXMLObject(SubjectConfirmation.DEFAULT_ELEMENT_NAME); + + ConfirmationMethod method = SAMLUtils.createSubjectConfirmationMethod(confirmationMethod); + subjectConfirmation.getConfirmationMethods().add(method); + + if (keyInfoContent != null) { + subjectConfirmation.setKeyInfo(keyInfoContent); + } + + return subjectConfirmation; } + + /** + * Creates an opensaml Subject representation. The relevant XML would looks as follows, + * <saml:Subject> + * <saml:NameIdentifier + * NameQualifier="www.example.com" + * Format="..."> + * uid=joe,ou=people,ou=saml-demo,o=baltimore.com + * </saml:NameIdentifier> + * <saml:SubjectConfirmation> + * <saml:ConfirmationMethod> + * urn:oasis:names:tc:SAML:1.0:cm:holder-of-key + * </saml:ConfirmationMethod> + * <ds:KeyInfo> + * <ds:KeyValue>...</ds:KeyValue> + * </ds:KeyInfo> + * </saml:SubjectConfirmation> + * </saml:Subject> + * @param nameIdentifier Represent the "NameIdentifier" of XML element above. + * @param confirmationMethod Represent the bearer, HOK or Sender-Vouches. + * @param keyInfoContent Key info information. This could be null. + * @return OpenSAML representation of the Subject. + * @throws TrustException If a relevant XML builder is unable to find. + */ + public static Subject createSubject(final NameIdentifier nameIdentifier, final String confirmationMethod, + KeyInfo keyInfoContent) throws TrustException { + + Subject subject = (Subject)SAMLUtils.buildXMLObject(Subject.DEFAULT_ELEMENT_NAME); + subject.setNameIdentifier(nameIdentifier); + + SubjectConfirmation subjectConfirmation + = SAMLUtils.createSubjectConfirmation(confirmationMethod,keyInfoContent); + subject.setSubjectConfirmation(subjectConfirmation); + + return subject; + } + + /** + * Creates an AuthenticationStatement. The relevant XML element looks as follows, + * <AuthenticationStatement + * AuthenticationInstant="2003-04-17T00:46:00Z" + * AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:password"> + * <Subject> + * <NameIdentifier + * Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"> + * sc...@example.org</NameIdentifier> + * <SubjectConfirmation> + * <ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</ConfirmationMethod> + * </SubjectConfirmation> + * </Subject> + * <SubjectLocality IPAddress="127.0.0.1"/> + * </AuthenticationStatement> + * @param subject OpenSAML Subject implementation. + * @param authenticationMethod How subject is authenticated ? i.e. by using a password, kerberos, certificate + * etc ... The method is defined as a URL in SAML specification. + * @param authenticationInstant Time which authentication took place. + * @return opensaml AuthenticationStatement object. + * @throws org.apache.rahas.TrustException If unable to find the builder. + */ + public static AuthenticationStatement createAuthenticationStatement(Subject subject, String authenticationMethod, + DateTime authenticationInstant) + throws TrustException { + + AuthenticationStatement authenticationStatement + = (AuthenticationStatement)SAMLUtils.buildXMLObject(AuthenticationStatement.DEFAULT_ELEMENT_NAME); + + authenticationStatement.setSubject(subject); + authenticationStatement.setAuthenticationMethod(authenticationMethod); + authenticationStatement.setAuthenticationInstant(authenticationInstant); + + return authenticationStatement; + } + + /**Creates an attribute statement. Sample attribute statement would look like follows, + * <saml:AttributeStatement> + * <saml:Subject> + * <saml:NameIdentifier + * NameQualifier="www.example.com" + * Format="..."> + * uid=joe,ou=people,ou=saml-demo,o=baltimore.com + * </saml:NameIdentifier> + * <saml:SubjectConfirmation> + * <saml:ConfirmationMethod> + * urn:oasis:names:tc:SAML:1.0:cm:holder-of-key + * </saml:ConfirmationMethod> + * <ds:KeyInfo> + * <ds:KeyValue>...</ds:KeyValue> + * </ds:KeyInfo> + * </saml:SubjectConfirmation> + * </saml:Subject> + * <saml:Attribute + * AttributeName="MemberLevel" + * AttributeNamespace="http://www.oasis.open.org/Catalyst2002/attributes"> + * <saml:AttributeValue>gold</saml:AttributeValue> + * </saml:Attribute> + * <saml:Attribute + * AttributeName="E-mail" + * AttributeNamespace="http://www.oasis.open.org/Catalyst2002/attributes"> + * <saml:AttributeValue>j...@yahoo.com</saml:AttributeValue> + * </saml:Attribute> + * </saml:AttributeStatement> + * + * @param subject The OpenSAML representation of the Subject. + * @param attributeList List of attribute values to include within the message. + * @return OpenSAML representation of AttributeStatement. + * @throws org.apache.rahas.TrustException If unable to find the appropriate builder. + */ + public static AttributeStatement createAttributeStatement(Subject subject, List<Attribute> attributeList) + throws TrustException { + + AttributeStatement attributeStatement + = (AttributeStatement)SAMLUtils.buildXMLObject(AttributeStatement.DEFAULT_ELEMENT_NAME); + + attributeStatement.setSubject(subject); + attributeStatement.getAttributes().addAll(attributeList); + + return attributeStatement; + } + + /** + * Creates Conditions object. Analogous XML element is as follows, + * <saml:Conditions> + * NotBefore="2002-06-19T16:53:33.173Z" + * NotOnOrAfter="2002-06-19T17:08:33.173Z"/> + * @param notBefore The validity of the Assertion starts from this value. + * @param notOnOrAfter The validity ends from this value. + * @return OpenSAML Conditions object. + * @throws org.apache.rahas.TrustException If unable to find appropriate builder. + */ + public static Conditions createConditions(DateTime notBefore, DateTime notOnOrAfter) throws TrustException { + + Conditions conditions = (Conditions)SAMLUtils.buildXMLObject(Conditions.DEFAULT_ELEMENT_NAME); + + conditions.setNotBefore(notBefore); + conditions.setNotOnOrAfter(notOnOrAfter); + + return conditions; + } + + /** + * This method creates the final SAML assertion. The final SAML assertion would looks like as follows, + * <saml:Assertion AssertionID="_a75adf55-01d7-40cc-929f-dbd8372ebdfc" + * IssueInstant="2003-04-17T00:46:02Z" + * Issuer=âwww.opensaml.orgâ + * MajorVersion="1" + * MinorVersion="1" + * xmlns="urn:oasis:names:tc:SAML:1.0:assertion"> + * <saml:Conditions> + * NotBefore="2002-06-19T16:53:33.173Z" + * NotOnOrAfter="2002-06-19T17:08:33.173Z"/> + * <saml:AttributeStatement> + * <saml:Subject> + * <saml:NameIdentifier + * NameQualifier="www.example.com" + * Format="..."> + * uid=joe,ou=people,ou=saml-demo,o=baltimore.com + * </saml:NameIdentifier> + * <saml:SubjectConfirmation> + * <saml:ConfirmationMethod> + * urn:oasis:names:tc:SAML:1.0:cm:holder-of-key + * </saml:ConfirmationMethod> + * <ds:KeyInfo> + * <ds:KeyValue>...</ds:KeyValue> + * </ds:KeyInfo> + * </saml:SubjectConfirmation> + * </saml:Subject> + * <saml:Attribute + * AttributeName="MemberLevel" + * AttributeNamespace="http://www.oasis.open.org/Catalyst2002/attributes"> + * <saml:AttributeValue>gold</saml:AttributeValue> + * </saml:Attribute> + * <saml:Attribute + * AttributeName="E-mail" AttributeNamespace="http://www.oasis.open.org/Catalyst2002/attributes"> + * <saml:AttributeValue>j...@yahoo.com</saml:AttributeValue> + * </saml:Attribute> + * </saml:AttributeStatement> + * <ds:Signature>...</ds:Signature> + * </saml:Assertion> + * @param issuerName Represents the "Issuer" in Assertion. + * @param notBefore The Condition's NotBefore value + * @param notOnOrAfter The Condition's NotOnOrAfter value + * @param statements Other statements. + * @return An opensaml Assertion object. + * @throws org.apache.rahas.TrustException If unable to find the appropriate builder. + */ + public static Assertion createAssertion(String issuerName, DateTime notBefore, DateTime notOnOrAfter, + List<Statement> statements) throws TrustException { + + Assertion assertion = (Assertion)SAMLUtils.buildXMLObject(Assertion.DEFAULT_ELEMENT_NAME); + + assertion.setIssuer(issuerName); + assertion.setConditions(SAMLUtils.createConditions(notBefore, notOnOrAfter)); + assertion.getStatements().addAll(statements); + + return assertion; + } + + /** + * Creates a SAML attribute similar to following, + * <saml:Attribute + * AttributeName="MemberLevel" + * AttributeNamespace="http://www.oasis.open.org/Catalyst2002/attributes"> + * <saml:AttributeValue>gold</saml:AttributeValue> + * </saml:Attribute> + * @param name attribute name + * @param namespace attribute namespace. + * @param value attribute value. + * @return OpenSAML representation of the attribute. + * @throws org.apache.rahas.TrustException If unable to find the appropriate builder. + */ + public static Attribute createAttribute(String name, String namespace, String value) throws TrustException { + + Attribute attribute = (Attribute)SAMLUtils.buildXMLObject(Attribute.DEFAULT_ELEMENT_NAME); + + attribute.setAttributeName(name); + attribute.setAttributeNamespace(namespace); + + XSStringBuilder attributeValueBuilder = (XSStringBuilder)Configuration.getBuilderFactory(). + getBuilder(XSString.TYPE_NAME); + + XSString stringValue + = attributeValueBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); + stringValue.setValue(value); + + attribute.getAttributeValues().add(stringValue); + + return attribute; + + } + + /** + * Creates a KeyInfo object + * @return OpenSAML KeyInfo representation. + * @throws TrustException If an error occurred while creating KeyInfo. + */ + public static KeyInfo createKeyInfo() throws TrustException { + + return (KeyInfo)SAMLUtils.buildXMLObject(KeyInfo.DEFAULT_ELEMENT_NAME); + } + + /** + * Creates a KeyInfo element given EncryptedKey. The relevant XML would looks as follows, + * <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> + * <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" + * .... + * </xenc:EncryptedKey> + * </ds:KeyInfo> + * @param encryptedKey The OpemSAML representation of encrypted key. + * @return The appropriate opensaml representation of the KeyInfo. + * @throws org.apache.rahas.TrustException If unable to find the builder. + */ + public static KeyInfo createKeyInfo(EncryptedKey encryptedKey) throws TrustException { + + KeyInfo keyInfo = createKeyInfo(); + keyInfo.getEncryptedKeys().add(encryptedKey); + + return keyInfo; + } + + /** + * Creates a KeyInfo element given EncryptedKey. The relevant XML would looks as follows, + * <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> + * <X509Data xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" + * .... + * </X509Data> + * </ds:KeyInfo> + * @param x509Data The OpemSAML representation X509Data + * @return The appropriate opensaml representation of the KeyInfo. + * @throws org.apache.rahas.TrustException If unable to find the builder. + */ + public static KeyInfo createKeyInfo(X509Data x509Data) throws TrustException { + + KeyInfo keyInfo = createKeyInfo(); + keyInfo.getX509Datas().add(x509Data); + + return keyInfo; + } + + /** + * Creates the certificate based KeyInfo object. + * @param certificate The public key certificate used to create the KeyInfo object. + * @return OpenSAML representation of KeyInfo object. + * @throws TrustException If an error occurred while creating the KeyInfo + */ + public static KeyInfo getCertificateBasedKeyInfo(X509Certificate certificate) throws TrustException { + X509Data x509Data = SAMLUtils.createX509Data(certificate); + return SAMLUtils.createKeyInfo(x509Data); + } + + + /** + * This method creates KeyInfo element of an assertion. This is a facade, in which it calls + * to other helper methods to create KeyInfo. The TokenIssuer will call this method to + * create the KeyInfo. + * @param doc An Axiom based DOM Document. + * @param data The ephemeral key which we use here need in encrypting the message also. Therefore + * we need to save the ephemeral key in RahasData passed here. + * @param serviceCert Public key used to encrypt the assertion is extracted from this certificate. + * @param keySize Size of the key to be used + * @param crypto The relevant private key + * @param keyComputation Key computation mechanism. + * @return OpenSAML KeyInfo representation. + * @throws WSSecurityException We use WSS4J to generate encrypted key. This exception will trigger if an + * error occurs while generating the encrypted key. + * @throws TrustException If an error occurred while creating KeyInfo object. + */ + public static KeyInfo getSymmetricKeyBasedKeyInfo(Document doc, + RahasData data, + X509Certificate serviceCert, + int keySize, + Crypto crypto, + int keyComputation) throws WSSecurityException, TrustException { + + byte[] ephemeralKey = TokenIssuerUtil.getSharedSecret( + data, keyComputation, keySize); + + WSSecEncryptedKey encryptedKey = getSymmetricKeyBasedKeyInfoContent(doc, ephemeralKey, serviceCert, + keySize, crypto); + + // Extract the base64 encoded secret value + byte[] tempKey = new byte[keySize / 8]; + System.arraycopy(encryptedKey.getEphemeralKey(), 0, tempKey, + 0, keySize / 8); + + + data.setEphmeralKey(tempKey); + + EncryptedKey samlEncryptedKey = SAMLUtils.createEncryptedKey(serviceCert, encryptedKey); + return SAMLUtils.createKeyInfo(samlEncryptedKey); + } + + + + static WSSecEncryptedKey getSymmetricKeyBasedKeyInfoContent(Document doc, + byte[] ephemeralKey, + X509Certificate serviceCert, + int keySize, + Crypto crypto) throws WSSecurityException, + TrustException { + // Create the encrypted key + WSSecEncryptedKey encryptedKeyBuilder = new WSSecEncryptedKey(); + + // Use thumbprint id + encryptedKeyBuilder + .setKeyIdentifierType(WSConstants.THUMBPRINT_IDENTIFIER); + + // SEt the encryption cert + encryptedKeyBuilder.setUseThisCert(serviceCert); + + // set keysize + encryptedKeyBuilder.setKeySize(keySize); + + encryptedKeyBuilder.setEphemeralKey(ephemeralKey); + + // Set key encryption algo + encryptedKeyBuilder + .setKeyEncAlgo(EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSA15); + + // Build + encryptedKeyBuilder.prepare(doc, crypto); + + return encryptedKeyBuilder; + } + + /** + * Creates the X509 data element in a SAML issuer token. Should create an element similar to following, + * <X509Data xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" + * xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> + * <X509Certificate> + * MIICNTCCAZ6gAwIB... + * </X509Certificate> + * </X509Data> + * @param clientCert Client certificate to be used when generating X509 data + * @return SAML X509Data representation. + * @throws TrustException If an error occurred while creating X509Data and X509Certificate. + */ + static X509Data createX509Data(X509Certificate clientCert) throws TrustException { + + byte[] clientCertBytes; + try { + clientCertBytes = clientCert.getEncoded(); + } catch (CertificateEncodingException e) { + log.error("An error occurred while encoding certificate.", e); + throw new TrustException("An error occurred while encoding certificate.", e); + } + String base64Cert = Base64.encode(clientCertBytes); + + org.opensaml.xml.signature.X509Certificate x509Certificate + = (org.opensaml.xml.signature.X509Certificate)SAMLUtils.buildXMLObject + (org.opensaml.xml.signature.X509Certificate.DEFAULT_ELEMENT_NAME); + + x509Certificate.setValue(base64Cert); + + X509Data x509Data = (X509Data)SAMLUtils.buildXMLObject(X509Data.DEFAULT_ELEMENT_NAME); + x509Data.getX509Certificates().add(x509Certificate); + + return x509Data; + } + + /** + * This method will created the "EncryptedKey" of a SAML assertion. + * An encrypted key would look like as follows, + * <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" + * xmlns:ds="http://www.w3.org/2000/09/xmldsig#" + * Id="EncKeyId-E5CEA44F9C25F55C4913269595550814"> + * <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/> + * <ds:KeyInfo> + * <wsse:SecurityTokenReference + * xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> + * <wsse:KeyIdentifier + * EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0 + * #Base64Binary" + * ValueType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#ThumbprintSHA1"> + * a/jhNus21KVuoFx65LmkW2O/l10= + * </wsse:KeyIdentifier> + * </wsse:SecurityTokenReference> + * </ds:KeyInfo> + * <xenc:CipherData> + * <xenc:CipherValue> + * dnP0MBHiMLlSmnjJhGFs/I8/z... + * </xenc:CipherValue> + * </xenc:CipherData> + * </xenc:EncryptedKey> + * @param certificate Certificate which holds the public key to encrypt ephemeral key. + * @param wsSecEncryptedKey WS Security object which contains encrypted ephemeral key. + * TODO Passing WSSecEncryptedKey is an overhead. We should be able to create encrypted ephemeral + * key without WSS4J + * @return OpenSAML EncryptedKey representation. + * @throws TrustException If an error occurred while creating EncryptedKey. + */ + static EncryptedKey createEncryptedKey(X509Certificate certificate, WSSecEncryptedKey wsSecEncryptedKey) + throws TrustException { + + SecurityTokenReference securityTokenReference + = (SecurityTokenReference)SAMLUtils.buildXMLObject(SecurityTokenReference.ELEMENT_NAME); + + KeyIdentifier keyIdentifier = (KeyIdentifier)SAMLUtils.buildXMLObject(KeyIdentifier.ELEMENT_NAME); + + // Encoding type set to http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0 + // #Base64Binary + keyIdentifier.setEncodingType(KeyIdentifier.ENCODING_TYPE_BASE64_BINARY); + keyIdentifier.setValueType(WSSecurityConstants.THUMB_PRINT_SHA1); + keyIdentifier.setValue(getThumbprintSha1(certificate)); + + securityTokenReference.getUnknownXMLObjects().add(keyIdentifier); + + KeyInfo keyInfo = SAMLUtils.createKeyInfo(); + keyInfo.getXMLObjects().add(securityTokenReference); + + CipherValue cipherValue = (CipherValue)buildXMLObject(CipherValue.DEFAULT_ELEMENT_NAME); + cipherValue.setValue(Base64.encode(wsSecEncryptedKey.getEncryptedEphemeralKey())); + + CipherData cipherData = (CipherData)buildXMLObject(CipherData.DEFAULT_ELEMENT_NAME); + cipherData.setCipherValue(cipherValue); + + EncryptionMethod encryptionMethod = (EncryptionMethod)buildXMLObject(EncryptionMethod.DEFAULT_ELEMENT_NAME); + encryptionMethod.setAlgorithm(EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSA15); + + EncryptedKey encryptedKey = (EncryptedKey)SAMLUtils.buildXMLObject(EncryptedKey.DEFAULT_ELEMENT_NAME); + + encryptedKey.setID(wsSecEncryptedKey.getId()); + encryptedKey.setEncryptionMethod(encryptionMethod); + encryptedKey.setCipherData(cipherData); + encryptedKey.setKeyInfo(keyInfo); + + return encryptedKey; + + } + + private static String getThumbprintSha1(X509Certificate cert) throws TrustException { + + MessageDigest sha; + try { + sha = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e1) { + throw new TrustException("sha1NotFound", e1); + } + sha.reset(); + try { + sha.update(cert.getEncoded()); + } catch (CertificateEncodingException e1) { + throw new TrustException("certificateEncodingError", e1); + } + byte[] data = sha.digest(); + + return Base64.encode(data); + } + + /** + * Converts java.util.Date to opensaml DateTime object. + * @param date Java util date + * @return opensaml specific DateTime object. + */ + public static DateTime convertToDateTime(Date date) { + return new DateTime(date); + } + }
Added: axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/test/java/org/apache/rahas/impl/util/SAMLUtilsTest.java URL: http://svn.apache.org/viewvc/axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/test/java/org/apache/rahas/impl/util/SAMLUtilsTest.java?rev=1295060&view=auto ============================================================================== --- axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/test/java/org/apache/rahas/impl/util/SAMLUtilsTest.java (added) +++ axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/test/java/org/apache/rahas/impl/util/SAMLUtilsTest.java Wed Feb 29 10:45:37 2012 @@ -0,0 +1,375 @@ +/* + * Copyright The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rahas.impl.util; + +import junit.framework.Assert; +import junit.framework.TestCase; +import org.apache.axiom.om.OMElement; +import org.apache.axiom.soap.SOAPEnvelope; +import org.apache.axis2.AxisFault; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.rahas.Rahas; +import org.apache.rahas.TrustException; +import org.apache.rahas.TrustUtil; +import org.apache.rahas.impl.AbstractIssuerConfig; +import org.apache.ws.security.components.crypto.Crypto; +import org.apache.ws.security.components.crypto.CryptoFactory; +import org.apache.ws.security.message.WSSecEncryptedKey; +import org.apache.ws.security.util.Base64; +import org.joda.time.DateTime; +import org.opensaml.Configuration; +import org.opensaml.saml1.core.*; +import org.opensaml.xml.io.MarshallerFactory; +import org.opensaml.xml.io.MarshallingException; +import org.opensaml.xml.signature.X509Data; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.*; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +/** + * A test class for SAML 1 Token Issuer. + */ +public class SAMLUtilsTest extends TestCase{ + + protected static MarshallerFactory marshallerFactory; + + private static final boolean PRINT = false; + + private static final Log log = LogFactory.getLog(SAMLUtilsTest.class); + + public void setUp() throws AxisFault { + Rahas rahas = new Rahas(); + rahas.init(null, null); + + marshallerFactory = Configuration.getMarshallerFactory(); + } + + public void testBuildXMLObjectNegative() { + try { + SAMLUtils.buildXMLObject(new QName("http://x.com", "y")); + Assert.fail("This should throw an exception"); + } catch (TrustException e) { + } + } + + public void testCreateSubjectConfirmationMethod() + throws TrustException, MarshallingException, TransformerException { + ConfirmationMethod confirmationMethod + = SAMLUtils.createSubjectConfirmationMethod("urn:oasis:names:tc:SAML:1.0:cm:holder-of-key"); + + marshallerFactory.getMarshaller(confirmationMethod).marshall(confirmationMethod); + Assert.assertNotNull(confirmationMethod.getDOM()); + + try { + printElement(confirmationMethod.getDOM()); + } catch (TransformerException e) { + log.error("Error printing SAML element", e); + throw e; + } + } + + public void testCreateKeyInfo() { + //TODO + } + + public void testConditions() throws TrustException, MarshallingException, TransformerException { + Conditions conditions = SAMLUtils.createConditions(new DateTime(), new DateTime(2050, 1, 1, 0, 0, 0, 0)); + + marshallerFactory.getMarshaller(conditions).marshall(conditions); + Assert.assertNotNull(conditions.getDOM()); + + try { + printElement(conditions.getDOM()); + } catch (TransformerException e) { + log.error("Error printing SAML element", e); + throw e; + } + } + + public void testCreateSubject() { + //TODO + } + + public void testCreateAuthenticationStatement(){ + //TODO + } + + public void testSignAssertion() throws Exception { + + Assertion assertion = getAssertion(); + + SAMLUtils.signAssertion(assertion,getCrypto(), "apache", "password"); + + //marshallerFactory.getMarshaller(assertion).marshall(assertion); + + Assert.assertNotNull(assertion.getDOM()); + printElement(assertion.getDOM()); + + boolean signatureFound = false; + int numberOfNodes = assertion.getDOM().getChildNodes().getLength(); + for(int i=0; i < numberOfNodes; ++i) { + + OMElement n = (OMElement)assertion.getDOM().getChildNodes().item(i); + + if (n.getLocalName().equals("Signature")) { + signatureFound = true; + break; + } + } + + Assert.assertTrue("Signature not found.", signatureFound); + } + + public void testCreateKeyInfoWithEncryptedKey() throws Exception { + + WSSecEncryptedKey encryptedKey = getWSEncryptedKey(); + + org.opensaml.xml.encryption.EncryptedKey samlEncryptedKey + = SAMLUtils.createEncryptedKey(getTestCertificate(), encryptedKey); + + org.opensaml.xml.signature.KeyInfo keyInfo = SAMLUtils.createKeyInfo(samlEncryptedKey); + + marshallerFactory.getMarshaller(keyInfo).marshall(keyInfo); + + Assert.assertNotNull(keyInfo.getDOM()); + printElement(keyInfo.getDOM()); + } + + public void testCreateKeyInfoWithX509Data() throws Exception { + + X509Data x509Data = SAMLUtils.createX509Data(getTestCertificate()); + + org.opensaml.xml.signature.KeyInfo keyInfo = SAMLUtils.createKeyInfo(x509Data); + + marshallerFactory.getMarshaller(keyInfo).marshall(keyInfo); + + Assert.assertNotNull(keyInfo.getDOM()); + printElement(keyInfo.getDOM()); + } + + public void testCreateAssertion() throws Exception { + + Assertion assertion = getAssertion(); + marshallerFactory.getMarshaller(assertion).marshall(assertion); + Assert.assertNotNull(assertion.getDOM()); + + try { + printElement(assertion.getDOM()); + } catch (TransformerException e) { + log.error("Error printing SAML element", e); + throw e; + } + } + + private Assertion getAssertion() throws Exception{ + + Attribute attributeMemberLevel + = SAMLUtils.createAttribute("MemberLevel", "http://www.oasis.open.org/Catalyst2002/attributes", "gold"); + + Attribute email + = SAMLUtils.createAttribute("E-mail", + "http://www.oasis.open.org/Catalyst2002/attributes", + "j...@yahoo.com"); + + NameIdentifier nameIdentifier + = SAMLUtils.createNamedIdentifier("joe,ou=people,ou=saml-demo,o=baltimore.com", + NameIdentifier.X509_SUBJECT); + + X509Data x509Data = SAMLUtils.createX509Data(getTestCertificate()); + + org.opensaml.xml.signature.KeyInfo keyInfo = SAMLUtils.createKeyInfo(x509Data); + + Subject subject + = SAMLUtils.createSubject(nameIdentifier, "urn:oasis:names:tc:SAML:1.0:cm:holder-of-key", keyInfo); + + AttributeStatement attributeStatement + = SAMLUtils.createAttributeStatement(subject, Arrays.asList(attributeMemberLevel, email)); + + List<Statement> statements = new ArrayList<Statement>(); + statements.add(attributeStatement); + + Assertion assertion + = SAMLUtils.createAssertion("www.opensaml.org", new DateTime(), + new DateTime(2050, 1, 1, 0, 0, 0, 0), statements); + + return assertion; + + } + + public void testCreateX509Data() throws Exception { + + X509Data x509Data = SAMLUtils.createX509Data(getTestCertificate()); + Assert.assertNotNull(x509Data); + + marshallerFactory.getMarshaller(x509Data).marshall(x509Data); + Assert.assertNotNull(x509Data.getDOM()); + + // Check certificates are equal + + String base64Cert = Base64.encode(getTestCertificate().getEncoded()); + Assert.assertEquals(base64Cert, x509Data.getDOM().getFirstChild().getTextContent()); + + /* try { + printElement(x509Data.getDOM()); + } catch (TransformerException e) { + log.error("Error printing SAML element", e); + throw e; + }*/ + + } + + public void testGetSymmetricKeyBasedKeyInfoContent() throws Exception { + + WSSecEncryptedKey encryptedKey = getWSEncryptedKey(); + + org.opensaml.xml.encryption.EncryptedKey samlEncryptedKey + = SAMLUtils.createEncryptedKey(getTestCertificate(), encryptedKey); + + marshallerFactory.getMarshaller(samlEncryptedKey).marshall(samlEncryptedKey); + printElement(samlEncryptedKey.getDOM()); + + Assert.assertTrue(equals(getXMLString(samlEncryptedKey.getDOM()), + getXMLString(encryptedKey.getEncryptedKeyElement()))); + + } + + private static WSSecEncryptedKey getWSEncryptedKey() throws Exception { + + SOAPEnvelope env = TrustUtil.createSOAPEnvelope("http://schemas.xmlsoap.org/soap/envelope/"); + Document doc = ((Element) env).getOwnerDocument(); + + int keySize = 256; + int keyComputation = AbstractIssuerConfig.KeyComputation.KEY_COMP_PROVIDE_ENT; + + byte [] ephemeralKey = generateEphemeralKey(256); + + WSSecEncryptedKey encryptedKey + = SAMLUtils.getSymmetricKeyBasedKeyInfoContent(doc, + ephemeralKey, getTestCertificate(), keySize, getCrypto()); + + Assert.assertNotNull(encryptedKey.getEncryptedKeyElement()); + printElement(encryptedKey.getEncryptedKeyElement()); + + return encryptedKey; + } + + private static byte[] generateEphemeralKey(int keySize) throws TrustException { + try { + SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); + byte[] temp = new byte[keySize / 8]; + random.nextBytes(temp); + return temp; + } catch (Exception e) { + throw new TrustException("errorCreatingSymmKey", e); + } + } + + private static Crypto getCrypto() throws IOException { + + File file = new File("src/test/resources/crypto.config"); + Assert.assertTrue(file.exists()); + + Properties properties = new Properties(); + try { + properties.load(new FileInputStream(file)); + } catch (IOException e) { + log.error("Unable to open crypto configuration file"); + throw e; + } + + Crypto crypto = CryptoFactory.getInstance(properties); + + X509Certificate[] certificates = crypto.getCertificates("apache"); + Assert.assertEquals(certificates.length, 1); + + return crypto; + + } + + private static void printElement(Element element) throws TransformerException { + + // print xml + if (PRINT) { + System.out.println(getXMLString(element)); + } + } + + private static X509Certificate getTestCertificate() throws IOException { + + Crypto crypto = getCrypto(); + + X509Certificate[] certificates = crypto.getCertificates("apache"); + Assert.assertEquals(certificates.length, 1); + + return certificates[0]; + + } + + private static String getXMLString(Element element) throws TransformerException { + + TransformerFactory transfac = TransformerFactory.newInstance(); + Transformer trans = transfac.newTransformer(); + trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + trans.setOutputProperty(OutputKeys.INDENT, "yes"); + + // create string from xml tree + StringWriter sw = new StringWriter(); + StreamResult result = new StreamResult(sw); + DOMSource source = new DOMSource(element); + trans.transform(source, result); + return sw.toString(); + + } + + private static boolean equals(String element1, String element2) throws ParserConfigurationException, IOException, SAXException { + + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + dbf.setCoalescing(true); + dbf.setIgnoringElementContentWhitespace(true); + dbf.setIgnoringComments(true); + DocumentBuilder db = dbf.newDocumentBuilder(); + + Document doc1 = db.parse(new ByteArrayInputStream(element1.getBytes("UTF-8"))); + doc1.normalizeDocument(); + + Document doc2 = db.parse(new ByteArrayInputStream(element1.getBytes("UTF-8"))); + doc2.normalizeDocument(); + + return doc1.isEqualNode(doc2); + } + +} Added: axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/test/resources/crypto.config URL: http://svn.apache.org/viewvc/axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/test/resources/crypto.config?rev=1295060&view=auto ============================================================================== --- axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/test/resources/crypto.config (added) +++ axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/test/resources/crypto.config Wed Feb 29 10:45:37 2012 @@ -0,0 +1,5 @@ +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.password=password +org.apache.ws.security.crypto.merlin.keystore.alias=apache +org.apache.ws.security.crypto.merlin.file=keystore.jks Added: axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/test/resources/keystore.jks URL: http://svn.apache.org/viewvc/axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/test/resources/keystore.jks?rev=1295060&view=auto ============================================================================== Binary file - no diff available. Propchange: axis/axis2/java/rampart/branches/1_6/modules/rampart-trust/src/test/resources/keystore.jks ------------------------------------------------------------------------------ svn:mime-type = application/octet-stream Modified: axis/axis2/java/rampart/branches/1_6/pom.xml URL: http://svn.apache.org/viewvc/axis/axis2/java/rampart/branches/1_6/pom.xml?rev=1295060&r1=1295059&r2=1295060&view=diff ============================================================================== --- axis/axis2/java/rampart/branches/1_6/pom.xml (original) +++ axis/axis2/java/rampart/branches/1_6/pom.xml Wed Feb 29 10:45:37 2012 @@ -226,6 +226,10 @@ <artifactId>xalan</artifactId> <groupId>xalan</groupId> </exclusion> + <!--exclusion> + <artifactId>org.opensaml</artifactId> + <groupId>opensaml1</groupId> + </exclusion--> </exclusions> </dependency> <dependency> @@ -235,13 +239,24 @@ </dependency> <dependency> <groupId>org.opensaml</groupId> - <artifactId>opensaml1</artifactId> - <version>1.1</version> + <artifactId>opensaml</artifactId> + <version>${opensaml.version}</version> + <exclusions> + <!-- Don't allow OpenSAML to impose a particular logging implementation --> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>jcl-over-slf4j</artifactId> + </exclusion> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>log4j-over-slf4j</artifactId> + </exclusion> + </exclusions> </dependency> - <dependency> + <dependency> <groupId>org.opensaml</groupId> - <artifactId>opensaml</artifactId> - <version>2.2.3</version> + <artifactId>opensaml1</artifactId> + <version>1.1</version> <exclusions> <!-- Don't allow OpenSAML to impose a particular logging implementation --> <exclusion> @@ -401,7 +416,7 @@ <axiom.version>1.2.13-SNAPSHOT</axiom.version> <wss4j.version>1.5.12</wss4j.version> - <opensaml.version>1.1</opensaml.version> + <opensaml.version>2.5.1-1</opensaml.version> <bcprov.jdk15.version>140</bcprov.jdk15.version>