001/*
002 * $Id: OcspClientBouncyCastle.java 4784 2011-03-15 08:33:00Z blowagie $
003 *
004 * This file is part of the iText (R) project.
005 * Copyright (c) 1998-2011 1T3XT BVBA
006 * Authors: Bruno Lowagie, Paulo Soares, et al.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU Affero General Public License version 3
010 * as published by the Free Software Foundation with the addition of the
011 * following permission added to Section 15 as permitted in Section 7(a):
012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
014 *
015 * This program is distributed in the hope that it will be useful, but
016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
017 * or FITNESS FOR A PARTICULAR PURPOSE.
018 * See the GNU Affero General Public License for more details.
019 * You should have received a copy of the GNU Affero General Public License
020 * along with this program; if not, see http://www.gnu.org/licenses or write to
021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
022 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
023 * http://itextpdf.com/terms-of-use/
024 *
025 * The interactive user interfaces in modified source and object code versions
026 * of this program must display Appropriate Legal Notices, as required under
027 * Section 5 of the GNU Affero General Public License.
028 *
029 * In accordance with Section 7(b) of the GNU Affero General Public License,
030 * a covered work must retain the producer line in every PDF that is created
031 * or manipulated using iText.
032 *
033 * You can be released from the requirements of the license by purchasing
034 * a commercial license. Buying such a license is mandatory as soon as you
035 * develop commercial activities involving the iText software without
036 * disclosing the source code of your own applications.
037 * These activities include: offering paid services to customers as an ASP,
038 * serving PDFs on the fly in a web application, shipping iText with a closed
039 * source product.
040 *
041 * For more information, please contact iText Software Corp. at this
042 * address: sales@itextpdf.com
043 */
044package com.itextpdf.text.pdf;
045
046import java.io.BufferedOutputStream;
047import java.io.DataOutputStream;
048import java.io.IOException;
049import java.io.InputStream;
050import java.io.OutputStream;
051import java.math.BigInteger;
052import java.net.HttpURLConnection;
053import java.net.URL;
054import java.security.Security;
055import java.security.cert.X509Certificate;
056import java.util.Vector;
057
058import org.bouncycastle.asn1.DERObjectIdentifier;
059import org.bouncycastle.asn1.DEROctetString;
060import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
061import org.bouncycastle.asn1.x509.X509Extension;
062import org.bouncycastle.asn1.x509.X509Extensions;
063import org.bouncycastle.ocsp.BasicOCSPResp;
064import org.bouncycastle.ocsp.CertificateID;
065import org.bouncycastle.ocsp.CertificateStatus;
066import org.bouncycastle.ocsp.OCSPException;
067import org.bouncycastle.ocsp.OCSPReq;
068import org.bouncycastle.ocsp.OCSPReqGenerator;
069import org.bouncycastle.ocsp.OCSPResp;
070import org.bouncycastle.ocsp.SingleResp;
071
072import com.itextpdf.text.ExceptionConverter;
073import com.itextpdf.text.error_messages.MessageLocalization;
074
075/**
076 * OcspClient implementation using BouncyCastle.
077 * @author psoares
078 * @since       2.1.6
079 */
080public class OcspClientBouncyCastle implements OcspClient {
081    /** root certificate */
082    private X509Certificate rootCert;
083    /** check certificate */
084    private X509Certificate checkCert;
085    /** OCSP URL */
086    private String url;
087
088    /**
089     * Creates an instance of an OcspClient that will be using BouncyCastle.
090     * @param checkCert the check certificate
091     * @param rootCert  the root certificate
092     * @param url       the OCSP URL
093     */
094    public OcspClientBouncyCastle(X509Certificate checkCert, X509Certificate rootCert, String url) {
095        this.checkCert = checkCert;
096        this.rootCert = rootCert;
097        this.url = url;
098    }
099
100    /**
101     * Generates an OCSP request using BouncyCastle.
102     * @param issuerCert        certificate of the issues
103     * @param serialNumber      serial number
104     * @return  an OCSP request
105     * @throws OCSPException
106     * @throws IOException
107     */
108    private static OCSPReq generateOCSPRequest(X509Certificate issuerCert, BigInteger serialNumber) throws OCSPException, IOException {
109        //Add provider BC
110        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
111
112        // Generate the id for the certificate we are looking for
113        CertificateID id = new CertificateID(CertificateID.HASH_SHA1, issuerCert, serialNumber);
114
115        // basic request generation with nonce
116        OCSPReqGenerator gen = new OCSPReqGenerator();
117
118        gen.addRequest(id);
119
120        // create details for nonce extension
121        Vector<DERObjectIdentifier> oids = new Vector<DERObjectIdentifier>();
122        Vector<X509Extension> values = new Vector<X509Extension>();
123
124        oids.add(OCSPObjectIdentifiers.id_pkix_ocsp_nonce);
125        values.add(new X509Extension(false, new DEROctetString(new DEROctetString(PdfEncryption.createDocumentId()).getEncoded())));
126
127        gen.setRequestExtensions(new X509Extensions(oids, values));
128
129        return gen.generate();
130    }
131
132    /**
133     * @return  a byte array
134     * @see com.itextpdf.text.pdf.OcspClient#getEncoded()
135     */
136    public byte[] getEncoded() {
137        try {
138            OCSPReq request = generateOCSPRequest(rootCert, checkCert.getSerialNumber());
139            byte[] array = request.getEncoded();
140            URL urlt = new URL(url);
141            HttpURLConnection con = (HttpURLConnection)urlt.openConnection();
142            con.setRequestProperty("Content-Type", "application/ocsp-request");
143            con.setRequestProperty("Accept", "application/ocsp-response");
144            con.setDoOutput(true);
145            OutputStream out = con.getOutputStream();
146            DataOutputStream dataOut = new DataOutputStream(new BufferedOutputStream(out));
147            dataOut.write(array);
148            dataOut.flush();
149            dataOut.close();
150            if (con.getResponseCode() / 100 != 2) {
151                throw new IOException(MessageLocalization.getComposedMessage("invalid.http.response.1", con.getResponseCode()));
152            }
153            //Get Response
154            InputStream in = (InputStream) con.getContent();
155            OCSPResp ocspResponse = new OCSPResp(in);
156
157            if (ocspResponse.getStatus() != 0)
158                throw new IOException(MessageLocalization.getComposedMessage("invalid.status.1", ocspResponse.getStatus()));
159            BasicOCSPResp basicResponse = (BasicOCSPResp) ocspResponse.getResponseObject();
160            if (basicResponse != null) {
161                SingleResp[] responses = basicResponse.getResponses();
162                if (responses.length == 1) {
163                    SingleResp resp = responses[0];
164                    Object status = resp.getCertStatus();
165                    if (status == CertificateStatus.GOOD) {
166                        return basicResponse.getEncoded();
167                    }
168                    else if (status instanceof org.bouncycastle.ocsp.RevokedStatus) {
169                        throw new IOException(MessageLocalization.getComposedMessage("ocsp.status.is.revoked"));
170                    }
171                    else {
172                        throw new IOException(MessageLocalization.getComposedMessage("ocsp.status.is.unknown"));
173                    }
174                }
175            }
176        }
177        catch (Exception ex) {
178            throw new ExceptionConverter(ex);
179        }
180        return null;
181    }
182}