org.ejbca.core.protocol.ocsp.ProtocolOcspHttpTest.java Source code

Java tutorial

Introduction

Here is the source code for org.ejbca.core.protocol.ocsp.ProtocolOcspHttpTest.java

Source

/*************************************************************************
 *                                                                       *
 *  EJBCA Community: The OpenSource Certificate Authority                *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/

package org.ejbca.core.protocol.ocsp;

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

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.security.interfaces.DSAPublicKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ejb.ObjectNotFoundException;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1GeneralizedTime;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.BERTags;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.CRLReason;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.ExtensionsGenerator;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.cert.ocsp.OCSPException;
import org.bouncycastle.cert.ocsp.OCSPReq;
import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
import org.bouncycastle.cert.ocsp.OCSPResp;
import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
import org.bouncycastle.cert.ocsp.RevokedStatus;
import org.bouncycastle.cert.ocsp.SingleResp;
import org.bouncycastle.cert.ocsp.UnknownStatus;
import org.bouncycastle.cert.ocsp.jcajce.JcaCertificateID;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.provider.JCEECPublicKey;
import org.bouncycastle.operator.BufferingContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.bouncycastle.util.encoders.Hex;
import org.cesecore.CaTestUtils;
import org.cesecore.authentication.tokens.AuthenticationToken;
import org.cesecore.authentication.tokens.UsernamePrincipal;
import org.cesecore.authorization.AuthorizationDeniedException;
import org.cesecore.certificates.ca.CAConstants;
import org.cesecore.certificates.ca.CADoesntExistsException;
import org.cesecore.certificates.ca.CAExistsException;
import org.cesecore.certificates.ca.CAInfo;
import org.cesecore.certificates.ca.CaSessionRemote;
import org.cesecore.certificates.ca.InvalidAlgorithmException;
import org.cesecore.certificates.ca.SignRequestException;
import org.cesecore.certificates.ca.SignRequestSignatureException;
import org.cesecore.certificates.ca.X509CAInfo;
import org.cesecore.certificates.ca.catoken.CAToken;
import org.cesecore.certificates.ca.extendedservices.ExtendedCAServiceInfo;
import org.cesecore.certificates.ca.internal.CaCertificateCache;
import org.cesecore.certificates.certificate.HashID;
import org.cesecore.certificates.certificate.IllegalKeyException;
import org.cesecore.certificates.certificateprofile.CertificatePolicy;
import org.cesecore.certificates.certificateprofile.CertificateProfile;
import org.cesecore.certificates.certificateprofile.CertificateProfileConstants;
import org.cesecore.certificates.certificateprofile.CertificateProfileExistsException;
import org.cesecore.certificates.certificateprofile.CertificateProfileSessionRemote;
import org.cesecore.certificates.crl.RevokedCertInfo;
import org.cesecore.certificates.endentity.EndEntityConstants;
import org.cesecore.certificates.endentity.EndEntityInformation;
import org.cesecore.certificates.endentity.EndEntityTypes;
import org.cesecore.certificates.ocsp.OcspResponseGeneratorSessionRemote;
import org.cesecore.certificates.ocsp.SHA1DigestCalculator;
import org.cesecore.certificates.util.AlgorithmConstants;
import org.cesecore.config.GlobalOcspConfiguration;
import org.cesecore.config.OcspConfiguration;
import org.cesecore.configuration.CesecoreConfigurationProxySessionRemote;
import org.cesecore.configuration.GlobalConfigurationSessionRemote;
import org.cesecore.keys.token.CryptoTokenAuthenticationFailedException;
import org.cesecore.keys.token.CryptoTokenOfflineException;
import org.cesecore.keys.token.CryptoTokenTestUtils;
import org.cesecore.keys.util.KeyTools;
import org.cesecore.keys.util.PublicKeyWrapper;
import org.cesecore.mock.authentication.tokens.TestAlwaysAllowLocalAuthenticationToken;
import org.cesecore.util.Base64;
import org.cesecore.util.CertTools;
import org.cesecore.util.CryptoProviderTools;
import org.cesecore.util.EjbRemoteHelper;
import org.ejbca.core.ejb.ca.CaTestCase;
import org.ejbca.core.ejb.ca.caadmin.CAAdminSessionRemote;
import org.ejbca.core.ejb.ca.revoke.RevocationSessionRemote;
import org.ejbca.core.ejb.ca.sign.SignSessionRemote;
import org.ejbca.core.ejb.ra.EndEntityManagementSessionRemote;
import org.ejbca.core.ejb.ra.raadmin.EndEntityProfileSessionRemote;
import org.ejbca.core.model.InternalEjbcaResources;
import org.ejbca.core.model.SecConst;
import org.ejbca.core.model.approval.ApprovalException;
import org.ejbca.core.model.approval.WaitingForApprovalException;
import org.ejbca.core.model.ca.AuthLoginException;
import org.ejbca.core.model.ca.AuthStatusException;
import org.ejbca.core.model.ca.caadmin.extendedcaservices.HardTokenEncryptCAServiceInfo;
import org.ejbca.core.model.ca.caadmin.extendedcaservices.KeyRecoveryCAServiceInfo;
import org.ejbca.core.model.ra.raadmin.EndEntityProfile;
import org.ejbca.core.model.ra.raadmin.EndEntityProfileExistsException;
import org.ejbca.core.model.ra.raadmin.UserDoesntFullfillEndEntityProfile;
import org.ejbca.core.protocol.ocsp.extension.certhash.OcspCertHashExtension;
import org.ejbca.ui.web.LimitLengthASN1Reader;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * Tests http pages of ocsp
 * @version $Id: ProtocolOcspHttpTest.java 20728 2015-02-20 14:55:55Z mikekushner $
 *
 */
public class ProtocolOcspHttpTest extends ProtocolOcspTestBase {

    public static final String DEFAULT_SUPERADMIN_CN = "SuperAdmin";

    private static final String DSA_DN = "CN=OCSPDSATEST,O=Foo,C=SE";

    private static final Logger log = Logger.getLogger(ProtocolOcspHttpTest.class);

    private static final InternalEjbcaResources intres = InternalEjbcaResources.getInstance();

    private static final AuthenticationToken admin = new TestAlwaysAllowLocalAuthenticationToken(
            new UsernamePrincipal("ProtocolOcspHttpTest"));

    private static byte[] ks3 = Base64.decode(("MIACAQMwgAYJKoZIhvcNAQcBoIAkgASCAyYwgDCABgkqhkiG9w0BBwGggCSABIID"
            + "DjCCAwowggMGBgsqhkiG9w0BDAoBAqCCAqkwggKlMCcGCiqGSIb3DQEMAQMwGQQU"
            + "/h0pQXq7ZVjYWlDvzEwwmiJ8O8oCAWQEggJ4MZ12+kTVGd1w7SP4ZWlq0bCc4MsJ"
            + "O0FFSX3xeVp8Bx16io1WkEFOW3xfqjuxKOL6YN9atoOZdfhlOMhmbhglm2PJSzIg"
            + "JSDHvWk2xKels5vh4hY1iXWOh48077Us4wP4Qt94iKglCq4xwxYcSCW8BJwbu93F"
            + "uxE1twnWXbH192nMhaeIAy0v4COdduQamJEtHRmIJ4GZwIhH+lNHj/ARdIfNw0Dm"
            + "uPspuSu7rh6rQ8SrRsjg63EoxfSH4Lz6zIJKF0OjNX07T8TetFgznCdGCrqOZ1fK"
            + "5oRzXIA9hi6UICiuLSm4EoHzEpifCObpiApwNj3Kmp2uyz2uipU0UKhf/WqvmU96"
            + "yJj6j1JjZB6p+9sgecPFj1UMWhEFTwxMEwR7iZDvjkKDNWMit+0cQyeS7U0Lxn3u"
            + "m2g5e6C/1akwHZsioLC5OpFq/BkPtnbtuy4Kr5Kwb2y7vSiKpjFr7sKInjdAsgCi"
            + "8kyUV8MyaIfZdtREjwqBe0imfP+IPVqAsl1wGW95YXsLlK+4P1bspAgeHdDq7Q91"
            + "bJJQAS5OTD38i1NY6MRtt/fWsShVBLjf2FzNpw6siHHl2N7BDNyO3ALtgfp50e0Z"
            + "Dsw5WArgKLiXfwZIrIKbYA73RFc10ReDqnJSF+NXgBo1/i4WhZLHC1Osl5UoKt9q"
            + "UoXIUmYhAwdAT5ZKVw6A8yp4e270yZTXNsDz8u/onEwNc1iM0v0RnPQhNE5sKEZH"
            + "QrMxttiwbKe3YshCjbruz/27XnNA51t2p1M6eC1HRab4xSHAyH5NTxGJ8yKhOfiT"
            + "aBKqdTH3P7QzlcoCUDVDDe7aLMaZEf+a2Te63cZTuUVpkysxSjAjBgkqhkiG9w0B"
            + "CRQxFh4UAHAAcgBpAHYAYQB0AGUASwBlAHkwIwYJKoZIhvcNAQkVMRYEFCfeHSg6"
            + "EdeP5A1IC8ydjyrjyFSdAAQBAAQBAAQBAAQBAASCCBoAMIAGCSqGSIb3DQEHBqCA"
            + "MIACAQAwgAYJKoZIhvcNAQcBMCcGCiqGSIb3DQEMAQYwGQQURNy47tUcttscSleo"
            + "8gY6ZAPFOl0CAWSggASCB8jdZ+wffUP1B25Ys48OFBMg/itT0EBS6J+dYVofZ84c"
            + "x41q9U+CRMZJwVNZbkqfRZ+F3tLORSwuIcwyioa2/JUpv8uJCjQ2tru5+HtqCrzR"
            + "Huh7TfdiMqvjkKpnXi69DPPjQdCSPwYMy1ahZrP5KgEZg4S92xpU2unF1kKQ30Pq"
            + "PTEBueDlFC39rojp51Wsnqb1QzjPo53YvJQ8ztCoG0yk+0omELyPbc/qMKe5/g5h"
            + "Lx7Q+2D0PC/ZHtoDkCRfMDKwgwALFsSj2uWNJsCplspmc7YgIzSr/GqqeSXHp4Ue"
            + "dwVJAswrhpkXZTlp1rtl/lCSFl9akwjY1fI144zfpYKpLqfoHL1uI1c3OumrFzHd"
            + "ZldZYgsM/h3qjgu8qcXqI0sKVXsffcftCaVs+Bxmdu9vpY15rlx1e0an/O05nMKU"
            + "MBU2XpGkmWxuy0tOKs3QtGzHUJR5+RdEPURctRyZocEjJgTvaIMq1dy/FIaBhi+d"
            + "IeAbFmjBu7cv9C9v/jMuUjLroycmo7QW9jGgyTOQ68J+6w2/PtqiqIo3Ry9WC0SQ"
            + "8+fVNOGLr5O2YPpw17sDQa/+2gjozngvL0OHiABwQ3EbXAQLF046VYkTi5R+8iGV"
            + "3jlTvvStIKY06E/s/ih86bzwJWAQENCazXErN69JO+K3IUiwxac+1AOO5WyR9qyv"
            + "6m/yHdIdbOVE21M2RARbI8UiDpRihCzk4duPfj/x2bZyFqLclIMhbTd2UOQQvr+W"
            + "4etpMJRtyFGhdLmNgYAhYrbUgmdL1kRkzPzOs77PqleMpfkii7HPk3HlVkM7NIqd"
            + "dN0WQaQwGJuh5f1ynhyqtsaw6Gu/X56H7hpziAh0eSDQ5roRE7yy98h2Mcwb2wtY"
            + "PqVFTmoKuRWR2H5tT6gCaAM3xiSC7RLa5SF1hYQGaqunqBaNPYyUIg/r03dfwF9r"
            + "AkOhh6Mq7Z2ktzadWTxPl8OtIZFVeyqIOtSKBHhJyGDGiz3+SSnTnSX81NaTSJYZ"
            + "7YTiXkXvSYNpjpPckIKfjpBw0T4pOva3a6s1z5p94Dkl4kz/zOmgveGd3dal6wUV"
            + "n3TR+2cyv51WcnvB9RIp58SJOc+CvCvYTvkEdvE2QtRw3wt4ngGJ5pxmC+7+8fCf"
            + "hRDzw9LBNz/ry88y/0Bidpbhwr8gEkmHuaLp43WGQQsQ+cWYJ8AeLZMvKplbCWqy"
            + "iuks0MnKeaC5dcB+3BL55OvcTfGkMtz0oYBkcGBTbbR8BKJZgkIAx7Q+/rCaqv6H"
            + "HN/cH5p8iz5k+R3MkmR3gi6ktelQ2zx1pbPz3IqR67cTX3IyTX56F2aY54ueY17m"
            + "7hFwSy4aMen27EO06DXn/b6vPKj73ClE2B/IPHO/H2e8r04JWMltFWuStV0If5x0"
            + "5ZImXx068Xw34eqSWvoMzr97xDxUwdlFgrKrkMKNoTDhA4afrZ/lwHdUbNzh6cht"
            + "jHW/IfIaMo3NldN/ihO851D399FMsWZW7YA7//RrWzBDiLvh+RfwkMOfEpbujy0G"
            + "73rO/Feed2MoVXvmuKBRpTNyFuBVvFDwIzBT4m/RaVf5m1pvprSk3lo43aumdN9f"
            + "NDETktVZ/CYaKlYK8rLcNBKJicM5+maiQSTa06XZXDMY84Q0xtCqJ/aUH4sa/z8j"
            + "KukVUSyUZDJk/O82B3NA4+CoP3Xyc9LAUKucUvoOmGt2JCw6goB/vqeZEg9Tli0Q"
            + "+aRer720QdVRkPVXKSshL2FoXHWUMaBF8r//zT6HbjTNQEdxbRcBNvkUXUHzITfl"
            + "YjQcEn+FGrF8+HVdXCKzSXSgu7mSouYyJmZh42spUFCa4j60Ks1fhQb2H1p72nJD"
            + "n1mC5sZkU68ITVu1juVl/L2WJPmWfasb1Ihnm9caJ/mEE/i1iKp7qaY9DPTw5hw4"
            + "3QplYWFv47UA/sOmnWwupRuPk7ISdimuUnih8OYR75rJ0z6OYexvj/2svx9/O5Mw"
            + "654jFF2hAq69jt7GJo6VZaeCRCAxEU7N97l3EjqaKJVrpIPQ+3yLmqHit/CWxImB"
            + "iIl3sW7MDEHgPdQy3QiZmAYNLQ0Te0ygcIHwtPyzhFoFmjbQwib2vxDqWaMQpUM1"
            + "/W96R/vbCjA7tfKYchImwAPCyRM5Je2FHewErG413kZct5tJ1JqkcjPsP7Q8kmgw"
            + "Ec5QNq1/PZOzL1ZLr6ryfA4gLBXa6bJmf43TUkdFYTvIYbvH2jp4wpAtA152YgPI"
            + "FL19/Tv0B3Bmb1qaK+FKiiQmYfVOm/J86i/L3b8Z3jj8dRWEBztaI/KazZ/ZVcs/"
            + "50bF9jH7y5+2uZxByjkM/kM/Ov9zIHbYdxLw2KHnHsGKTCooSSWvPupQLBGgkd6P"
            + "M9mgE6MntS+lk9ucpP5j1LXo5zlZaLSwrvSzE3/bbWJKsJuomhRbKeZ+qSYOWvPl"
            + "/1RqREyZHbSDKzVk39oxH9EI9EWKlCbrz5EHWiSv0+9HPczxbO3q+YfqcY8plPYX"
            + "BvgxHUeDR+LxaAEcVEX6wd2Pky8pVwxQydU4cEgohrgZnKhxxLAvCp5sb9kgqCrh"
            + "luvBsHpmiUSCi/r0PNXDgApvTrVS/Yv0jTpX9u9IWMmNMrnskdcP7tpEdkw8/dpf"
            + "RFLLgqwmNEhCggfbyT0JIUxf2rldKwd6N1wZozaBg1uKjNmAhJc1RxsABAEABAEA"
            + "BAEABAEABAEABAEABAEABAEABAEABAEABAEAAAAAAAAAMDwwITAJBgUrDgMCGgUA"
            + "BBSS2GOUxqv3IT+aesPrMPNn9RQ//gQUYhjCLPh/h2ULjh+1L2s3f5JIZf0CAWQA" + "AA==").getBytes());

