/** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: SecurityUtils.java,v 1.5 2009/06/08 23:42:33 madan_ranganath Exp $ * * Portions Copyrighted 2014 ForgeRock AS */ package com.sun.identity.liberty.ws.security; import com.sun.identity.shared.debug.Debug; import com.sun.identity.shared.configuration.SystemPropertiesManager; import com.sun.identity.shared.xml.XMLUtils; import com.sun.identity.shared.encode.Base64; import com.sun.identity.liberty.ws.common.wsse.BinarySecurityToken; import com.sun.identity.liberty.ws.common.wsse.WSSEConstants; import com.sun.identity.liberty.ws.soapbinding.Message; import com.sun.identity.saml.assertion.AuthenticationStatement; import com.sun.identity.saml.assertion.Statement; import com.sun.identity.saml.assertion.Subject; import com.sun.identity.saml.assertion.SubjectConfirmation; import com.sun.identity.saml.common.SAMLConstants; import com.sun.identity.saml.common.SAMLUtils; import com.sun.identity.saml.xmlsig.KeyProvider; import com.sun.identity.saml.xmlsig.XMLSignatureException; import com.sun.identity.saml.xmlsig.XMLSignatureManager; import org.apache.xml.security.exceptions.XMLSecurityException; import org.apache.xml.security.keys.content.keyvalues.DSAKeyValue; import org.apache.xml.security.keys.content.keyvalues.RSAKeyValue; import org.apache.xml.security.utils.Constants; import java.io.ByteArrayInputStream; import java.math.BigInteger; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.PublicKey; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import javax.xml.namespace.QName; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * This class has common utility methods . */ public class SecurityUtils { private static SecurityUtils securityManager = null; private static XMLSignatureManager sm = null; private static Debug debug = null; private static String PROP_TRUSTED_CA_CERT_ALIASES = "com.sun.identity.liberty.ws.trustedca.certaliases"; private static Set trustedCACertAliases = new HashSet(); private static Map issuerTrustedCACertAliases = new HashMap(); private static KeyProvider keystore = null; static { debug = Debug.getInstance("libIDWSF"); String tmpStr = SystemPropertiesManager.get( PROP_TRUSTED_CA_CERT_ALIASES); if (debug.messageEnabled()) { debug.message("SecurityUtils.static: trusted ca certaliases = " + tmpStr); } if (tmpStr != null) { StringTokenizer stz = new StringTokenizer(tmpStr, "|"); while(stz.hasMoreTokens()) { String aliasIssuer = stz.nextToken().trim(); if (aliasIssuer.length() > 0) { int index = aliasIssuer.indexOf(":"); if (index == -1) { trustedCACertAliases.add(aliasIssuer); if (debug.messageEnabled()) { debug.message("SecurityUtils.static: add " + aliasIssuer + " to trustedCACertAliases"); } } else { String alias = aliasIssuer.substring(0, index).trim(); if (alias.length() > 0) { trustedCACertAliases.add(alias); if (debug.messageEnabled()) { debug.message("SecurityUtils.static: add " + alias +" to trustedCACertAliases"); } String issuer = aliasIssuer.substring(index + 1).trim(); if (issuer.length() > 0) { issuerTrustedCACertAliases.put(issuer, alias); if (debug.messageEnabled()) { debug.message("SecurityUtils.static: add "+ "[" + issuer + ", " + alias + "] to issuerTrustedCACertAliases"); } } } } } } } sm = XMLSignatureManager.getInstance(); if (sm != null) { keystore = sm.getKeyProvider(); } } /* * Sign part of the Message object based on the Security Token * profile embedded in the object. * * @param m Message object * @return Signature of Security Token Profile */ public static Element signMessage(Message m) { try { Document doc = m.toDocument(true); int securityType = m.getSecurityProfileType(); Certificate cert = null; List ids = m.getSigningIds(); if (debug.messageEnabled()) { debug.message("Security Type = " + securityType); } if (securityType==m.X509_TOKEN) { cert = m.getMessageCertificate(); return sm.signWithWSSX509TokenProfile(doc, cert, "", ids, m.getWSFVersion()); } else if (securityType==m.SAML_TOKEN) { SecurityAssertion assertion = m.getAssertion(); cert = m.getMessageCertificate(); String assertionID = assertion.getAssertionID(); return sm.signWithWSSSAMLTokenProfile(doc, cert, assertionID, "", ids, m.getWSFVersion()); } else if (securityType==m.ANONYMOUS) { // Should be transportation layer encryption. } } catch (Exception e) { debug.error("Unable to sign Soap message!",e); } return null; } /** * Verify all the signatures of the of Message object passed * from Soap Binding. * * @param m Message object whose signature to be verified * @return true if the signature is verified. */ public static boolean verifyMessage(Message m){ try { Document doc = m.toDocument(false); Certificate clientCert = (Certificate) m.getPeerCertificate(); Certificate messageCert = (Certificate) m.getMessageCertificate(); int securityProfileType = m.getSecurityProfileType(); if (securityProfileType == Message.SAML_TOKEN || securityProfileType == Message.BEARER_TOKEN) { SecurityAssertion assertion = m.getAssertion(); String certAlias = null; Certificate signingCert = getAssertionSigningCert(assertion); if (signingCert == null) { certAlias = (String)issuerTrustedCACertAliases .get(assertion.getIssuer()); if (certAlias == null) { debug.error("SecurityUtils.verifyMessage: " + "assertion doesn't have keyInfo and " + "issuer is not in " + "com.sun.identity.liberty.ws.trustedca.certalias" + " in AMConfig"); return false; } } else { certAlias = keystore.getCertificateAlias(signingCert); if (certAlias == null) { debug.error("SecurityUtils.verifyMessage: " + "assertion is signed with a certificate that " + " is not in the keystore"); return false; } else if (!trustedCACertAliases.contains(certAlias)) { debug.error("SecurityUtils.verifyMessage: " + "assertion is signed with a certificate that " + " is in the keystore but not in " + "com.sun.identity.liberty.ws.trustedca.certalias" + " in AMConfig"); return false; } } assertion.setVerifyingCertAlias(certAlias); if (!assertion.isSignatureValid()) { debug.error("SecurityUtils.verifyMessage: assertion " + "signature invalid"); return false; } if (debug.messageEnabled()) { debug.message("SecurityUtils.verifyMessage: Assertion " + " signing cert alias = " + certAlias); } } if ((clientCert!=null)&&(!clientCert.equals(messageCert))) { debug.error("Client authentication certificate is not " + "the same as the certificate inside the " + "soap message"); return false; } if (messageCert != null) { String messageCertAlias = keystore.getCertificateAlias(messageCert); return sm.verifyXMLSignature(m.getWSFVersion(), messageCertAlias, doc); } return true; } catch (Exception e) { debug.error("Unable to verify Soap Message!", e); } return false; } /** * Get Certificate from X509 Security Token Profile document. * * @param binarySecurityToken the Security Token. * @return X509 Certiticate object. */ public static java.security.cert.Certificate getCertificate( BinarySecurityToken binarySecurityToken) { java.security.cert.Certificate cert = null; try { String certString = binarySecurityToken.getTokenValue(); StringBuffer xml = new StringBuffer(100); xml.append(WSSEConstants.BEGIN_CERT); xml.append(certString); xml.append(WSSEConstants.END_CERT); byte[] barr = null; barr = (xml.toString()).getBytes(); CertificateFactory cf = CertificateFactory.getInstance("X.509"); ByteArrayInputStream bais = new ByteArrayInputStream(barr); QName valueType = binarySecurityToken.getValueType(); if (valueType.equals(BinarySecurityToken.PKCS7)) { // PKCS7 format Collection c = cf.generateCertificates(bais); Iterator i = c.iterator(); while (i.hasNext()) { cert = (java.security.cert.Certificate) i.next(); } } else { //X509:v3 format while (bais.available() > 0) { cert = cf.generateCertificate(bais); } } } catch (Exception e) { // Certificate encoding error! debug.error("WSSecurityManager:getX509Certificate", e); } return cert; } /** * Gets the Certificate from the Assertion. * * @param assertion the SAML Assertion. * @return X509Certificate object. */ public static java.security.cert.Certificate getCertificate( SecurityAssertion assertion) { if (debug.messageEnabled()) { debug.message("SecurityAssertion = " + assertion.toString()); } try { Set statements = assertion.getStatement(); if (statements !=null && !(statements.isEmpty())) { Iterator iterator = statements.iterator(); while (iterator.hasNext()) { Statement statement =(Statement)iterator.next(); int stype = statement.getStatementType(); Subject subject = null; if (stype == Statement.AUTHENTICATION_STATEMENT) { subject = ((AuthenticationStatement)statement).getSubject(); } else if (stype == ResourceAccessStatement.RESOURCEACCESS_STATEMENT) { ResourceAccessStatement raStatement = (ResourceAccessStatement)statement; subject = raStatement.getProxySubject(); if (subject == null) { subject = raStatement.getSubject(); } } else if (stype == SessionContextStatement.SESSIONCONTEXT_STATEMENT) { SessionContextStatement scStatement = (SessionContextStatement)statement; subject = scStatement.getProxySubject(); if (subject == null) { subject = scStatement.getSubject(); } } if (subject != null) { SubjectConfirmation subConfirm = subject.getSubjectConfirmation(); if (subConfirm.getConfirmationMethod().contains( SAMLConstants.CONFIRMATION_METHOD_HOLDEROFKEY)) { Element keyinfo = subConfirm.getKeyInfo(); return getCertificate(keyinfo); } } } } else { debug.error("Assertion does not contain any Statement."); } } catch (Exception e) { debug.error("getCertificate Exception: ", e); } return null; } /** * Returns the X509Certificate object. * * @param keyinfo the KeyInfo Document Element. * @return the X509Certificate object. */ private static X509Certificate getCertificate(Element keyinfo) { X509Certificate cert = null; if (debug.messageEnabled()) { debug.message("KeyInfo = " + XMLUtils.print(keyinfo)); } Element x509 = (Element) keyinfo.getElementsByTagNameNS( Constants.SignatureSpecNS, SAMLConstants.TAG_X509CERTIFICATE).item(0); if (x509 == null) { // no cert found. try DSA/RSA key try { PublicKey pk = getPublicKey(keyinfo); cert = (X509Certificate) keystore.getCertificate(pk); } catch (Exception e) { debug.error("getCertificate Exception: ", e); } } else { String certString = x509.getChildNodes().item(0).getNodeValue(); cert = getCertificate(certString, null); } return cert; } /** * Returns the PublicKey. */ private static PublicKey getPublicKey(Element reference) throws XMLSignatureException { PublicKey pubKey = null; Document doc = reference.getOwnerDocument(); Element dsaKey = (Element) reference.getElementsByTagNameNS( Constants.SignatureSpecNS, SAMLConstants.TAG_DSAKEYVALUE).item(0); if (dsaKey != null) { // It's DSAKey NodeList nodes = dsaKey.getChildNodes(); int nodeCount = nodes.getLength(); if (nodeCount > 0) { BigInteger p=null, q=null, g=null, y=null; for (int i = 0; i < nodeCount; i++) { Node currentNode = nodes.item(i); if (currentNode.getNodeType() == Node.ELEMENT_NODE) { String tagName = currentNode.getLocalName(); Node sub = currentNode.getChildNodes().item(0); String value = sub.getNodeValue(); value = SAMLUtils.removeNewLineChars(value); BigInteger v = new BigInteger(Base64.decode(value)); if (tagName.equals("P")) { p = v; } else if (tagName.equals("Q")) { q = v; } else if (tagName.equals("G")) { g = v; } else if (tagName.equals("Y")) { y = v; } else { SAMLUtils.debug.error("Wrong tag name in DSA key."); throw new XMLSignatureException( SAMLUtils.bundle.getString("errorObtainPK")); } } } DSAKeyValue dsaKeyValue = new DSAKeyValue(doc, p, q, g, y); try { pubKey = dsaKeyValue.getPublicKey(); } catch (XMLSecurityException xse) { SAMLUtils.debug.error("Could not get Public Key from" + " DSA key value."); throw new XMLSignatureException( SAMLUtils.bundle.getString("errorObtainPK")); } } } else { Element rsaKey = (Element) reference.getElementsByTagNameNS( Constants.SignatureSpecNS, SAMLConstants.TAG_RSAKEYVALUE).item(0); if (rsaKey != null) { // It's RSAKey NodeList nodes = rsaKey.getChildNodes(); int nodeCount = nodes.getLength(); BigInteger m=null, e=null; if (nodeCount > 0) { for (int i = 0; i < nodeCount; i++) { Node currentNode = nodes.item(i); if (currentNode.getNodeType() == Node.ELEMENT_NODE) { String tagName = currentNode.getLocalName(); Node sub = currentNode.getChildNodes().item(0); String value = sub.getNodeValue(); value = SAMLUtils.removeNewLineChars(value); BigInteger v =new BigInteger(Base64.decode(value)); if (tagName.equals("Exponent")) { e = v; } else if (tagName.equals("Modulus")){ m = v; } else { SAMLUtils.debug.error("Wrong tag name from " + "RSA key element."); throw new XMLSignatureException( SAMLUtils.bundle.getString("errorObtainPK")); } } } } RSAKeyValue rsaKeyValue = new RSAKeyValue(doc,m, e); try { pubKey = rsaKeyValue.getPublicKey(); } catch (XMLSecurityException ex) { SAMLUtils.debug.error("Could not get Public Key from" + " RSA key value."); throw new XMLSignatureException( SAMLUtils.bundle.getString("errorObtainPK")); } } } return pubKey; } /** * Returns the X509Certificate object. * * @param certString the Certificate String. * @param format the Certificate's format. * @return the X509Certificate object. */ private static X509Certificate getCertificate(String certString, String format) { X509Certificate cert = null; try { if (SAMLUtils.debug.messageEnabled()) { SAMLUtils.debug.message("getCertificate(Assertion) : " + certString); } StringBuffer xml = new StringBuffer(100); xml.append(SAMLConstants.BEGIN_CERT); xml.append(certString); xml.append(SAMLConstants.END_CERT); byte[] barr = null; barr = (xml.toString()).getBytes(); CertificateFactory cf = CertificateFactory.getInstance("X.509"); ByteArrayInputStream bais = new ByteArrayInputStream(barr); if ((format !=null) && format.equals(SAMLConstants.TAG_PKCS7)) { // PKCS7 format Collection c = cf.generateCertificates(bais); Iterator i = c.iterator(); while (i.hasNext()) { cert = (java.security.cert.X509Certificate) i.next(); } } else { //X509:v3 format while (bais.available() > 0) { cert = (java.security.cert.X509Certificate) cf.generateCertificate(bais); } } } catch (Exception e) { SAMLUtils.debug.error("getCertificate Exception: ", e); } return cert; } /** * Returns the X509Certificate in the Assertion. * * @param assertion the SAML Assertion * @return the X509Certificate object. */ private static X509Certificate getAssertionSigningCert( SecurityAssertion assertion) { X509Certificate cert = null; Element signature = assertion.getSignature(); Element keyInfo = (Element) signature.getElementsByTagNameNS( Constants.SignatureSpecNS, SAMLConstants.TAG_KEYINFO).item(0); if (keyInfo != null) { cert = (X509Certificate) getCertificate(keyInfo); } return cert; } /** * Returns XML Signature instance. */ public static XMLSignatureManager getSignatureManager() { return sm; } }