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}