    private static byte[] ksexpired = Base64
            .decode(("MIACAQMwgAYJKoZIhvcNAQcBoIAkgASCA+gwgDCABgkqhkiG9w0BBwGggCSABIID"
                    + "FzCCAxMwggMPBgsqhkiG9w0BDAoBAqCCArIwggKuMCgGCiqGSIb3DQEMAQMwGgQU"
                    + "+FPoYyKdBmCiikns2YwMZh4pPSkCAgQABIICgC5leUCbJ8w3O8KEUMRvHOA+Xhzm"
                    + "R5y7aHJHL1z3ZnoskDL4YW/r1TQ5AFliaH7e7kuA7NYOjv9HdFsZ9BekLkWPybit"
                    + "rcryLkPbRF+YdAXNkbGluukY0F8O4FP9n7FtfBd5uKitvOHZgHp3JAC9A+jYfayk"
                    + "ULfZRRGmzUys+D4czobY1tkCbQIb3kzR1kaqBownMkie+y5P56dRB2lJXpkpeilM"
                    + "H0PZvckG5jQw7ua4sVUkIzyDAZpiCtNmOF5nvyRwQRLWAHwn7Yid5e8w2A6xTq6P"
                    + "wko+2OdqHK/r/fmABREWf9GJa5Lb1QkUzITsWmPVskCUdl+VZzcYL8EV8cREH7DG"
                    + "sWuKyp8UJ0m3fiJEZHR2538Ydp6yp6R6/9DcGwxj20fO9FQnUanYcs6bDgwZ46UK"
                    + "blnbJAWGaChG3C9T6moXroLT7Mt2gxefW8RCds09EslhVTES01fmkovpcNuF/3U9"
                    + "ukGTCN49/mnuUpeMDrm8/BotuL+jkWBOnFy3RfEfsHyPzYflBb/M9T7Q8wsGuh0O"
                    + "oPecIsVvo4hgXX6R0fpYdPArMfuI5JaGopt07XRhbUuCqlEc4Q6DD46F/SVLk34Q"
                    + "Yaq76xwVplsa4QZZKNE6QTpApM61KpIKFxP3FzkqQIL4AKNb/mbSclr7L25aQmMw"
                    + "YiIgWOOaXlVh1U+4eZjqqVyYH5a6Y5e0EpMdMagvfuIA09b/Bp9LVnxQD6GmQgRC"
                    + "MRCaTr3wMQqEv92iTrj718rWmyYWTRArH/7mb4Ef250x2WgqjytuShBcL4McagQG"
                    + "NMpMBZLFAlseQYQDlgkGDMfcSZJQ34CeH7Uvy+lBYvFIGnb2o3hnHuZicOgxSjAj"
                    + "BgkqhkiG9w0BCRQxFh4UAG8AYwBzAHAAYwBsAGkAZQBuAHQwIwYJKoZIhvcNAQkV"
                    + "MRYEFO0W5oXdg6jY3vp316fMaEFzMEYpAAAAAAAAMIAGCSqGSIb3DQEHBqCAMIAC"
                    + "AQAwgAYJKoZIhvcNAQcBMCgGCiqGSIb3DQEMAQYwGgQU30rkEXMscb9M1uCfhs6v"
                    + "wV3eWCICAgQAoIAEggcYMs4iLKX/OQHK9oFu7l79H2zf0IlV58kAyjQG4yvadJnK"
                    + "Y6FOVLkwidcX33qRnMkGI1vidvRBbxnyH5+HVd3hVws/v3XBbZvhhX7A8loZZmye"
                    + "wFlHwT6TzIy/MJsz3Ev6EwoYBIID6HUrQhJiT/YPmiVhoWuaMw50YSbRGOUKwxEJ"
                    + "ggqnC4WOPxdP8xZbD+h3V1/W0KdbKyqFyXYVnfTgDisyEBnEn2BN3frl7vlucRsS"
                    + "ci0ZpJpkdlCyuF77KzPaq6/yAgPHAhABvjgiEPE11hsdDA635mDb1dRPoM6IFfzR"
                    + "n6JGZ7PEkKHdHudimx55eoUTJskXYaNcrPR2jlrxxX6tWV07m1G61kbgNIeuBdK6"
                    + "trJslSVPlli2YsTDQ2g+EmtDZc186nAYuQN03/TdSdhByPZxcT5nVs+xv1A3BdDX"
                    + "ow1HCyuGyBrAIEVoITE171csT78iPxNY9bukYy678XDxWkDQu7QMV8FeGEXec5sh"
                    + "NL/IUSYtzuPxaP5V/QALC0ybGxjIoxmdKS0zPxyekA+Cj8XjQBKVW2DPjWXWtAHR"
                    + "6lfWpwIgTwD0B7o59RVjKo/jrWRsH+RKfN17FXSKInTrm1gNHQPDCyIAv2luTSUa"
                    + "2qMRqH7/qivEWXbAWBz9dtEkqeuf/j698Rfie3QNtZ5qXmaVq1LBI0sduSJM+jHr"
                    + "uRtICzEzWMvSqVnW+3ejyHmpLc6zBYx8VwNuFy8IH+qtV0pDYyoNL96KBOJhX2hf"
                    + "DsH82SNf1CbIf8245YNmtzDby8h+3NXNIo8qAleLvgTgSN1tmS5kEJKw3M9/MYgE"
                    + "8XHGATAJB0E7uVRS1Ktr8R1w0hunautq7ylsw62zXdPp+6EsO0tMluCyWB0lMNAh"
                    + "uPiIMudNMA+O7NlCFQVTPxPxaRXg37dLm2XFy4ZnquKDuLvKkujdIwc9VBMER+MC"
                    + "6FiNtJw5Kq4PcARt1ulKGMknn38+3jSh3Dzg93XNMUx7lmqZCosYc4kf5X6dAWKd"
                    + "xBVNi3/hLejvWCCb55BncXiGMvs75L6b07IXcm3HTXZxCzzl5QtWM7XqpPVqbqhW"
                    + "wz03K4qko97YdD61oa8719SRjqBpbaW6RKIx5qGvAWYKg5usNorm/SsGg37zAfPa"
                    + "0LRoD22M5psU8MmH2E0iDDsf4sZDjeAY7LUGhgUGyyQ9t6hlEjD1Nhsxb9TSKNc+"
                    + "UBzCVRqjUWqImo8q7ZHhcDn64eXY4sSyQWWRP+TUfbpfgo+tb6NQvEhceU8sQlAh"
                    + "HGqi1/4kvc54O+dUFsRMJkXoobSRc053JgdUgaLQ22iI0nZSVVLgcR8/jTTvQhbv"
                    + "LRNES5vdoSUd+QiC83Hlx38uZtCgJ7HZfdnhYdaRIFIc7K1nqV+8ht6s7DdXK/JP"
                    + "8/QhtsLLfn1kies1/Xi+FeATef57jtBKh75yeBR5WFigEtSgFbRUNTLIrQQiDK07"
                    + "71bi+VA8QGH/dpUVNg0EggLZI0qqSXqD+2f2XnhK90fHl3RLZWX8xvU6sP6wGMLj"
                    + "R+OlW0Gsv0gWeVLbSKRmNyesl0lznC2yVAeoyLMSkU6YLYCuzQTzZ2dpjdPwkBOP"
                    + "7YhIIL7c1PWPGDLb35E/57Zd+I+dUdSX8SQyKzDgWyxyLGTaozkyaR3PK3XPKJNf"
                    + "t+RjfAJOtN3uSIjhpj90YL28p+kSlWxGRLM7FFDsS8nkcWQ113ZSfUnC5k5HmGmK"
                    + "FA5b6oVkxk98uxgK7jJ6h9wONZR9t8WbyfMYnjMgo5ZgGmKzoBRJ9rD0WiIJfHiR"
                    + "zrv9yejClIHdseps4rB96hqQjXDSk1f3e/5IQ6Zp++x7nIZy50C9HfnuDugigpNr"
                    + "IJS46o/86AgrBikc+CUoGLnu9OKvVCznFkwyz6ZzBdE3ITwHW4TXnlbkP888wax9"
                    + "lCKde+7/dBdUVwasgrU/F05MKCGqjWHIZ0po0owOTjMzkllqDtEmUdyUrGmLEmsA"
                    + "0tE8txLSi6TPmqL/th/7Os0B+7nyC3Ju8kBhmXVmoudcmWh2QH6VM6pegqETkCtA"
                    + "hGErIKKrdUSVNXy4izJFh9dgyYJKwm+X6XAaLWN1nlQlS08U0jR3vikDfJqUknxP"
                    + "Dg14TeC5Sgl2UjIpGX+XVxM8PV+2+WwvcwR0Nn1HFu99toZUD7FjkP6DR+XcHOhQ"
                    + "1tZZsutVPuyVJW9sTiYw48fIlYWDJXVESbLHDNN5TJD4NY9fhzfG3BYlex+YbbOx"
                    + "sCvmUNrrFwi1ZOGa/Z2ow5V7Kdf4rbWbyuV+0CCVJBcPTKageONp4AOaARpBMFg3"
                    + "QuTvzwEXmrTMbbrPY2o1GOS8ulwOp1VI8PcOyGwRpHXzpRZPv2u9gTmYgnfu2PcU"
                    + "F8NfHRFnPzFkO95KYFTYxZrg3vrU49IRJXqbjaeruQaKxPibxTDOsatJpWYAnw/s"
                    + "KuCHXrnUlw5RLeublCbUAAAAAAAAAAAAAAAAAAAAAAAAMD0wITAJBgUrDgMCGgUA"
                    + "BBRo3arw4fuHPsqvDnvA8Q/TLyjoRQQU3Xm6ZsAJT0/iLV7S3mKeme0FVGACAgQA" + "AAA=").getBytes());

    private final CAAdminSessionRemote caAdminSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(CAAdminSessionRemote.class);
    private final CaSessionRemote caSession = EjbRemoteHelper.INSTANCE.getRemoteSession(CaSessionRemote.class);
    private final CesecoreConfigurationProxySessionRemote cesecoreConfigurationProxySession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(CesecoreConfigurationProxySessionRemote.class, EjbRemoteHelper.MODULE_TEST);
    private final GlobalConfigurationSessionRemote globalConfigurationSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(GlobalConfigurationSessionRemote.class);
    private final RevocationSessionRemote revocationSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(RevocationSessionRemote.class);
    private final SignSessionRemote signSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(SignSessionRemote.class);
    private final EndEntityManagementSessionRemote endEntityManagementSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(EndEntityManagementSessionRemote.class);
    private final OcspResponseGeneratorSessionRemote ocspResponseGeneratorSession = EjbRemoteHelper.INSTANCE
            .getRemoteSession(OcspResponseGeneratorSessionRemote.class);

