com.vmware.identity.samlservice.SamlServiceTest.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.identity.samlservice.SamlServiceTest.java

Source

/*
 *  Copyright (c) 2012-2015 VMware, Inc.  All Rights Reserved.
 *
 *  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 com.vmware.identity.samlservice;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertPath;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.lang.StringEscapeUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.opensaml.common.SAMLVersion;
import org.opensaml.saml2.core.AuthnRequest;
import org.opensaml.saml2.core.Response;
import org.opensaml.xml.io.UnmarshallingException;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import com.vmware.identity.diagnostics.DiagnosticsLoggerFactory;
import com.vmware.identity.diagnostics.IDiagnosticsLogger;
import com.vmware.identity.idm.ServerConfig;
import com.vmware.identity.SharedUtils;
import com.vmware.identity.TestConstants;
import com.vmware.identity.saml.SignatureAlgorithm;

public class SamlServiceTest {

    private final String RESPONSE_MESSAGE = "Request contains unsupported elements";
    private final String VSPHERE_LOCAL_TENANT = "vsphere.local";

    private static SamlService service;
    private static PrivateKey privateKey;
    private static X509Certificate x509Certificate;
    private static final IDiagnosticsLogger log = DiagnosticsLoggerFactory.getLogger(SamlServiceTest.class);

    private static String acsUrl;

    @BeforeClass
    public static void setUp() throws Exception {
        SharedUtils.bootstrap(false); // use real data
        String tenantName = ServerConfig.getTenant(0);
        String rpName = ServerConfig.getRelyingParty(tenantName, 0);
        String issuerUrl = ServerConfig.getRelyingPartyUrl(rpName);
        String acsName = ServerConfig.getAssertionConsumerService(rpName, 0);
        acsUrl = ServerConfig.getServiceEndpoint(acsName);

        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        InputStream is = new FileInputStream(SamlServiceTest.class.getResource("/sts-store.jks").getFile());
        char[] stsKeystorePassword = "ca$hc0w".toCharArray();
        ks.load(is, stsKeystorePassword);

        String stsAlias = "stskey";
        Certificate certificate = ks.getCertificate(stsAlias);
        Key key = ks.getKey(stsAlias, stsKeystorePassword);

        List<X509Certificate> certificates = new ArrayList<X509Certificate>();
        certificates.add((X509Certificate) certificate);

        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        CertPath certPath = certFactory.generateCertPath(certificates);

        privateKey = (PrivateKey) key;
        x509Certificate = (X509Certificate) certificate;

        SamlServiceFactory factory = new DefaultSamlServiceFactory();
        service = factory.createSamlService(privateKey, SignatureAlgorithm.RSA_SHA256,
                SignatureAlgorithm.RSA_SHA256, issuerUrl, certPath);
    }

    @AfterClass
    public static void cleanUp() throws Exception {
        SharedUtils.cleanupTenant();
    }

    @Test
    public void testVerifySignature() throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        // pick a sample message
        String message = "This is a sample message to be encoded";

        // sign using our algorithm
        SignatureAlgorithm algo = SignatureAlgorithm.getSignatureAlgorithmForURI(TestConstants.SIGNATURE_ALGORITHM);
        Signature sig = Signature.getInstance(algo.getAlgorithmName());
        sig.initSign(privateKey);

        byte[] messageBytes = message.getBytes();
        sig.update(messageBytes);

        byte[] sigBytes = sig.sign();
        String signature = Shared.encodeBytes(sigBytes);

        // verify signature here
        sig.initVerify(x509Certificate.getPublicKey());
        sig.update(messageBytes);
        boolean verifies = sig.verify(sigBytes);
        log.debug("signature verifies in test: " + verifies);

        // just call verifySignature method and expect to not throw
        service.verifySignature(message, signature);
    }

    @Test
    public void testVerifySignatureVcd() throws Exception {
        // pick a sample VCD message
        String message = "SAMLResponse=fZJNb9swDIb%2FiqF7LPnbEeIUw4oCAVoMqN"
                + "MedhlomU49yJJrSml%2F%2FuykX8uhFwGkXpKvHmpz9Tro4IgT9dZULAoF"
                + "C9Ao2%2FbmULGH%2Fc2qZFfbDcGg41He2oP17h5ptIYwmEsNyfNdxfxkpA"
                + "XqSRoYkKRTsv5xdyvjUMhxss4qq1lwjeR6A%2B407sm5kSTnkQijtAgzES"
                + "apTNOEK219y%2B104IpGvkzg9exI49kBB90D8aNqWbC7rtifrmkhVaop26h"
                + "oijJLEOajXBdtl2KSZ7PMvNve24pBmkG%2BLiBumr%2BJyNZJLqKuzRC7J"
                + "4RmZrAj8rgz5MC4isUiilciW8XRPo5klktRhlGZ%2FmbB4zu5%2BZXsjZM"
                + "8FU9f8XxPB4hwWoiw7UJkBqJ7419XAGMeNqg1Hj2Gx%2BEFJgyVHWQhSsF"
                + "fsCGyfOkS87r%2BtaDa8K8OPvZWO3CeLsKftsXgEbTH793RSS1rrxQSMX7"
                + "R5m5OwgG39%2Fjs590GdNZ1Xr%2BZuRRepD%2Fj%2F3%2FX9h8%3D&SigA"
                + "lg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rs" + "a-sha256";

        // sign using our algorithm
        SignatureAlgorithm algo = SignatureAlgorithm.getSignatureAlgorithmForURI(TestConstants.SIGNATURE_ALGORITHM);
        Signature sig = Signature.getInstance(algo.getAlgorithmName());
        sig.initSign(privateKey);

        byte[] messageBytes = message.getBytes();
        sig.update(messageBytes);

        byte[] sigBytes = sig.sign();
        String signature = Shared.encodeBytes(sigBytes);

        // verify signature here
        sig.initVerify(x509Certificate.getPublicKey());
        sig.update(messageBytes);
        boolean verifies = sig.verify(sigBytes);
        log.debug("signature verifies in test: " + verifies);

        // just call verifySignature method and expect to not throw
        service.verifySignature(message, signature);
        /* disabled now: task 1301740
        // import our csp settings
        CasIdmClient idmClient = new CasIdmClient(SharedUtils.getIdmHostName());
            
        SharedUtils.importConfiguration(idmClient, VSPHERE_LOCAL_TENANT,
            "/csp.xml");
            
        CasIdmAccessor idmAccessor = new CasIdmAccessor(idmClient);
        log.debug("CSP settings imported successfully");
        idmAccessor.setTenant(VSPHERE_LOCAL_TENANT);
            
        // create new SamlService
        SamlServiceFactory factory2 = new DefaultSamlServiceFactory();
        CertificateFactory certFactory = CertificateFactory
            .getInstance("X.509");
        CertPath certPath = certFactory.generateCertPath(idmAccessor
            .getSAMLAuthorityChain());
        SamlService service2 = factory2.createSamlService(
            idmClient.getTenantPrivateKey(VSPHERE_LOCAL_TENANT),
            SignatureAlgorithm.RSA_SHA256, SignatureAlgorithm.RSA_SHA256,
            idmClient.getEntityID(VSPHERE_LOCAL_TENANT), certPath);
            
        // now call it again with generated signature
        String vcdSignature = "YkgxdGRqY3FiVlQvUWRLTWRHUjF1V2dJeGJZa0pHNTJJ" +
            "NGd0RUsyUEtZTDAzcloyNWJ3dmxuLzg3TlNMN1JsSVhYc2NOSkxTaVZ4Mm" +
            "c4TjNxWTBTLzg2Z0dvYjZVdVU5elY2cEZtQnJ2N0ZFZFdndFJwVDlvZE5w" +
            "VVpaa3BxQ1ROZVU4STRQYTltMVVOTDB1TUp5ckJvaVBnY3dUbk5LTko4S0" +
            "dxMWNLMlVuWTZBZGlodW5XaXdTZW5CVDVVRjZ6MHFHWmZ2d25kM2dkTWl4" +
            "eHY2WWovVElXWUg5REZYN2FJN3R0a3RTaSs5dUhTbUViMTFWRElNcGhpbm" +
            "1rdldGT3VWWHIxWFR5RUNKYnpLNXhYR3ArZXZ1UGk2TzR1UDlEVjlVdjlU" +
            "V01uVVNPYkw1aExEUDFadC9Vbzl0S1MySWIwcUp0OGIzVzV2UzVDWVdlUU" +
            "JGRTBnPT0%3D";
        vcdSignature = URLDecoder.decode(vcdSignature, "UTF-8");
        vcdSignature = Shared.decodeString(vcdSignature);
        // just call verifySignature method and expect to not throw
            
        service2.verifySignature(message, vcdSignature);*/
    }

    @Test
    public void testVerifySignatureVcd2() throws Exception {
        // pick a sample VCD message
        String message = "SAMLResponse=fZJNb%2BIwEIb%2FSuR7%2FJWQJhYBrRatVK"
                + "lcCsuhl5XjTCAosbMZm%2B7P3wClHxx6nPE7M%2B884%2FnyX99FJxixdb"
                + "YkgnISgTWubu2%2BJL%2B3v%2BKcLBdz1H0nB%2FXk9i74Z8DBWYRoKrWo"
                + "rm8lCaNVTmOLyuoeUHmjNj%2FWT0pSrobReWdcR6IVoG%2Bt9pdxB%2B8H"
                + "VIyJQlKR5VTIggqu8jRNmOlcqNm5OdtMZjq4Dme6azWyk6lJ9LgqyZ%2Bq"
                + "SUzKCw41AM%2BNEEZC1dQJT7K0qJJmktmb460riZamqhueFW1xNKI6pqbh"
                + "TXtIjqbJk6Sa5IgBHi16bX1JJBcy5lkssy3nSsyUEFTM8hcS7W7QpgXJGy"
                + "J1KR4%2Fk%2FkejEaE8QyDLG4whrE%2FxLbtq4DxqY%2FFwywWklOwe3rq"
                + "X%2FUI1LhePaRpyl6hQnTs3E2yNXhda6%2BZwWHOPtt5v9%2FGax%2FwLv"
                + "zpaoh2ugvwvVW8qNUmGAOIhN21WU9JvYfFM%2FwN040jvOqa0L2ZuRfepT"
                + "%2Fir79s8R8%3D&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%" + "2Fxmldsig-more%23rsa-sha256";

        // sign using our algorithm
        SignatureAlgorithm algo = SignatureAlgorithm.getSignatureAlgorithmForURI(TestConstants.SIGNATURE_ALGORITHM);
        Signature sig = Signature.getInstance(algo.getAlgorithmName());
        sig.initSign(privateKey);

        byte[] messageBytes = message.getBytes();
        sig.update(messageBytes);

        byte[] sigBytes = sig.sign();
        String signature = Shared.encodeBytes(sigBytes);

        // verify signature here
        sig.initVerify(x509Certificate.getPublicKey());
        sig.update(messageBytes);
        boolean verifies = sig.verify(sigBytes);
        log.debug("signature verifies in test: " + verifies);

        // just call verifySignature method and expect to not throw
        service.verifySignature(message, signature);
        /* disabled now: task 1301740
        // import our csp settings
        CasIdmClient idmClient = new CasIdmClient(SharedUtils.getIdmHostName());
            
        SharedUtils.importConfiguration(idmClient, VSPHERE_LOCAL_TENANT,
            "/csp2.xml");
            
        CasIdmAccessor idmAccessor = new CasIdmAccessor(idmClient);
        log.debug("CSP settings imported successfully");
        idmAccessor.setTenant(VSPHERE_LOCAL_TENANT);
            
        // create new SamlService
        SamlServiceFactory factory2 = new DefaultSamlServiceFactory();
        CertificateFactory certFactory = CertificateFactory
            .getInstance("X.509");
        CertPath certPath = certFactory.generateCertPath(idmAccessor
            .getSAMLAuthorityChain());
        SamlService service2 = factory2.createSamlService(
            idmClient.getTenantPrivateKey(VSPHERE_LOCAL_TENANT),
            SignatureAlgorithm.RSA_SHA256, SignatureAlgorithm.RSA_SHA256,
            idmClient.getEntityID(VSPHERE_LOCAL_TENANT), certPath);
            
        // now call it again with generated signature
        String vcdSignature = "tTUaPscQSmPKkqP9XGgCHZYoH%2FUy2MvZ1eoeP%2B3Y" +
            "nTDLxiuV5glxngtMbOGspo9NbL37lNVjdCUo7qQVDznUNmKpIOGa%2BGwE" +
            "jcgqeS7mBDsYPcICxVHZPYxbIaFCmlTIo125olswe4LuP92lIroe%2B%2F" +
            "DpeNXIGjUAFLHQwlLO7r73cHLH%2BPY2pcYww4X2I7Mhk%2FQ7I3tdMX1O" +
            "eOhqcRpMn8uyOs6JmVbMoVXuTVKyO96LmQUPCQVLmVjDeD%2BZjVALVLbs" +
            "vjWsdFt%2F%2Ff2MEXIQkYmeIM5HxZ5rW0uXRocarUrp8nhgxk%2FEQGhk" +
            "00KYP1xZTCC9JZR6OcbXJZZemBgq%2BA%3D%3D";
        vcdSignature = URLDecoder.decode(vcdSignature, "UTF-8");
        // just call verifySignature method and expect to not throw
            
        service2.verifySignature(message, vcdSignature); */
    }

    @Test
    public void testDecodeSamlAuthnRequest() throws Exception {
        SharedUtils.bootstrap(false); // use real data

        // create basic SAML Authn request
        String id = "42"; // the answer to life the universe and everything
        int tenantId = 0;
        AuthnRequest authnRequest = SharedUtils.createSamlAuthnRequest(id, tenantId);
        StringBuffer sbRequestUrl = new StringBuffer();
        sbRequestUrl.append(authnRequest.getDestination());

        // encode
        HttpServletRequest request = SharedUtils.buildMockRequestObject(authnRequest, null, null, null,
                sbRequestUrl, null, null, tenantId);

        AuthnRequest decodedAuthnRequest = service.decodeSamlAuthnRequest(request);
        assertTrue(decodedAuthnRequest.getID().equals(id));
        assertTrue(decodedAuthnRequest.getVersion().equals(SAMLVersion.VERSION_20));
    }

    @Test
    public void testCreateSamlResponse() throws UnmarshallingException {
        Response response = service.createSamlResponse("42", acsUrl, OasisNames.REQUESTER,
                OasisNames.REQUEST_UNSUPPORTED, RESPONSE_MESSAGE, null);
        assertNotNull(response);
    }

    @Test
    public void testCreateSamlResponseWithToken()
            throws SAXException, IOException, ParserConfigurationException, UnmarshallingException {
        Document token = readToken();
        Response response = service.createSamlResponse("42", acsUrl, OasisNames.REQUESTER,
                OasisNames.REQUEST_UNSUPPORTED, RESPONSE_MESSAGE, token);
        assertNotNull(response);
    }

    @Test
    public void testBuildPostResponseForm() throws UnmarshallingException {
        Response response = service.createSamlResponse("42", acsUrl, OasisNames.REQUESTER,
                OasisNames.REQUEST_UNSUPPORTED, RESPONSE_MESSAGE, null);
        String relayState = "{\"dest\":\"d2d25106-44ee-4e36-877e-1d7aa1335dcd\",\"idpId\":53}";
        String formHtml = service.buildPostResponseForm(response, relayState, acsUrl);
        assertNotNull(formHtml);

        // find input attribute for RelayState in html post form, validate the expected RelayState is embedded in the form
        Pattern inputPattern = Pattern.compile("<input(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        Pattern attributePattern = Pattern.compile("(\\w+)=\"(.*?)\"");
        Matcher inputPatternMatcher = inputPattern.matcher(formHtml);
        boolean relayStateFound = false;
        while (inputPatternMatcher.find()) {
            String attributesStr = inputPatternMatcher.group(1);
            Matcher attributePatternMatcher = attributePattern.matcher(attributesStr);
            while (attributePatternMatcher.find()) {
                if (attributePatternMatcher.group(1).equals("name")) {
                    String key = attributePatternMatcher.group(2).trim();
                    if (key.equals("RelayState")) {
                        attributePatternMatcher.find();
                        if (attributePatternMatcher.group(1).equals("value")) {
                            String value = StringEscapeUtils.unescapeHtml(attributePatternMatcher.group(2).trim());
                            assertEquals(relayState, value);
                            relayStateFound = true;
                            break;
                        }
                    }
                }
            }
        }
        assertTrue(relayStateFound);
    }

    // read token from resource stream
    private Document readToken() throws SAXException, IOException, ParserConfigurationException {
        InputStream is = new FileInputStream(SamlServiceTest.class.getResource("/saml-assertion.xml").getFile());
        return SharedUtils.readXml(is);
    }
}