a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * The contents of this file are subject to the terms
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * of the Common Development and Distribution License
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * (the License). You may not use this file except in
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * compliance with the License.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * You can obtain a copy of the License at
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * https://opensso.dev.java.net/public/CDDLv1.0.html or
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * See the License for the specific language governing
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * permission and limitations under the License.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * When distributing Covered Code, include this CDDL
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * Header Notice in each file and include the License file
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * If applicable, add the following below the CDDL Header,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * with the fields enclosed by brackets [] replaced by
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * your own identifying information:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * "Portions Copyrighted [year] [name of copyright owner]"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * $Id: QuerySignatureUtil.java,v 1.2 2008/06/25 05:47:45 qcheng Exp $
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Portions Copyrighted 2015 ForgeRock AS.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbingsimport com.sun.identity.shared.configuration.SystemPropertiesManager;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterimport com.sun.identity.shared.encode.URLEncDec;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterimport com.sun.identity.saml.common.SAMLConstants;
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbingsimport org.apache.xml.security.algorithms.JCEMapper;
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbingsimport org.apache.xml.security.signature.XMLSignature;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * The <code>QuerySignatureUtil</code> provides methods to
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * sign query string and to verify signature on query string
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings private static final String SIGNATURE = "Signature";
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * Signs the query string.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * @param queryString Query String
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * @param privateKey siging key
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * @return String signed query string
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * @exception SAML2Exception if the signing fails
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings public static String sign(String queryString, PrivateKey privateKey) throws SAML2Exception {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "QuerySignatureUtil.sign: ";
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "Either input query string or private key is null."
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "Input query string:\n" +
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings //Defaulting to RSA-SHA1 for the sake of interoperability
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings querySigAlg = SystemPropertiesManager.get(SAML2Constants.QUERY_SIGNATURE_ALGORITHM_RSA,
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings //Defaulting to SHA1WithDSA as JDK7 does not support SHA256WithDSA
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings querySigAlg = SystemPropertiesManager.get(SAML2Constants.QUERY_SIGNATURE_ALGORITHM_DSA,
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings querySigAlg = SystemPropertiesManager.get(SAML2Constants.QUERY_SIGNATURE_ALGORITHM_EC,
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings SAML2Utils.debug.error(classMethod + "Private Key algorithm not supported: " + alg);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings throw new SAML2Exception(SAML2Utils.bundle.getString("algorithmNotSupported"));
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings sig = Signature.getInstance(JCEMapper.translateURItoJCEID(querySigAlg));
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings queryString += SAML2Constants.SIG_ALG + "=" + URLEncDec.encode(querySigAlg);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "Final string to be signed:\n" +
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "Generated signature is null");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "nullSigGenerated"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "Signed query string:\n" +
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * Verifies the query string signature.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param queryString Signed query String.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @param verificationCerts Verification certificates.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @return boolean whether the verification is successful or not.
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings * @throws SAML2Exception if there is an error during verification.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "QuerySignatureUtil.verify: ";
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings queryString.length() == 0 || verificationCerts.isEmpty()) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "Input query string or certificate is null");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "Query string to be verifed:\n" + queryString);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (token.startsWith(SAML2Constants.SAML_REQUEST)) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster } else if (token.startsWith(SAML2Constants.SAML_RESPONSE)) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster } else if (token.startsWith(SAML2Constants.RELAY_STATE)) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster } else if (token.startsWith(SAML2Constants.SIG_ALG)) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster } else if (token.startsWith(SAML2Constants.SIGNATURE)) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "Null SigAlg query parameter.");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "Null Signature query parameter.");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster // The following manipulation is necessary because
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster // other implementations could send the query
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster // parameters out of order, i.e., not in the same
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster // order when signature is produced
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "Query string to be verifed (re-arranged):\n" +
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (sigAlgValue == null || sigAlgValue.equals("")) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "Null SigAlg query parameter value.");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "SigAlg query parameter value: " +
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (encSigValue == null || encSigValue.equals("")) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "Null Signature query parameter value.");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "Signature query parameter value:\n" +
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster // base-64 decode the signature value
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster // get Signature instance based on algorithm
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings if (!SIGNATURE.equals(JCEMapper.getAlgorithmClassFromURI(sigAlgValue))) {
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings SAML2Utils.debug.error(classMethod + "Signature algorithm " + sigAlgValue + " is not supported.");
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings throw new SAML2Exception(SAML2Utils.bundle.getString("algNotSupported"));
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings sig = Signature.getInstance(JCEMapper.translateURItoJCEID(sigAlgValue));
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings return isValidSignature(sig, verificationCerts, newQueryString.getBytes(), signature);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings private static boolean isValidSignature(Signature sig, Set<X509Certificate> certificates, byte[] queryString,
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings final String classMethod = "QuerySignatureUtil.isValidSignature: ";
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings for (X509Certificate certificate : certificates) {
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings } catch (InvalidKeyException | SignatureException ex) {
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings SAML2Utils.debug.warning(classMethod + "Signature validation failed due to " + ex);
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings return false;