    @BeforeClass
    public static void beforeClass() throws CertificateException {
        // Install BouncyCastle provider
        CryptoProviderTools.installBCProviderIfNotAvailable();
    }

    public ProtocolOcspHttpTest() throws MalformedURLException, URISyntaxException {
        super("http", "ejbca", "publicweb/status/ocsp");
    }

    @Before
    public void setUp() throws Exception {
        CaTestCase.removeTestCA();
        CaTestCase.createTestCA();
        unknowncacert = (X509Certificate) CertTools.getCertfromByteArray(unknowncacertBytes);
        helper.reloadKeys();
        log.debug("httpReqPath=" + httpReqPath);
        assertTrue("This test can only be run on a full EJBCA installation.",
                ((HttpURLConnection) new URL(httpReqPath + '/').openConnection()).getResponseCode() == 200);
        cacert = (X509Certificate) CaTestCase.getTestCACert();
        caid = CaTestCase.getTestCAId();

        Map<String, String> config = new HashMap<String, String>();

        config.put("ocsp.nonexistingisgood", "false");
        config.put("ocsp.nonexistingisrevoked", "false");
        helper.alterConfig(config);
        helper.reloadKeys();
        GlobalOcspConfiguration ocspConfiguration = (GlobalOcspConfiguration) globalConfigurationSession
                .getCachedConfiguration(GlobalOcspConfiguration.OCSP_CONFIGURATION_ID);
        ocspConfiguration.setOcspDefaultResponderReference(CertTools.getSubjectDN(CaTestCase.getTestCACert()));
        globalConfigurationSession.saveConfiguration(admin, ocspConfiguration);
    }

    @After
    public void tearDown() throws Exception {
        CaTestCase.removeTestCA();
        removeDSACA();
        removeECDSACA();
    }

    public String getRoleName() {
        return this.getClass().getSimpleName();
    }

    @Test
    public void test01Access() throws Exception {
        super.test01Access();
    }

    /**
     * Tests ocsp message
     *
     * @throws Exception
     *           error
     */
    @Test
    public void test02OcspGood() throws Exception {
        log.trace(">test02OcspGood()");

        // find a CA (TestCA?) create a user and generate his cert
        // send OCSP req to server and get good response
        // change status of cert to bad status
        // send OCSP req and get bad status
        // (send crap message and get good error)

        // Get user and ocspTestCert that we know...
        loadUserCert(this.caid);
        this.helper.reloadKeys();

        this.helper.verifyStatusGood(this.caid, this.cacert, this.ocspTestCert.getSerialNumber());
        log.trace("<test02OcspGood()");
    }

    /**
     * Tests ocsp message
     *
     * @throws Exception
     *           error
     */
    @Test
    public void test03OcspRevoked() throws Exception {
        log.trace(">test03OcspRevoked()");
        loadUserCert(this.caid);
        // Now revoke the certificate and try again
        this.revocationSession.revokeCertificate(admin, this.ocspTestCert, null,
                RevokedCertInfo.REVOCATION_REASON_KEYCOMPROMISE, null);
        this.helper.reloadKeys();
        this.helper.verifyStatusRevoked(this.caid, this.cacert, this.ocspTestCert.getSerialNumber(),
                RevokedCertInfo.REVOCATION_REASON_KEYCOMPROMISE, null);
        log.trace("<test03OcspRevoked()");
    }

    @Test
    public void test04OcspUnknown() throws Exception {
        super.test04OcspUnknown();
    }

    @Test
    public void test05OcspUnknownCA() throws Exception {
        super.test05OcspUnknownCA();
    }

    @Test
    public void test06OcspSendWrongContentType() throws Exception {
        super.test06OcspSendWrongContentType();
    }

    @Test
    public void test07SignedOcsp() throws Exception {
        assertTrue("This test can only be run on a full EJBCA installation.",
                ((HttpURLConnection) new URL(httpReqPath + '/').openConnection()).getResponseCode() == 200);

        // find a CA (TestCA?) create a user and generate his cert
        // send OCSP req to server and get good response
        // change status of cert to bad status
        // send OCSP req and get bad status
        // (send crap message and get good error)
        try {
            KeyPair keys = createUserCert(caid);

            // And an OCSP request
            OCSPReqBuilder gen = new OCSPReqBuilder();
            gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert,
                    ocspTestCert.getSerialNumber()));
            Extension[] extensions = new Extension[1];
            extensions[0] = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false,
                    new DEROctetString("123456789".getBytes()));
            gen.setRequestExtensions(new Extensions(extensions));

            X509CertificateHolder chain[] = new X509CertificateHolder[2];
            chain[0] = new JcaX509CertificateHolder(ocspTestCert);
            chain[1] = new JcaX509CertificateHolder(cacert);
            gen.setRequestorName(chain[0].getSubject());
            OCSPReq req = gen.build(new JcaContentSignerBuilder("SHA1WithRSA")
                    .setProvider(BouncyCastleProvider.PROVIDER_NAME).build(keys.getPrivate()), chain);

            // First test with a signed OCSP request that can be verified
            Collection<Certificate> cacerts = new ArrayList<Certificate>();
            cacerts.add(cacert);
            CaCertificateCache certcache = CaCertificateCache.INSTANCE;
            certcache.loadCertificates(cacerts);
            X509Certificate signer = checkRequestSignature("127.0.0.1", req, certcache);
            assertNotNull(signer);
            assertEquals(ocspTestCert.getSerialNumber().toString(16), signer.getSerialNumber().toString(16));

            // Try with an unsigned request, we should get a SignRequestException
            req = gen.build();
            boolean caught = false;
            try {
                signer = checkRequestSignature("127.0.0.1", req, certcache);
            } catch (SignRequestException e) {
                caught = true;
            }
            assertTrue(caught);

            // sign with a keystore where the CA-certificate is not known
            KeyStore store = KeyStore.getInstance("PKCS12", "BC");
            ByteArrayInputStream fis = new ByteArrayInputStream(ks3);
            store.load(fis, "foo123".toCharArray());
            Certificate[] certs = KeyTools.getCertChain(store, "privateKey");
            chain[0] = new JcaX509CertificateHolder((X509Certificate) certs[0]);
            chain[1] = new JcaX509CertificateHolder((X509Certificate) certs[1]);
            PrivateKey pk = (PrivateKey) store.getKey("privateKey", "foo123".toCharArray());
            req = gen.build(new BufferingContentSigner(new JcaContentSignerBuilder("SHA1WithRSA").build(pk), 20480),
                    chain);
            // Send the request and receive a singleResponse, this response should
            // throw an SignRequestSignatureException
            caught = false;
            try {
                signer = checkRequestSignature("127.0.0.1", req, certcache);
            } catch (SignRequestSignatureException e) {
                caught = true;
            }
            assertTrue(caught);

            // sign with a keystore where the signing certificate has expired
            store = KeyStore.getInstance("PKCS12", "BC");
            fis = new ByteArrayInputStream(ksexpired);
            store.load(fis, "foo123".toCharArray());
            certs = KeyTools.getCertChain(store, "ocspclient");
            chain[0] = new JcaX509CertificateHolder((X509Certificate) certs[0]);
            chain[1] = new JcaX509CertificateHolder((X509Certificate) certs[1]);
            pk = (PrivateKey) store.getKey("ocspclient", "foo123".toCharArray());
            req = gen.build(new BufferingContentSigner(new JcaContentSignerBuilder("SHA1WithRSA").build(pk), 20480),
                    chain);
            // Send the request and receive a singleResponse, this response should
            // throw an SignRequestSignatureException
            caught = false;
            try {
                signer = checkRequestSignature("127.0.0.1", req, certcache);
            } catch (SignRequestSignatureException e) {
                caught = true;
            }
            assertTrue(caught);
        } finally {
            endEntityManagementSession.deleteUser(admin, "ocsptest");
        }

    } // test07SignedOcsp

    /**
     * Tests ocsp message
     *
     * @throws Exception error
     */
    @Test
    public void test08OcspEcdsaGood() throws Exception {
        assertTrue("This test can only be run on a full EJBCA installation.",
                ((HttpURLConnection) new URL(httpReqPath + '/').openConnection()).getResponseCode() == 200);
        final int ecdsacaid = "CN=OCSPECDSATEST".hashCode();
        final CAInfo caInfo = addECDSACA("CN=OCSPECDSATEST", "secp256r1");
        final X509Certificate ecdsacacert = (X509Certificate) caInfo.getCertificateChain().iterator().next();
        helper.reloadKeys();
        try {
            // Make user and ocspTestCert that we know...
            createUserCert(ecdsacaid);
            this.helper.verifyStatusGood(ecdsacaid, ecdsacacert, this.ocspTestCert.getSerialNumber());
        } finally {
            endEntityManagementSession.deleteUser(admin, "ocsptest");
            CryptoTokenTestUtils.removeCryptoToken(admin, caInfo.getCAToken().getCryptoTokenId());
        }
    } // test08OcspEcdsaGood

    /**
     * Tests ocsp message
     *
     * @throws Exception
     *           error
     */
    @Test
    public void test09OcspEcdsaImplicitlyCAGood() throws Exception {
        assertTrue("This test can only be run on a full EJBCA installation.",
                ((HttpURLConnection) new URL(httpReqPath + '/').openConnection()).getResponseCode() == 200);
        int ecdsacaid = "CN=OCSPECDSAIMPCATEST".hashCode();
        final CAInfo caInfo = addECDSACA("CN=OCSPECDSAIMPCATEST", "implicitlyCA");
        final X509Certificate ecdsacacert = (X509Certificate) caInfo.getCertificateChain().iterator().next();
        helper.reloadKeys();
        try {
            // Make user and ocspTestCert that we know...
            createUserCert(ecdsacaid);
            this.helper.verifyStatusGood(ecdsacaid, ecdsacacert, this.ocspTestCert.getSerialNumber());
        } finally {
            endEntityManagementSession.deleteUser(admin, "ocsptest");
            CryptoTokenTestUtils.removeCryptoToken(admin, caInfo.getCAToken().getCryptoTokenId());
        }
    } // test09OcspEcdsaImplicitlyCAGood

    @Test
    public void test10MultipleRequests() throws Exception {
        this.helper.reloadKeys();
        super.test10MultipleRequests();
    }

    @Test
    public void test11MalformedRequest() throws Exception {
        super.test11MalformedRequest();
    }

    @Test
    public void test12CorruptRequests() throws Exception {
        super.test12CorruptRequests();
    }

    /**
     * Just verify that a simple GET works.
     */
    @Test
    public void test13GetRequests() throws Exception {
        // See if the OCSP Servlet can read non-encoded requests
        final String plainReq = httpReqPath + '/' + resourceOcsp + '/'
                + "MGwwajBFMEMwQTAJBgUrDgMCGgUABBRBRfilzPB+Aevx0i1AoeKTkrHgLgQUFJw5gwk9BaEgsX3pzsRF9iso29ICCCzdx5N0v9XwoiEwHzAdBgkrBgEFBQcwAQIEECrZswo/a7YW+hyi5Sn85fs=";
        URL url = new URL(plainReq);
        log.info(url.toString()); // Dump the exact string we use for access
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        assertEquals("Response code did not match. ", 200, con.getResponseCode());
        assertNotNull(con.getContentType());
        assertTrue(con.getContentType().startsWith("application/ocsp-response"));
        OCSPResp response = new OCSPResp(IOUtils.toByteArray(con.getInputStream()));
        assertNotNull("Response should not be null.", response);
        assertTrue("Should not be considered malformed.",
                OCSPRespBuilder.MALFORMED_REQUEST != response.getStatus());
        final String dubbleSlashNonEncReq = httpReqPath + '/' + resourceOcsp + '/'
                + "MGwwajBFMEMwQTAJBgUrDgMCGgUABBRBRfilzPB%2BAevx0i1AoeKTkrHgLgQUFJw5gwk9BaEgsX3pzsRF9iso29ICCAvB//HJyKqpoiEwHzAdBgkrBgEFBQcwAQIEEOTzT2gv3JpVva22Vj8cuKo%3D";
        url = new URL(dubbleSlashNonEncReq);
        log.info(url.toString()); // Dump the exact string we use for access
        con = (HttpURLConnection) url.openConnection();
        assertEquals("Response code did not match. ", 200, con.getResponseCode());
        assertNotNull(con.getContentType());
        assertTrue(con.getContentType().startsWith("application/ocsp-response"));
        response = new OCSPResp(IOUtils.toByteArray(con.getInputStream()));
        assertNotNull("Response should not be null.", response);
        assertTrue("Should not be concidered malformed.",
                OCSPRespBuilder.MALFORMED_REQUEST != response.getStatus());
        // An OCSP request, ocspTestCert is already created in earlier tests
        loadUserCert(this.caid);
        this.helper.reloadKeys();
        this.helper.verifyStatusGood(this.caid, this.cacert, this.ocspTestCert.getSerialNumber());
    }

    @Test
    public void test14CorruptGetRequests() throws Exception {
        super.test14CorruptGetRequests();
    }

    @Test
    public void test15MultipleGetRequests() throws Exception {
        super.test15MultipleGetRequests();
    }

    /**
     * Tests ocsp message
     *
     * @throws Exception
     *           error
     */
    @Test
    public void test16OcspDsaGood() throws Exception {
        assertTrue("This test can only be run on a full EJBCA installation.",
                ((HttpURLConnection) new URL(httpReqPath + '/').openConnection()).getResponseCode() == 200);

        int dsacaid = DSA_DN.hashCode();
        X509Certificate ecdsacacert = addDSACA(DSA_DN, "DSA1024");
        helper.reloadKeys();

        // Make user and ocspTestCert that we know...
        createUserCert(dsacaid);

        this.helper.verifyStatusGood(dsacaid, ecdsacacert, this.ocspTestCert.getSerialNumber());
    } // test16OcspDsaGood

    /**
     * Verify that Internal OCSP responses are signed by CA signing key.
     */
    @Test
    public void test17OCSPResponseSignature() throws Exception {

        // Get user and ocspTestCert that we know...
        loadUserCert(caid);
        this.helper.reloadKeys();
        // And an OCSP request
        OCSPReqBuilder gen = new OCSPReqBuilder();
        gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert,
                ocspTestCert.getSerialNumber()));
        Extension[] extensions = new Extension[1];
        extensions[0] = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false,
                new DEROctetString("123456789".getBytes()));
        gen.setRequestExtensions(new Extensions(extensions));
        OCSPReq req = gen.build();

        // POST the OCSP request
        URL url = new URL(httpReqPath + '/' + resourceOcsp);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        // we are going to do a POST
        con.setDoOutput(true);
        con.setRequestMethod("POST");

        // POST it
        con.setRequestProperty("Content-Type", "application/ocsp-request");
        OutputStream os = con.getOutputStream();
        os.write(req.getEncoded());
        os.close();
        assertTrue("HTTP error", con.getResponseCode() == 200);

        // Some appserver (Weblogic) responds with
        // "application/ocsp-response; charset=UTF-8"
        assertNotNull("No Content-Type in reply.", con.getContentType());
        assertTrue(con.getContentType().startsWith("application/ocsp-response"));
        OCSPResp response = new OCSPResp(IOUtils.toByteArray(con.getInputStream()));
        assertTrue("Response status not the expected.", response.getStatus() != 200);

        BasicOCSPResp brep = (BasicOCSPResp) response.getResponseObject();
        boolean verify = brep
                .isSignatureValid(new JcaContentVerifierProviderBuilder().build(cacert.getPublicKey()));
        assertTrue("Signature verification", verify);
    }

    /**
     * Verify OCSP response for a malicious request. Uses nonsense payload.
     *
     * HTTP Content-length: 1000 byte ASN1 sequence length: 199995 byte Payload
     * size: 200000 byte (not including HTTP header)
     */
    @Test
    public void test18MaliciousOcspRequest() throws Exception {
        log.trace(">test18MaliciousOcspRequest");
        int i = 0;
        // Construct the fake data.
        byte data[] = new byte[LimitLengthASN1Reader.MAX_REQUEST_SIZE * 2];
        // The first byte indicate that this is a sequence. Necessary to past
        // the first test as an accepted OCSP object.
        data[0] = (byte) BERTags.SEQUENCE;
        // The second byte indicates the number if the following bytes are more
        // than can be represented by one byte and will be represented by 3
        // bytes instead.
        data[1] = (byte) 0x83;
        // The third through the forth bytes are the number of the following
        // bytes. (0x030D3B = 199995)
        data[2] = (byte) 0x03; // MSB
        data[3] = (byte) 0x0D;
        data[4] = (byte) 0x3B; // LSB
        // Fill the rest of the array with some fake data.
        for (i = 5; i < data.length; i++) {
            data[i] = (byte) i;
        }
        // Create the HTTP header
        String path = "/ejbca/" + resourceOcsp;
        String headers = "POST " + path + " HTTP/1.1\r\n" + "Host: " + httpHost + "\r\n"
                + "Content-Type: application/ocsp-request\r\n" + "Content-Length: 1000\r\n" + "\r\n";
        // Merge the HTTP headers and the raw data into one package.
        byte input[] = concatByteArrays(headers.getBytes(), data);
        // Create the socket.
        Socket socket = new Socket(InetAddress.getByName(httpHost), Integer.parseInt(httpPort));
        OutputStream os = socket.getOutputStream();
        try {
            // Send data byte for byte.
            try {
                os.write(input);
            } catch (IOException e) {
                log.info("Socket threw an IOException.", e);
                // Windows throws an IOException when trying to write more bytes to
                // the server than it should. JBoss on Linux does not.
                // assertTrue("Tried to write more than it should to the server (>1000), "+i, i > 1000);
                return;
            }
            /* Note that an Apache proxy interprets this as two requests in the same session (where the second one is bad):
            HTTP/1.1 200 OK
            Date: Thu, 27 Mar 2014 16:13:24 GMT
            Server: Apache/2.4.6 (Unix) OpenSSL/1.0.1e
            Content-Type: application/ocsp-response
            Content-Length: 5
                
            0
            HTTP/1.1 400 Bad Request
            Date: Thu, 27 Mar 2014 16:13:24 GMT
            Server: Apache/2.4.6 (Unix) OpenSSL/1.0.1e
            Content-Length: 226
            Connection: close
            Content-Type: text/html; charset=iso-8859-1
                
            <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
            <html><head>
            <title>400 Bad Request</title>
            </head><body>
            <h1>Bad Request</h1>
            <p>Your browser sent a request that this server could not understand.<br />
            </p>
            </body></html>
                
            But since the response is ANS1 encoded, the response is still correctly parsed even though we provide 420 bytes extra.
             */
            // Reading the response.
            InputStream ins = socket.getInputStream();
            byte ret[] = new byte[1024];
            int len = ins.read(ret);
            assertTrue("Could not read response.", len != -1);
            // Removing the HTTP headers. The HTTP headers end at the first occurrence of "\r\n\r\n".
            for (i = 3; i < len; i++) {
                if ((ret[i] == 0x0A) && (ret[i - 1] == 0x0D) && (ret[i - 2] == 0x0A) && (ret[i - 3] == 0x0D)) {
                    break;
                }
            }
            log.info("response headers:  " + new String(ret, 0, i));
            int start = i + 1;
            byte respa[] = new byte[len - start];
            for (i = start; i < len; i++) {
                respa[i - start] = ret[i];
            }
            log.info("response contains: " + respa.length + " bytes.");
            log.info("response bytes:    " + Hex.toHexString(respa));
            log.info("response as string:" + new String(respa));
            // Reading the response as a OCSPResp. When the input data array is
            // longer than allowed the OCSP response will return as an internal
            // error.
            OCSPResp response = new OCSPResp(respa);
            assertEquals("Incorrect response status.", OCSPRespBuilder.INTERNAL_ERROR, response.getStatus());
        } finally {
            os.close();
            socket.close();
        }
        log.trace("<test18MaliciousOcspRequest");
    }

    /**
     * Verify OCSP response for a malicious request. Uses nonsense payload.
     *
     * HTTP Content-length: 200000 byte ASN1 sequence length: 9996 byte Payload
     * size: 200000 byte (not including HTTP header)
     */
    @Test
    public void test19MaliciousOcspRequest() throws Exception {
        log.trace(">test19MaliciousOcspRequest");
        int i = 0;
        // Construct the fake data.
        byte data[] = new byte[LimitLengthASN1Reader.MAX_REQUEST_SIZE * 2];
        // The first byte indicate that this is a sequence. Necessary to past
        // the first test as an accepted OCSP object.
        data[0] = (byte) BERTags.SEQUENCE;
        // The second byte indicates the number of the following bytes are more
        // than can be represented by one byte and will be represented by 2
        // bytes instead.
        data[1] = (byte) 0x82;
        // The third through the forth bytes are the number of the following
        // bytes. (0x270C = 9996)
        data[2] = (byte) 0x27; // MSB
        data[3] = (byte) 0x0C; // LSB
        // Fill the rest of the array with some fake data.
        for (i = 4; i < data.length; i++) {
            data[i] = (byte) i;
        }
        // Create the HTTP header
        String path = "/ejbca/" + resourceOcsp;
        String headers = "POST " + path + " HTTP/1.1\r\n" + "Host: " + httpHost + "\r\n"
                + "Content-Type: application/ocsp-request\r\n" + "Content-Length: 200000\r\n" + "\r\n";
        // Merge the HTTP headers and the raw data into one package.
        byte input[] = concatByteArrays(headers.getBytes(), data);
        // Create the socket.
        Socket socket = new Socket(InetAddress.getByName(httpHost), Integer.parseInt(httpPort));
        // Send data byte for byte.
        OutputStream os = socket.getOutputStream();
        try {
            os.write(input);
        } catch (IOException e) {
            log.info("Socket threw an IOException.", e);
        }
        // Reading the response.
        InputStream ins = socket.getInputStream();
        byte ret[] = new byte[1024];
        ins.read(ret);
        socket.close();
        // Removing the HTTP headers. The HTTP headers end at the last
        // occurrence of "\r\n".
        for (i = ret.length - 1; i > 0; i--) {
            if ((ret[i] == 0x0A) && (ret[i - 1] == 0x0D)) {
                break;
            }
        }
        int start = i + 1;
        byte respa[] = new byte[ret.length - start];
        for (i = start; i < ret.length; i++) {
            respa[i - start] = ret[i];
        }
        log.info("response contains: " + respa.length + " bytes.");
        // Reading the response as a OCSPResp.
        OCSPResp response = new OCSPResp(respa);
        assertEquals("Incorrect response status.", OCSPRespBuilder.MALFORMED_REQUEST, response.getStatus());
        log.trace("<test19MaliciousOcspRequest");
    }

    /**
     * Verify OCSP response for a malicious request where the POST data starts
     * with a proper OCSP request.
     */
    @Test
    public void test20MaliciousOcspRequest() throws Exception {
        log.trace(">test20MaliciousOcspRequest");
        // Start by sending a valid OCSP requests so we know the helpers work
        byte validOcspReq[] = getValidOcspRequest();
        OCSPResp response = sendRawRequestToOcsp(validOcspReq.length, validOcspReq, false);
        assertEquals("Incorrect response status.", OCSPRespBuilder.SUCCESSFUL, response.getStatus());
        // Try sending a valid request and then keep sending some more data.
        byte[] buf = new byte[LimitLengthASN1Reader.MAX_REQUEST_SIZE * 2];
        Arrays.fill(buf, (byte) 123);
        buf = concatByteArrays(validOcspReq, buf);
        // This should return an error because we only allow content length of 100000 bytes
        response = sendRawRequestToOcsp(buf.length, buf, false);
        assertEquals("Incorrect response status.", OCSPRespBuilder.MALFORMED_REQUEST, response.getStatus());
        // Now try with a fake HTTP content-length header
        try {
            response = sendRawRequestToOcsp(validOcspReq.length, buf, false);
            // When sending a large request body with a too short content-length the serves sees this as two streaming
            // requests. The first request will be read and processed by EJBCA normally and sent back, but the
            // second one will not be a valid request so the server will send back an error.
            // Glassfish actually sends back a "400 Bad request". Our reading code in sendRawRequestToOcsp
            // does not handle multiple streaming responses so it will barf on the second one.
            // This is different for JBoss and Glassfish though, with JBoss we will get a IOException trying
            // to read the response, while for Glassfish we will get the response with 0 bytes from the 400 response

            // Only glassfish will come here, with a non-null response, but of length 2(/r/n?). JBoss (4, 5, 6) will go to the
            // IOException below
            try {
                byte[] encoded = response.getEncoded();
                if ((encoded != null) && (encoded.length > 2)) {
                    // Actually this error message is wrong, since it is our client that does not handle streaming responses
                    // where the first response should be good.
                    fail("Was able to send a lot of data with a fake HTTP Content-length without any error.");
                }
            } catch (NullPointerException npe) { // NOPMD
                // the response.getEncoded() can give NPE, in some versions of BC, if it was not created with correct input
            }
        } catch (IOException e) {
        }
        // Try sneaking through a payload that is just under the limit. The
        // responder will answer politely, but log a warning.
        buf = new byte[LimitLengthASN1Reader.MAX_REQUEST_SIZE - validOcspReq.length];
        Arrays.fill(buf, (byte) 123);
        buf = concatByteArrays(validOcspReq, buf);
        response = sendRawRequestToOcsp(buf.length, buf, false);
        assertEquals("Server accepted malicious request. (This might be a good thing!)", OCSPRespBuilder.SUCCESSFUL,
                response.getStatus());
        log.trace("<test20MaliciousOcspRequest");
    }

    /**
     * Tests ocsp message
     *
     * @throws Exception
     *           error
     */
    @Test
    public void test50OcspUnknownMayBeGood() throws Exception {
        log.trace(">test50OcspUnknownMayBeGood()");
        loadUserCert(this.caid);
        // An OCSP request for an unknown certificate (not exist in db)
        this.helper.verifyStatusUnknown(this.caid, this.cacert, new BigInteger("1"));
        final String bad1 = "Bad";
        final String bad2 = "Ugly";
        final String good1 = "Good";
        final String good2 = "Beautiful";
        {
            final Map<String, String> map = new HashMap<String, String>();
            map.put(OcspConfiguration.NONE_EXISTING_IS_GOOD, "true");
            map.put(OcspConfiguration.NONE_EXISTING_IS_BAD_URI + '1', ".*" + bad1 + "$");
            map.put(OcspConfiguration.NONE_EXISTING_IS_BAD_URI + '2', ".*" + bad2 + "$");
            map.put(OcspConfiguration.NONE_EXISTING_IS_GOOD_URI + '1', ".*" + good1 + "$");
            map.put(OcspConfiguration.NONE_EXISTING_IS_GOOD_URI + '2', ".*" + good2 + "$");
            this.helper.alterConfig(map);
        }
        this.helper.reloadKeys();
        this.helper.verifyStatusGood(this.caid, this.cacert, new BigInteger("1"));
        this.helper.setURLEnding(bad1);
        this.helper.verifyStatusUnknown(this.caid, this.cacert, new BigInteger("1"));
        this.helper.setURLEnding(bad2);
        this.helper.verifyStatusUnknown(this.caid, this.cacert, new BigInteger("1"));
        {
            final Map<String, String> map = new HashMap<String, String>();
            map.put(OcspConfiguration.NONE_EXISTING_IS_GOOD, "false");
            this.helper.alterConfig(map);
        }
        this.helper.setURLEnding("");
        this.helper.verifyStatusUnknown(this.caid, this.cacert, new BigInteger("1"));
        this.helper.setURLEnding(good1);
        this.helper.verifyStatusGood(this.caid, this.cacert, new BigInteger("1"));
        this.helper.setURLEnding(good2);
        this.helper.verifyStatusGood(this.caid, this.cacert, new BigInteger("1"));
        log.trace("<test50OcspUnknownMayBeGood()");
    }

    /**
     * This test tests the feature of extensions of setting a '*' in front of the value in ocsp.extensionoid
     * forces that extension to be used for all requests.
     * 
     * @throws Exception
     */
    @Test
    public void testUseAlwaysExtensions() throws Exception {
        log.trace(">testUseAlwaysExtensions");
        final String EXTENSION_OID = "ocsp.extensionoid";
        final String EXTENSION_CLASS = "ocsp.extensionclass";
        final String oldOidValue = cesecoreConfigurationProxySession.getConfigurationValue(EXTENSION_OID);
        final String oldClass = cesecoreConfigurationProxySession.getConfigurationValue(EXTENSION_CLASS);
        try {
            cesecoreConfigurationProxySession.setConfigurationValue(EXTENSION_OID,
                    "*" + OcspCertHashExtension.CERT_HASH_OID);
            cesecoreConfigurationProxySession.setConfigurationValue(EXTENSION_CLASS,
                    "org.ejbca.core.protocol.ocsp.extension.certhash.OcspCertHashExtension");
            ocspResponseGeneratorSession.reloadOcspExtensionsCache();

            // An OCSP request, ocspTestCert is already created in earlier tests
            OCSPReqBuilder gen = new OCSPReqBuilder();
            loadUserCert(this.caid);
            this.helper.reloadKeys();
            gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert,
                    ocspTestCert.getSerialNumber()));
            OCSPReq req = gen.build();
            BasicOCSPResp response = helper.sendOCSPGet(req.getEncoded(), null, OCSPRespBuilder.SUCCESSFUL, 200);
            if (response == null) {
                throw new Exception("Could not retrieve response, test could not continue.");
            }
            Extension responseExtension = response
                    .getExtension(new ASN1ObjectIdentifier(OcspCertHashExtension.CERT_HASH_OID));
            assertNotNull("No extension sent with reply", responseExtension);
        } finally {
            cesecoreConfigurationProxySession.setConfigurationValue(EXTENSION_OID, oldOidValue);
            cesecoreConfigurationProxySession.setConfigurationValue(EXTENSION_CLASS, oldClass);
            ocspResponseGeneratorSession.reloadOcspExtensionsCache();
            log.trace("<testUseAlwaysExtensions");
        }
    }

    /**
     * Tests ocsp message
     *
     * @throws Exception
     *           error
     */
    @Test
    public void test60OcspUnknownIsRevoked() throws Exception {
        log.trace(">test60OcspUnknownIsRevoked()");

        loadUserCert(this.caid);
        // An OCSP request for an unknown certificate (not exist in db)
        this.helper.verifyStatusUnknown(this.caid, this.cacert, new BigInteger("1"));
        final String bad1 = "Bad";
        final String bad2 = "Ugly";
        final String good1 = "Good";
        final String good2 = "Beautiful";
        final String revoked1 = "Revoked";
        final String revoked2 = "Denied";
        {
            final Map<String, String> map = new HashMap<String, String>();
            map.put(OcspConfiguration.NONE_EXISTING_IS_REVOKED, "true");
            map.put(OcspConfiguration.NONE_EXISTING_IS_BAD_URI + '1', ".*" + bad1 + "$");
            map.put(OcspConfiguration.NONE_EXISTING_IS_BAD_URI + '2', ".*" + bad2 + "$");
            map.put(OcspConfiguration.NONE_EXISTING_IS_GOOD_URI + '1', ".*" + good1 + "$");
            map.put(OcspConfiguration.NONE_EXISTING_IS_GOOD_URI + '2', ".*" + good2 + "$");
            map.put(OcspConfiguration.NONE_EXISTING_IS_REVOKED_URI + '1', ".*" + revoked1 + "$");
            map.put(OcspConfiguration.NONE_EXISTING_IS_REVOKED_URI + '2', ".*" + revoked2 + "$");
            this.helper.alterConfig(map);
        }
        this.helper.reloadKeys();
        this.helper.verifyStatusRevoked(this.caid, this.cacert, new BigInteger("1"), CRLReason.certificateHold,
                new Date(0));
        this.helper.setURLEnding(bad1);
        this.helper.verifyStatusUnknown(this.caid, this.cacert, new BigInteger("1"));
        this.helper.setURLEnding(bad2);
        this.helper.verifyStatusUnknown(this.caid, this.cacert, new BigInteger("1"));
        this.helper.setURLEnding(good1);
        this.helper.verifyStatusGood(this.caid, this.cacert, new BigInteger("1"));
        this.helper.setURLEnding(good2);
        this.helper.verifyStatusGood(this.caid, this.cacert, new BigInteger("1"));
        {
            final Map<String, String> map = new HashMap<String, String>();
            map.put(OcspConfiguration.NONE_EXISTING_IS_REVOKED, "false");
            this.helper.alterConfig(map);
        }
        this.helper.setURLEnding("");
        this.helper.verifyStatusUnknown(this.caid, this.cacert, new BigInteger("1"));
        this.helper.setURLEnding(revoked1);
        this.helper.verifyStatusRevoked(this.caid, this.cacert, new BigInteger("1"), CRLReason.certificateHold,
                new Date(0));
        this.helper.setURLEnding(revoked2);
        this.helper.verifyStatusRevoked(this.caid, this.cacert, new BigInteger("1"), CRLReason.certificateHold,
                new Date(0));

        log.trace("<test60OcspUnknownIsRevoked()");
    }

    /**
     * This test tests that the OCSP response contains the extension "id-pkix-ocsp-extended-revoke" in case the 
     * status of an unknown cert is returned as revoked.
     * 
     * @throws Exception
     */
    @Test
    public void testExtendedRevokedExtension() throws Exception {

        OCSPReqBuilder gen = new OCSPReqBuilder();
        gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert, new BigInteger("1")));
        OCSPReq req = gen.build();
        BasicOCSPResp response = helper.sendOCSPGet(req.getEncoded(), null, OCSPRespBuilder.SUCCESSFUL, 200);
        assertNotNull("Could not retrieve response, test could not continue.", response);
        assertTrue(response.getResponses()[0].getCertStatus() instanceof UnknownStatus);
        // RFC 6960: id-pkix-ocsp-extended-revoke OBJECT IDENTIFIER ::= {id-pkix-ocsp 9}
        Extension responseExtension = response
                .getExtension(new ASN1ObjectIdentifier(OCSPObjectIdentifiers.id_pkix_ocsp + ".9"));
        assertNull("Wrong extension sent with reply", responseExtension);

        final Map<String, String> map = new HashMap<String, String>();
        map.put(OcspConfiguration.NONE_EXISTING_IS_REVOKED, "true");
        this.helper.alterConfig(map);

        gen = new OCSPReqBuilder();
        gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert, new BigInteger("1")));
        req = gen.build();
        response = helper.sendOCSPGet(req.getEncoded(), null, OCSPRespBuilder.SUCCESSFUL, 200);
        assertNotNull("Could not retrieve response, test could not continue.", response);
        assertTrue(response.getResponses()[0].getCertStatus() instanceof RevokedStatus);
        responseExtension = response
                .getExtension(new ASN1ObjectIdentifier(OCSPObjectIdentifiers.id_pkix_ocsp + ".9"));
        assertNotNull("No extension sent with reply", responseExtension);
        assertEquals(DERNull.INSTANCE, responseExtension.getParsedValue());
    }

    /**
     * This test tests that the OCSP response contains the extension "id_pkix_ocsp_archive_cutoff" if "ocsp.expiredcert.retentionperiod" 
     * is set in the condfiguration file
     * 
     * @throws Exception
     */
    @Test
    public void testExpiredCertArchiveCutoffExtension() throws Exception {

        final String username = "expiredCertUsername";
        String cpname = "ValidityCertProfile";
        String eepname = "ValidityEEProfile";
        X509Certificate xcert = null;

        CertificateProfileSessionRemote certProfSession = EjbRemoteHelper.INSTANCE
                .getRemoteSession(CertificateProfileSessionRemote.class);
        EndEntityProfileSessionRemote eeProfSession = EjbRemoteHelper.INSTANCE
                .getRemoteSession(EndEntityProfileSessionRemote.class);

        try {
            if (certProfSession.getCertificateProfile(cpname) == null) {
                final CertificateProfile cp = new CertificateProfile(
                        CertificateProfileConstants.CERTPROFILE_FIXED_ENDUSER);
                cp.setAllowValidityOverride(true);
                try {
                    certProfSession.addCertificateProfile(admin, cpname, cp);
                } catch (CertificateProfileExistsException e) {
                    log.error("Certificate profile exists: ", e);
                }
            }
            final int cpId = certProfSession.getCertificateProfileId(cpname);
            if (eeProfSession.getEndEntityProfile(eepname) == null) {
                final EndEntityProfile eep = new EndEntityProfile(true);
                eep.setValue(EndEntityProfile.AVAILCERTPROFILES, 0, "" + cpId);
                try {
                    eeProfSession.addEndEntityProfile(admin, eepname, eep);
                } catch (EndEntityProfileExistsException e) {
                    log.error("Could not create end entity profile.", e);
                }
            }
            final int eepId = eeProfSession.getEndEntityProfileId(eepname);

            if (!endEntityManagementSession.existsUser(username)) {
                endEntityManagementSession.addUser(admin, username, "foo123", "CN=expiredCertUsername", null,
                        "ocsptest@anatom.se", false, eepId, cpId, EndEntityTypes.ENDUSER.toEndEntityType(),
                        SecConst.TOKEN_SOFT_PEM, 0, caid);
                log.debug("created user: expiredCertUsername, foo123, CN=expiredCertUsername");
            } else {
                log.debug("User expiredCertUsername already exists.");
                EndEntityInformation userData = new EndEntityInformation(username, "CN=expiredCertUsername", caid,
                        null, "ocsptest@anatom.se", EndEntityConstants.STATUS_NEW,
                        EndEntityTypes.ENDUSER.toEndEntityType(), eepId, cpId, null, null, SecConst.TOKEN_SOFT_PEM,
                        0, null);
                userData.setPassword("foo123");
                endEntityManagementSession.changeUser(admin, userData, false);
                log.debug("Reset status to NEW");
            }

            // Generate certificate for the new user
            KeyPair keys = KeyTools.genKeys("512", "RSA");
            long now = (new Date()).getTime();
            long notAfter = now + 1000;
            xcert = (X509Certificate) signSession.createCertificate(admin, username, "foo123",
                    new PublicKeyWrapper(keys.getPublic()), -1, new Date(), new Date(notAfter));
            assertNotNull("Failed to create new certificate", xcert);

            Thread.sleep(2000L); // wait for the certificate to expire

            // -------- Testing with default config value

            OCSPReqBuilder gen = new OCSPReqBuilder();
            gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert,
                    xcert.getSerialNumber()));
            OCSPReq req = gen.build();
            BasicOCSPResp response = helper.sendOCSPGet(req.getEncoded(), null, OCSPRespBuilder.SUCCESSFUL, 200);
            assertNotNull("Could not retrieve response, test could not continue.", response);
            SingleResp resp = response.getResponses()[0];
            Extension singleExtension = resp.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_archive_cutoff);
            assertNotNull("No extension sent with reply", singleExtension);

            ASN1GeneralizedTime extvalue = ASN1GeneralizedTime.getInstance(singleExtension.getParsedValue());
            long expectedValue = (new Date()).getTime() - (31536000L * 1000);
            long actualValue = extvalue.getDate().getTime();
            long diff = expectedValue - actualValue;
            assertTrue("Wrong archive cutoff value.", diff < 60000);

            // -------- Send a request where id_pkix_ocsp_archive_cutoff SHOULD NOT be used
            // set ocsp configuration
            Map<String, String> map = new HashMap<String, String>();
            map.put(OcspConfiguration.EXPIREDCERT_RETENTIONPERIOD, "-1");
            this.helper.alterConfig(map);

            gen = new OCSPReqBuilder();
            gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert,
                    xcert.getSerialNumber()));
            req = gen.build();
            response = helper.sendOCSPGet(req.getEncoded(), null, OCSPRespBuilder.SUCCESSFUL, 200);
            assertNotNull("Could not retrieve response, test could not continue.", response);
            resp = response.getResponses()[0];
            singleExtension = resp.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_archive_cutoff);
            assertNull("The wrong extension was sent with reply", singleExtension);

            // ------------ Send a request where id_pkix_ocsp_archive_cutoff SHOULD be used
            // set ocsp configuration
            map = new HashMap<String, String>();
            map.put(OcspConfiguration.EXPIREDCERT_RETENTIONPERIOD, "63072000"); // 2 years
            this.helper.alterConfig(map);

            gen = new OCSPReqBuilder();
            gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert,
                    xcert.getSerialNumber()));
            req = gen.build();
            response = helper.sendOCSPGet(req.getEncoded(), null, OCSPRespBuilder.SUCCESSFUL, 200);
            assertNotNull("Could not retrieve response, test could not continue.", response);
            resp = response.getResponses()[0];
            singleExtension = resp.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_archive_cutoff);
            assertNotNull("No extension sent with reply", singleExtension);

            extvalue = ASN1GeneralizedTime.getInstance(singleExtension.getParsedValue());
            expectedValue = (new Date()).getTime() - (63072000L * 1000);
            actualValue = extvalue.getDate().getTime();
            diff = expectedValue - actualValue;
            assertTrue("Wrong archive cutoff value.", diff < 60000);

        } finally {
            endEntityManagementSession.revokeAndDeleteUser(admin, username, CRLReason.unspecified);
            eeProfSession.removeEndEntityProfile(admin, eepname);
            certProfSession.removeCertificateProfile(admin, cpname);
        }
    }

    /**
     * This test tests that the OCSP response for a status unknown contains the header "cache-control" with the value "no-cache, must-revalidate"
     * 
     * @throws Exception
     */
    @Test
    public void testUnknownStatusCacheControlHeader() throws Exception {

        // set ocsp configuration
        Map<String, String> map = new HashMap<String, String>();
        map.put(OcspConfiguration.UNTIL_NEXT_UPDATE, "1");
        this.helper.alterConfig(map);

        OCSPReqBuilder gen = new OCSPReqBuilder();
        gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert, new BigInteger("1")));
        OCSPReq req = gen.build();

        String sBaseURL = httpReqPath + '/' + resourceOcsp;
        String urlEnding = "";
        String b64 = new String(Base64.encode(req.getEncoded(), false));
        //String urls = URLEncoder.encode(b64, "UTF-8");    // JBoss/Tomcat will not accept escaped '/'-characters by default
        URL url = new URL(sBaseURL + '/' + b64 + urlEnding);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        if (con.getResponseCode() != 200) {
            log.info("URL when request gave unexpected result: " + url.toString() + " Message was: "
                    + con.getResponseMessage());
        }
        assertEquals("Response code did not match. ", 200, con.getResponseCode());
        assertNotNull(con.getContentType());
        assertTrue(con.getContentType().startsWith("application/ocsp-response"));

        assertNotNull("No Cache-Control in reply.", con.getHeaderField("Cache-Control"));
        assertEquals("no-cache, must-revalidate", con.getHeaderField("Cache-Control"));

        // Create a GET request using Nonce extension, in this case we should have no cache-control header
        gen = new OCSPReqBuilder();
        gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert, new BigInteger("1")));
        Extension[] extensions = new Extension[1];
        extensions[0] = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false,
                new DEROctetString("123456789".getBytes()));
        gen.setRequestExtensions(new Extensions(extensions));
        req = gen.build();
        b64 = new String(Base64.encode(req.getEncoded(), false));
        url = new URL(sBaseURL + '/' + b64 + urlEnding);
        con = (HttpURLConnection) url.openConnection();
        if (con.getResponseCode() != 200) {
            log.info("URL when request gave unexpected result: " + url.toString() + " Message was: "
                    + con.getResponseMessage());
        }
        assertEquals("Response code did not match. ", 200, con.getResponseCode());
        assertNotNull(con.getContentType());
        assertTrue(con.getContentType().startsWith("application/ocsp-response"));
        OCSPResp response = new OCSPResp(IOUtils.toByteArray(con.getInputStream()));
        BasicOCSPResp brep = (BasicOCSPResp) response.getResponseObject();
        byte[] noncerep = brep.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce).getExtnValue().getEncoded();
        // Make sure we have a nonce in the response, we should have since we sent one in the request
        assertNotNull("Response should have nonce since we sent a nonce in the request", noncerep);
        ASN1InputStream ain = new ASN1InputStream(noncerep);
        ASN1OctetString oct = ASN1OctetString.getInstance(ain.readObject());
        ain.close();
        assertEquals("Response Nonce was not the same as the request Nonce, it must be", "123456789",
                new String(oct.getOctets()));
        assertNull(
                "Cache-Control in reply although we used Nonce in the request. Responses with Nonce should not have a Cache-control header.",
                con.getHeaderField("Cache-Control"));
    }

    /**
     * This test tests that the OCSP response contains is signed by the preferred signature algorithm specified in the request.
     * 
     * @throws Exception
    */
    @Test
    @Deprecated // This test verifies legacy behavior from EJBCA 6.1.0 and should be removed when we no longer need to support it
    public void testSigAlgExtensionLegacy() throws Exception {
        loadUserCert(this.caid);

        // Try sending a request where the preferred signature algorithm in the extension is expected to be used to sign the response.

        // set ocsp configuration
        Map<String, String> map = new HashMap<String, String>();
        map.put("ocsp.signaturealgorithm",
                AlgorithmConstants.SIGALG_SHA256_WITH_RSA + ";" + AlgorithmConstants.SIGALG_SHA1_WITH_RSA);
        this.helper.alterConfig(map);

        ASN1EncodableVector algVec = new ASN1EncodableVector();
        algVec.add(X9ObjectIdentifiers.ecdsa_with_SHA256);
        algVec.add(PKCSObjectIdentifiers.sha1WithRSAEncryption);
        ASN1Sequence algSeq = new DERSequence(algVec);
        ExtensionsGenerator extgen = new ExtensionsGenerator();
        // RFC 6960: id-pkix-ocsp-pref-sig-algs   OBJECT IDENTIFIER ::= { id-pkix-ocsp 8 } 
        extgen.addExtension(new ASN1ObjectIdentifier(OCSPObjectIdentifiers.id_pkix_ocsp + ".8"), false, algSeq);
        Extensions exts = extgen.generate();
        assertNotNull(exts);

        OCSPReqBuilder gen = new OCSPReqBuilder();
        gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert,
                ocspTestCert.getSerialNumber()), exts);
        gen.setRequestExtensions(exts);
        OCSPReq req = gen.build();
        assertTrue(req.hasExtensions());

        BasicOCSPResp response = helper.sendOCSPGet(req.getEncoded(), null, OCSPRespBuilder.SUCCESSFUL, 200);
        assertNotNull("Could not retrieve response, test could not continue.", response);
        assertEquals(PKCSObjectIdentifiers.sha1WithRSAEncryption, response.getSignatureAlgOID());

        // Try sending a request where the preferred signature algorithm is not compatible with the signing key, but 
        // the configured algorithm is. Expected a response signed using the first configured algorithm

        algVec = new ASN1EncodableVector();
        algVec.add(X9ObjectIdentifiers.ecdsa_with_SHA256);
        algSeq = new DERSequence(algVec);

        extgen = new ExtensionsGenerator();
        extgen.addExtension(new ASN1ObjectIdentifier(OCSPObjectIdentifiers.id_pkix_ocsp + ".8"), false, algSeq);
        exts = extgen.generate();
        assertNotNull(exts);

        gen = new OCSPReqBuilder();
        gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert,
                ocspTestCert.getSerialNumber()), exts);
        gen.setRequestExtensions(exts);
        req = gen.build();
        assertTrue(req.hasExtensions());

        response = helper.sendOCSPGet(req.getEncoded(), null, OCSPRespBuilder.SUCCESSFUL, 200);
        assertNotNull("Could not retrieve response, test could not continue.", response);
        assertEquals(PKCSObjectIdentifiers.sha256WithRSAEncryption, response.getSignatureAlgOID());
    }

    /** This test tests that the OCSP response contains is signed by the preferred signature algorithm specified in the request. */
    /* Example of the ASN.1 dump (with friendly names from the RFC added ) of what the extensions should look like.
     * 
     * Note that we have left out the optional
     *  PreferredSignatureAlgorithm.pubKeyAlgIdentifier
     * and
     *  AlgorithmIdentifier.parameters
     *  
     *               ...
     *  75  48:     requestExtensions [2] {
     *  77  46:       Extensions ::= SEQUENCE {
     *  79  44:         Extension ::= SEQUENCE {
     *  81   9:           extnID OBJECT IDENTIFIER '1 3 6 1 5 5 7 48 1 8'
     *  92  31:           extnValue OCTET STRING, encapsulates {
     *  94  29:             PreferredSignatureAlgorithms ::= SEQUENCE {
     *  96  12:               PreferredSignatureAlgorithm ::= SEQUENCE {
     *  98  10:                 sigIdentifier AlgorithmIdentifier ::= SEQUENCE {
     * 100   8:                   algorithm OBJECT IDENTIFIER
     *        :                     ecdsaWithSHA256 (1 2 840 10045 4 3 2)
     *        :                   }
     *        :                 }
     * 110  13:               PreferredSignatureAlgorithm ::= SEQUENCE {
     * 112  11:                 sigIdentifier AlgorithmIdentifier ::= SEQUENCE {
     * 114   9:                   algorithm OBJECT IDENTIFIER
     *        :                     sha1WithRSAEncryption (1 2 840 113549 1 1 5)
     *        :                   }
     *        :                 ...
     */
    @Test
    public void testSigAlgExtension() throws Exception {
        log.trace(">testSigAlgExtensionNew");
        loadUserCert(caid);
        // Try sending a request where the preferred signature algorithm in the extension is expected to be used to sign the response.
        // set ocsp configuration
        final Map<String, String> map = new HashMap<String, String>();
        map.put("ocsp.signaturealgorithm",
                AlgorithmConstants.SIGALG_SHA256_WITH_RSA + ";" + AlgorithmConstants.SIGALG_SHA1_WITH_RSA);
        helper.alterConfig(map);
        final ASN1Sequence preferredSignatureAlgorithms = getPreferredSignatureAlgorithms(
                X9ObjectIdentifiers.ecdsa_with_SHA256, PKCSObjectIdentifiers.sha1WithRSAEncryption);
        final ExtensionsGenerator extensionsGenerator = new ExtensionsGenerator();
        // RFC 6960: id-pkix-ocsp-pref-sig-algs   OBJECT IDENTIFIER ::= { id-pkix-ocsp 8 } 
        extensionsGenerator.addExtension(new ASN1ObjectIdentifier(OCSPObjectIdentifiers.id_pkix_ocsp + ".8"), false,
                preferredSignatureAlgorithms);
        final Extensions extensions = extensionsGenerator.generate();
        assertNotNull(extensions);
        final OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder();
        ocspReqBuilder.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert,
                ocspTestCert.getSerialNumber()));
        ocspReqBuilder.setRequestExtensions(extensions);
        final OCSPReq ocspRequest = ocspReqBuilder.build();
        assertTrue(ocspRequest.hasExtensions());
        log.debug("base64 encoded request: " + new String(Base64.encode(ocspRequest.getEncoded(), false)));
        final BasicOCSPResp response1 = helper.sendOCSPGet(ocspRequest.getEncoded(), null,
                OCSPRespBuilder.SUCCESSFUL, 200);
        assertNotNull("Could not retrieve response, test could not continue.", response1);
        assertEquals(PKCSObjectIdentifiers.sha1WithRSAEncryption, response1.getSignatureAlgOID());
    }

    /** Test with a preferred signature algorithm specified in the request that is incompatible with the singing key. */
    @Test
    public void testSigAlgExtensionMismatch() throws Exception {
        log.trace(">testSigAlgExtensionNewMismatch");
        loadUserCert(caid);
        final Map<String, String> map = new HashMap<String, String>();
        map.put("ocsp.signaturealgorithm",
                AlgorithmConstants.SIGALG_SHA256_WITH_RSA + ";" + AlgorithmConstants.SIGALG_SHA1_WITH_RSA);
        helper.alterConfig(map);
        // Try sending a request where the preferred signature algorithm is not compatible with the signing key, but 
        // the configured algorithm is. Expected a response signed using the first configured algorithm
        final ASN1Sequence preferredSignatureAlgorithms = getPreferredSignatureAlgorithms(
                X9ObjectIdentifiers.ecdsa_with_SHA256);
        final ExtensionsGenerator extensionsGenerator = new ExtensionsGenerator();
        extensionsGenerator.addExtension(new ASN1ObjectIdentifier(OCSPObjectIdentifiers.id_pkix_ocsp + ".8"), false,
                preferredSignatureAlgorithms);
        final Extensions extensions = extensionsGenerator.generate();
        assertNotNull(extensions);
        final OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder();
        ocspReqBuilder.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert,
                ocspTestCert.getSerialNumber()));
        ocspReqBuilder.setRequestExtensions(extensions);
        final OCSPReq ocspRequest = ocspReqBuilder.build();
        assertTrue(ocspRequest.hasExtensions());
        log.debug("base64 encoded request: " + new String(Base64.encode(ocspRequest.getEncoded(), false)));
        final BasicOCSPResp response2 = helper.sendOCSPGet(ocspRequest.getEncoded(), null,
                OCSPRespBuilder.SUCCESSFUL, 200);
        assertNotNull("Could not retrieve response, test could not continue.", response2);
        assertEquals(PKCSObjectIdentifiers.sha256WithRSAEncryption, response2.getSignatureAlgOID());
    }

    /** @return a RFC 6960 PreferredSignatureAlgorithms object. */
    private ASN1Sequence getPreferredSignatureAlgorithms(final ASN1ObjectIdentifier... algorithmOids) {
        final ASN1Encodable[] asn1Encodables = new ASN1Encodable[algorithmOids.length];
        for (int i = 0; i < algorithmOids.length; i++) {
            // PreferredSignatureAlgorithm ::= SEQUENCE { sigIdentifier AlgorithmIdentifier, pubKeyAlgIdentifier SMIMECapability OPTIONAL } 
            final ASN1Sequence preferredSignatureAlgorithm = new DERSequence(
                    new ASN1Encodable[] { new AlgorithmIdentifier(algorithmOids[i]) });
            asn1Encodables[i] = preferredSignatureAlgorithm;
        }
        // PreferredSignatureAlgorithms ::= SEQUENCE OF PreferredSignatureAlgorithm 
        final ASN1Sequence preferredSignatureAlgorithms = new DERSequence(asn1Encodables);
        return preferredSignatureAlgorithms;
    }

    /**
     * This test tests that the OCSP response does not contain the signing cert if Ejbca is configured that way.
     * 
     * @throws Exception
     */
    @Test
    public void testSignCertNotIncludedInResponse() throws Exception {
        loadUserCert(this.caid);
        // set OCSP configuration
        Map<String, String> map = new HashMap<String, String>();
        map.put(OcspConfiguration.INCLUDE_SIGNING_CERT, "false");
        helper.alterConfig(map);
        // This setting is part of the OCSP signing cache so a reload of the cache is required
        helper.reloadKeys();
        // Build the OCSP request
        OCSPReqBuilder gen = new OCSPReqBuilder();
        gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert,
                ocspTestCert.getSerialNumber()), null);
        OCSPReq req = gen.build();
        // Send and verify the OCSP request
        BasicOCSPResp response = helper.sendOCSPGet(req.getEncoded(), null, OCSPRespBuilder.SUCCESSFUL, 200, false,
                cacert);
        assertNotNull("Could not retrieve response, test could not continue.", response);
        assertTrue("Response does contain certificates", response.getCerts().length == 0);
    }

    /**
     * This test tests that the OCSP response does not contain the root CA cert in the included certificate chain.
     * 
     * @throws Exception
     */
    @Test
    public void testRootCACertNotIncludedInResponse() throws Exception {
        log.trace(">testRootCACertNotIncludedInResponse()");

        // Create a subCA and a subsubCA
        String subcaDN = "CN=SubTestCA";
        createSubCA(subcaDN, caid);

        String subSubCaDN = "CN=SubSubTestCA";
        X509Certificate subSubCaCert = createSubCA(subSubCaDN, subcaDN.hashCode());

        // set OCSP configuration
        Map<String, String> map = new HashMap<String, String>();
        map.put(OcspConfiguration.INCLUDE_CERT_CHAIN, "true");
        GlobalOcspConfiguration ocspConfiguration = (GlobalOcspConfiguration) globalConfigurationSession
                .getCachedConfiguration(GlobalOcspConfiguration.OCSP_CONFIGURATION_ID);
        ocspConfiguration.setOcspDefaultResponderReference(subSubCaDN);
        globalConfigurationSession.saveConfiguration(admin, ocspConfiguration);
        this.helper.alterConfig(map);
        helper.reloadKeys();

        // Expects an OCSP response including a certchain that contains only the 2 subCAs and not their rootCA.
        try {
            loadUserCert(subSubCaDN.hashCode());

            OCSPReqBuilder gen = new OCSPReqBuilder();
            gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), subSubCaCert,
                    ocspTestCert.getSerialNumber()), null);
            OCSPReq req = gen.build();

            BasicOCSPResp response = helper.sendOCSPGet(req.getEncoded(), null, OCSPRespBuilder.SUCCESSFUL, 200);
            assertNotNull("Could not retrieve response, test could not continue.", response);
            assertTrue("Response contains more that 2 certificate", response.getCerts().length == 2);

            X509CertificateHolder[] includedCerts = response.getCerts();
            assertEquals(subSubCaDN, includedCerts[0].getSubject().toString());
            assertEquals(subcaDN, includedCerts[1].getSubject().toString());

        } finally {
            try {
                endEntityManagementSession.deleteUser(admin, "ocsptest");
            } catch (Exception e) {
                log.error("", e);
            }

            try {
                int cryptoTokenId = caSession.getCAInfo(admin, subSubCaDN.hashCode()).getCAToken()
                        .getCryptoTokenId();
                CryptoTokenTestUtils.removeCryptoToken(admin, cryptoTokenId);

                cryptoTokenId = caSession.getCAInfo(admin, subcaDN.hashCode()).getCAToken().getCryptoTokenId();
                CryptoTokenTestUtils.removeCryptoToken(admin, cryptoTokenId);
            } catch (Exception e) {
                log.error("", e);
            }

            try {
                caSession.removeCA(admin, subSubCaDN.hashCode());
                caSession.removeCA(admin, subcaDN.hashCode());
            } catch (Exception e) {
                log.info("Could not remove CA with SubjectDN " + subSubCaDN);
            }
        }

        log.trace("<testRootCACertNotIncludedInResponse()");
    }

    /**
     * removes DSA CA
     *
     * @throws Exception
     *           error
     */
    public void removeDSACA() throws Exception {
        log.trace(">test98RemoveDSACA()");
        assertTrue("This test can only be run on a full EJBCA installation.",
                ((HttpURLConnection) new URL(httpReqPath + '/').openConnection()).getResponseCode() == 200);
        try {
            final int cryptoTokenId = caSession.getCAInfo(admin, DSA_DN.hashCode()).getCAToken().getCryptoTokenId();
            CryptoTokenTestUtils.removeCryptoToken(admin, cryptoTokenId);
        } catch (Exception e) {
            log.error("", e);
        }
        try {
            caSession.removeCA(admin, DSA_DN.hashCode());
        } catch (Exception e) {
            log.info("Could not remove CA with SubjectDN " + DSA_DN);
        }
        try {
            caSession.removeCA(admin, "CN=OCSPDSAIMPCATEST".hashCode());
        } catch (Exception e) {
            log.info("Could not remove CA with SubjectDN CN=OCSPDSAIMPCATEST");
        }
        log.trace("<test98RemoveDSACA()");
    } // test98OcspDsaGood

    /**
     * removes ECDSA CA
     *
     * @throws Exception
     *           error
     */
    public void removeECDSACA() throws Exception {
        assertTrue("This test can only be run on a full EJBCA installation.",
                ((HttpURLConnection) new URL(httpReqPath + '/').openConnection()).getResponseCode() == 200);
        try {
            caSession.removeCA(admin, "CN=OCSPECDSATEST".hashCode());
        } catch (Exception e) {
            log.info("Could not remove CA with SubjectDN CN=OCSPECDSATEST");
        }
        try {
            caSession.removeCA(admin, "CN=OCSPECDSAIMPCATEST".hashCode());
        } catch (Exception e) {
            log.info("Could not remove CA with SubjectDN CN=OCSPECDSAIMPCATEST");
        }
    }

    //
    // Private helper methods
    //

    /**
     * Generate a simple OCSP Request object
     */
    private byte[] getValidOcspRequest() throws Exception {
        // Get user and ocspTestCert that we know...
        loadUserCert(caid);
        // And an OCSP request
        OCSPReqBuilder gen = new OCSPReqBuilder();
        gen.addRequest(new JcaCertificateID(SHA1DigestCalculator.buildSha1Instance(), cacert,
                ocspTestCert.getSerialNumber()));
        Extension[] extensions = new Extension[1];
        extensions[0] = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false,
                new DEROctetString("123456789".getBytes()));
        gen.setRequestExtensions(new Extensions(extensions));
        OCSPReq req = gen.build();
        return req.getEncoded();
    }

    /**
     * Sends the payload to the OCSP Servlet using TCP. Can be used for testing
     * malformed or malicious requests.
     *
     * @param contentLength
     *          The HTTP 'Content-Length' header to send to the server.
     * @return the OCSP Response from the server
     * @throws IOException
     *           if the is a IO problem
     */
    private OCSPResp sendRawRequestToOcsp(int contentLength, byte[] payload, boolean writeByteByByte)
            throws IOException {
        // Create the HTTP header
        String headers = "POST " + "/ejbca/" + resourceOcsp + " HTTP/1.1\r\n" + "Host: " + httpHost + "\r\n"
                + "Content-Type: application/ocsp-request\r\n" + "Content-Length: " + contentLength + "\r\n"
                + "\r\n";
        // Merge the HTTP headers, the OCSP request and the raw data into one
        // package.
        byte[] input = concatByteArrays(headers.getBytes(), payload);
        log.debug("HTTP request headers: " + headers);
        log.debug("HTTP headers size: " + headers.getBytes().length);
        log.debug("Size of data to send: " + input.length);
        // Create the socket.
        Socket socket = new Socket(InetAddress.getByName(httpHost), Integer.parseInt(httpPort));
        // Send data byte for byte.
        OutputStream os = socket.getOutputStream();
        if (writeByteByByte) {
            int i = 0;
            try {
                for (i = 0; i < input.length; i++) {
                    os.write(input[i]);
                }
            } catch (IOException e) {
                log.info("Socket wrote " + i + " bytes before throwing an IOException.");
            }
        } else {
            try {
                os.write(input);
            } catch (IOException e) {
                log.info("Could not write to TCP Socket " + e.getMessage());
            }
        }
        // Reading the response.
        byte rawResponse[] = getHttpResponse(socket.getInputStream());
        log.info("Response contains: " + rawResponse.length + " bytes.");
        socket.close();
        return new OCSPResp(rawResponse);
    }

    /**
     * Read the payload of a HTTP response as a byte array.
     */
    private byte[] getHttpResponse(InputStream ins) throws IOException {
        byte buf[] = inputStreamToBytes(ins);
        int i = 0;
        // Removing the HTTP headers. The HTTP headers end at the last
        // occurrence of "\r\n".
        for (i = buf.length - 1; i > 0; i--) {
            if ((buf[i] == 0x0A) && (buf[i - 1] == 0x0D)) {
                break;
            }
        }
        byte[] header = ArrayUtils.subarray(buf, 0, i + 1);
        log.debug("HTTP reponse header: " + new String(header));
        log.debug("HTTP reponse header size: " + header.length);
        log.debug("Stream length: " + buf.length);
        log.debug("HTTP payload length: " + (buf.length - header.length));
        return ArrayUtils.subarray(buf, header.length, buf.length);
    }

    /**
     * For small streams only.
     */
    private static byte[] inputStreamToBytes(InputStream in) throws IOException {
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while (true) {
            final int b = in.read();
            if (b < 0) {
                break;
            }
            baos.write(b);
        }
        baos.flush();
        in.close();
        return baos.toByteArray();
    }

    /**
     * @return a new byte array with the two arguments concatenated.
     */
    private byte[] concatByteArrays(byte[] array1, byte[] array2) {
        byte[] ret = new byte[array1.length + array2.length];
        System.arraycopy(array1, 0, ret, 0, array1.length);
        System.arraycopy(array2, 0, ret, array1.length, array2.length);
        return ret;
    }

    /**
     * adds a CA Using ECDSA keys to the database.
     *
     * It also checks that the CA is stored correctly.
     *
     * @throws Exception
     *           error
     */
    private CAInfo addECDSACA(String dn, String keySpec) throws Exception {
        log.trace(">addECDSACA()");
        boolean ret = false;
        int cryptoTokenId = 0;
        CAInfo info = null;
        try {
            cryptoTokenId = CryptoTokenTestUtils.createCryptoTokenForCA(admin, dn, keySpec);
            final CAToken catoken = CaTestUtils.createCaToken(cryptoTokenId,
                    AlgorithmConstants.SIGALG_SHA256_WITH_ECDSA, AlgorithmConstants.SIGALG_SHA1_WITH_RSA);
            // Create and active OSCP CA Service.
            List<ExtendedCAServiceInfo> extendedcaservices = new ArrayList<ExtendedCAServiceInfo>();
            extendedcaservices.add(new HardTokenEncryptCAServiceInfo(ExtendedCAServiceInfo.STATUS_ACTIVE));
            extendedcaservices.add(new KeyRecoveryCAServiceInfo(ExtendedCAServiceInfo.STATUS_ACTIVE));
            List<CertificatePolicy> policies = new ArrayList<CertificatePolicy>(1);
            policies.add(new CertificatePolicy("2.5.29.32.0", "", ""));

            X509CAInfo cainfo = new X509CAInfo(dn, dn, CAConstants.CA_ACTIVE,
                    CertificateProfileConstants.CERTPROFILE_FIXED_ROOTCA, 365, CAInfo.SELFSIGNED, null, catoken);
            cainfo.setDescription("JUnit ECDSA CA");
            cainfo.setPolicies(policies);
            cainfo.setExtendedCAServiceInfos(extendedcaservices);
            caAdminSession.createCA(admin, cainfo);

            info = caSession.getCAInfo(admin, dn);

            X509Certificate cert = (X509Certificate) info.getCertificateChain().iterator().next();
            assertTrue("Error in created ca certificate", cert.getSubjectDN().toString().equals(dn));
            assertTrue("Creating CA failed", info.getSubjectDN().equals(dn));
            // Make BC cert instead to make sure the public key is BC provider type (to make our test below easier)
            X509Certificate bccert = (X509Certificate) CertTools.getCertfromByteArray(cert.getEncoded());
            PublicKey pk = bccert.getPublicKey();
            if (pk instanceof JCEECPublicKey) {
                JCEECPublicKey ecpk = (JCEECPublicKey) pk;
                assertEquals(ecpk.getAlgorithm(), "EC");
                org.bouncycastle.jce.spec.ECParameterSpec spec = ecpk.getParameters();
                if (StringUtils.equals(keySpec, "implicitlyCA")) {
                    assertNull("ImplicitlyCA must have null spec", spec);
                } else {
                    assertNotNull("secp256r1 must not have null spec", spec);
                }
            } else if (pk instanceof BCECPublicKey) {
                BCECPublicKey ecpk = (BCECPublicKey) pk;
                assertEquals(ecpk.getAlgorithm(), "EC");
                org.bouncycastle.jce.spec.ECParameterSpec spec = ecpk.getParameters();
                if (StringUtils.equals(keySpec, "implicitlyCA")) {
                    assertNull("ImplicitlyCA must have null spec", spec);
                } else {
                    assertNotNull("secp256r1 must not have null spec", spec);
                }
            } else {
                assertTrue("Public key is not EC: " + pk.getClass().getName(), false);
            }

            ret = true;
        } catch (CAExistsException pee) {
            log.info("CA exists.");
        }
        assertTrue("Creating ECDSA CA failed", ret);
        log.trace("<addECDSACA()");
        return info;
    }

    /**
     * adds a CA Using DSA keys to the database.
     *
     * It also checks that the CA is stored correctly.
     *
     * @throws Exception
     *           error
     */
    private X509Certificate addDSACA(String dn, String keySpec) throws Exception {
        log.trace(">addDSACA()");
        boolean ret = false;
        X509Certificate cacert = null;
        int cryptoTokenId = 0;
        try {
            cryptoTokenId = CryptoTokenTestUtils.createCryptoTokenForCA(admin, dn, keySpec);
            final CAToken catoken = CaTestUtils.createCaToken(cryptoTokenId,
                    AlgorithmConstants.SIGALG_SHA1_WITH_DSA, AlgorithmConstants.SIGALG_SHA1_WITH_RSA);
            // Create and active OSCP CA Service.
            final List<ExtendedCAServiceInfo> extendedcaservices = new ArrayList<ExtendedCAServiceInfo>();
            extendedcaservices.add(new HardTokenEncryptCAServiceInfo(ExtendedCAServiceInfo.STATUS_ACTIVE));
            extendedcaservices.add(new KeyRecoveryCAServiceInfo(ExtendedCAServiceInfo.STATUS_ACTIVE));
            final List<CertificatePolicy> policies = new ArrayList<CertificatePolicy>(1);
            policies.add(new CertificatePolicy("2.5.29.32.0", "", ""));

            X509CAInfo cainfo = new X509CAInfo(dn, dn, CAConstants.CA_ACTIVE,
                    CertificateProfileConstants.CERTPROFILE_FIXED_ROOTCA, 365, CAInfo.SELFSIGNED, null, catoken);
            cainfo.setDescription("JUnit DSA CA");
            cainfo.setPolicies(policies);
            caAdminSession.createCA(admin, cainfo);

            CAInfo info = caSession.getCAInfo(admin, dn);

            X509Certificate cert = (X509Certificate) info.getCertificateChain().iterator().next();
            assertEquals("Error in created ca certificate", dn, CertTools.getSubjectDN(cert));
            assertEquals("Creating CA failed, DN was incorrect.", dn, info.getSubjectDN());
            assertTrue("Public key was not an instance of DSAPublicKey",
                    cert.getPublicKey() instanceof DSAPublicKey);

            ret = true;
            Collection<Certificate> coll = info.getCertificateChain();
            Object[] certs = coll.toArray();
            cacert = (X509Certificate) certs[0];
        } catch (CAExistsException e) {
            log.info("CA exists.");
            throw e;
        }
        assertTrue("Creating DSA CA failed", ret);
        log.trace("<addDSACA()");
        return cacert;
    }

    private X509Certificate createSubCA(String subcaDN, int signbyID)
            throws CryptoTokenOfflineException, CryptoTokenAuthenticationFailedException, InvalidAlgorithmException,
            AuthorizationDeniedException, CADoesntExistsException, CAExistsException {
        try {
            int cryptoTokenId = CryptoTokenTestUtils.createCryptoTokenForCA(admin, subcaDN, "1024");
            final CAToken catoken = CaTestUtils.createCaToken(cryptoTokenId,
                    AlgorithmConstants.SIGALG_SHA1_WITH_RSA, AlgorithmConstants.SIGALG_SHA1_WITH_RSA);
            // Create and active OSCP CA Service.
            final List<ExtendedCAServiceInfo> extendedcaservices = new ArrayList<ExtendedCAServiceInfo>();
            extendedcaservices.add(new HardTokenEncryptCAServiceInfo(ExtendedCAServiceInfo.STATUS_ACTIVE));
            extendedcaservices.add(new KeyRecoveryCAServiceInfo(ExtendedCAServiceInfo.STATUS_ACTIVE));
            final List<CertificatePolicy> policies = new ArrayList<CertificatePolicy>(1);
            policies.add(new CertificatePolicy("2.5.29.32.0", "", ""));

            X509CAInfo cainfo = new X509CAInfo(subcaDN, subcaDN, CAConstants.CA_ACTIVE,
                    CertificateProfileConstants.CERTPROFILE_FIXED_SUBCA, 365, signbyID, null, catoken);
            cainfo.setDescription("JUnit DSA CA");
            cainfo.setPolicies(policies);
            cainfo.setExtendedCAServiceInfos(extendedcaservices);
            caAdminSession.createCA(admin, cainfo);

            CAInfo info = caSession.getCAInfo(admin, subcaDN);

            return (X509Certificate) info.getCertificateChain().iterator().next();
        } catch (CAExistsException e) {
            log.info("CA exists.");
            throw e;
        }
    }

    /**
     * This method creates the user "ocsptest" and generated a certificate for it
     */
    protected void loadUserCert(int caid) throws Exception {
        createUserCert(caid);
    }

    private KeyPair createUserCert(int caid)
            throws AuthorizationDeniedException, UserDoesntFullfillEndEntityProfile, ApprovalException,
            WaitingForApprovalException, Exception, ObjectNotFoundException, AuthStatusException,
            AuthLoginException, IllegalKeyException, CADoesntExistsException {
        final String USERNAME = "ocsptest";
        if (!endEntityManagementSession.existsUser(USERNAME)) {

            endEntityManagementSession.addUser(admin, USERNAME, "foo123", "C=SE,O=AnaTom,CN=OCSPTest", null,
                    "ocsptest@anatom.se", false, SecConst.EMPTY_ENDENTITYPROFILE,
                    CertificateProfileConstants.CERTPROFILE_FIXED_ENDUSER, EndEntityTypes.ENDUSER.toEndEntityType(),
                    SecConst.TOKEN_SOFT_PEM, 0, caid);
            log.debug("created user: ocsptest, foo123, C=SE, O=AnaTom, CN=OCSPTest");

        } else {
            log.debug("User ocsptest already exists.");
            EndEntityInformation userData = new EndEntityInformation(USERNAME, "C=SE,O=AnaTom,CN=OCSPTest", caid,
                    null, "ocsptest@anatom.se", EndEntityConstants.STATUS_NEW,
                    EndEntityTypes.ENDUSER.toEndEntityType(), SecConst.EMPTY_ENDENTITYPROFILE,
                    CertificateProfileConstants.CERTPROFILE_FIXED_ENDUSER, null, null, SecConst.TOKEN_SOFT_PEM, 0,
                    null);
            userData.setPassword("foo123");
            endEntityManagementSession.changeUser(admin, userData, false);
            log.debug("Reset status to NEW");
        }
        // Generate certificate for the new user
        KeyPair keys = KeyTools.genKeys("512", "RSA");

        // user that we know exists...
        ocspTestCert = (X509Certificate) signSession.createCertificate(admin, USERNAME, "foo123",
                new PublicKeyWrapper(keys.getPublic()));
        assertNotNull("Failed to create new certificate", ocspTestCert);
        return keys;
    }

    /** Checks the signature on an OCSP request and checks that it is signed by an allowed CA.
     * Does not check for revocation of the signer certificate
     * 
     * @param clientRemoteAddr The ip address or hostname of the remote client that sent the request, can be null.
     * @param req The signed OCSPReq
     * @param cacerts a CertificateCache of Certificates, the authorized CA-certificates. The signer certificate must be issued by one of these.
     * @return X509Certificate which is the certificate that signed the OCSP request
     * @throws SignRequestSignatureException if signature verification fail, or if the signing certificate is not authorized
     * @throws SignRequestException if there is no signature on the OCSPReq
     * @throws OCSPException if the request can not be parsed to retrieve certificates
     * @throws NoSuchProviderException if the BC provider is not installed
     * @throws CertificateException if the certificate can not be parsed
     * @throws NoSuchAlgorithmException if the certificate contains an unsupported algorithm
     * @throws InvalidKeyException if the certificate, or CA key is invalid
     * @throws OperatorCreationException 
     */
    public static X509Certificate checkRequestSignature(String clientRemoteAddr, OCSPReq req,
            CaCertificateCache cacerts) throws SignRequestException, OCSPException, NoSuchProviderException,
            CertificateException, NoSuchAlgorithmException, InvalidKeyException, SignRequestSignatureException,
            OperatorCreationException {

        X509Certificate signercert = null;

        if (!req.isSigned()) {
            String infoMsg = intres.getLocalizedMessage("ocsp.errorunsignedreq", clientRemoteAddr);
            log.info(infoMsg);
            throw new SignRequestException(infoMsg);
        }
        // Get all certificates embedded in the request (probably a certificate chain)
        X509CertificateHolder[] certs = req.getCerts();
        // Set, as a try, the signer to be the first certificate, so we have a name to log...
        String signer = null;
        JcaX509CertificateConverter converter = new JcaX509CertificateConverter();
        if (certs.length > 0) {
            signer = CertTools.getSubjectDN(converter.getCertificate(certs[0]));
        }

        // We must find a cert to verify the signature with...
        boolean verifyOK = false;
        for (int i = 0; i < certs.length; i++) {
            if (req.isSignatureValid(new JcaContentVerifierProviderBuilder().build(certs[i])) == true) {
                signercert = converter.getCertificate(certs[i]);
                signer = CertTools.getSubjectDN(signercert);
                Date now = new Date();
                String signerissuer = CertTools.getIssuerDN(signercert);
                String infoMsg = intres.getLocalizedMessage("ocsp.infosigner", signer);
                log.info(infoMsg);
                verifyOK = true;
                // Also check that the signer certificate can be verified by one of the CA-certificates
                // that we answer for
                X509Certificate signerca = cacerts.findLatestBySubjectDN(HashID.getFromIssuerDN(certs[i]));
                String subject = signer;
                String issuer = signerissuer;
                if (signerca != null) {
                    try {
                        signercert.verify(signerca.getPublicKey());
                        if (log.isDebugEnabled()) {
                            log.debug("Checking validity. Now: " + now + ", signerNotAfter: "
                                    + signercert.getNotAfter());
                        }
                        CertTools.checkValidity(signercert, now);
                        // Move the error message string to the CA cert
                        subject = CertTools.getSubjectDN(signerca);
                        issuer = CertTools.getIssuerDN(signerca);
                        CertTools.checkValidity(signerca, now);
                    } catch (SignatureException e) {
                        infoMsg = intres.getLocalizedMessage("ocsp.infosigner.invalidcertsignature", subject,
                                issuer, e.getMessage());
                        log.info(infoMsg);
                        verifyOK = false;
                    } catch (InvalidKeyException e) {
                        infoMsg = intres.getLocalizedMessage("ocsp.infosigner.invalidcertsignature", subject,
                                issuer, e.getMessage());
                        log.info(infoMsg);
                        verifyOK = false;
                    } catch (CertificateNotYetValidException e) {
                        infoMsg = intres.getLocalizedMessage("ocsp.infosigner.certnotyetvalid", subject, issuer,
                                e.getMessage());
                        log.info(infoMsg);
                        verifyOK = false;
                    } catch (CertificateExpiredException e) {
                        infoMsg = intres.getLocalizedMessage("ocsp.infosigner.certexpired", subject, issuer,
                                e.getMessage());
                        log.info(infoMsg);
                        verifyOK = false;
                    }
                } else {
                    infoMsg = intres.getLocalizedMessage("ocsp.infosigner.nocacert", signer, signerissuer);
                    log.info(infoMsg);
                    verifyOK = false;
                }
                break;
            }
        }
        if (!verifyOK) {
            String errMsg = intres.getLocalizedMessage("ocsp.errorinvalidsignature", signer);
            log.info(errMsg);
            throw new SignRequestSignatureException(errMsg);
        }

        return signercert;
    }

}