Java tutorial
/* ***** BEGIN LICENSE BLOCK ******************************************** * Version: EUPL 1.1/GPL 3.0 * * The contents of this file are subject to the EUPL, Version 1.1 or * - as soon they will be approved by the European Commission - * subsequent versions of the EUPL (the "Licence"); * you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.osor.eu/eupl/european-union-public-licence-eupl-v.1.1 * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is /oxsit-custom_it/src/com/yacme/ext/oxsit/cust_it/security/crl/OCSPQuery.java. * * The Initial Developer of the Original Code is * Giuseppe Castagno giuseppe.castagno@acca-esse.it * * Portions created by the Initial Developer are Copyright (C) 2009-2011 * the Initial Developer. All Rights Reserved. * This code is adapted from www.openscdp.org * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 3 or later (the "GPL") * in which case the provisions of the GPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of the GPL, and not to allow others to * use your version of this file under the terms of the EUPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the EUPL, or the GPL. * * ***** END LICENSE BLOCK ******************************************** */ /* * Original file header retained * --------- * |.##> <##.| Open Smart Card Development Platform (www.openscdp.org) * |# #| * |# #| Copyright (c) 1999-2006 CardContact Software & System Consulting * |'##> <##'| Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de) * --------- * * This file is part of OpenSCDP. * * OpenSCDP is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * OpenSCDP is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenSCDP; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * original package was: * * package de.cardcontact.scdp.ocsp * * File imported from http://www.openscdp.org/scsh3/download.html * */ package com.yacme.ext.oxsit.cust_it.security.crl; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.security.Security; import java.security.cert.X509Certificate; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.ocsp.*; import org.bouncycastle.x509.extension.X509ExtensionUtil; /** * How to use it it's here: * * http://www.openscdp.org/scsh3/ocspquery.html * * Perform a OCSP query for a list of certificates * * @author Andreas Schwier (info@cardcontact.de) */ public class OCSPQuery { public final static int GOOD = 0; public final static int UNKNOWN = 1; public final static int REVOKED = 100; public final static int KEYCOMPROMISE = 101; public final static int CACOMPROMISE = 102; public final static int AFFILIATIONCHANGED = 103; public final static int SUPERSEDED = 104; public final static int CESSATIONOFOPERATION = 105; public final static int CERTIFICATEHOLD = 106; public final static int REMOVEFROMCRL = 108; public final static int PRIVILEGEWITHDRAWN = 109; public final static int AACOMPROMISE = 110; X509Certificate issuercert; X509Certificate rootcert; X509Certificate m_UserCert; HashMap map; // Map of certificates to be included in query HashMap cs; // Map of status responses for queried certificates byte[] ocspResponse; // DER encoded response /** * Create query object with issuer certificate and maximum number of * certificates included in query * * @param rootcert Root certificate * @param issuercert Certificate of issuer for certificate(s) in question */ public OCSPQuery(X509Certificate rootcert, X509Certificate issuercert) { this.issuercert = issuercert; this.rootcert = rootcert; map = new HashMap(); cs = null; } static { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); } /** * Return reason message for reason code * * @param reason code from certStatus() * @return String message */ public static String reasonText(int reason) { String rt = "Invalid reason code"; switch (reason) { case GOOD: rt = "Good"; break; case UNKNOWN: rt = "Unknown"; break; case REVOKED: rt = "Revoked(0)"; break; case KEYCOMPROMISE: rt = "Key compromise(1)"; break; case CACOMPROMISE: rt = "CA compromise(2)"; break; case AFFILIATIONCHANGED: rt = "Affilitation changed(3)"; break; case SUPERSEDED: rt = "Superseded(4)"; break; case CESSATIONOFOPERATION: rt = "Cessation of operation(5)"; break; case CERTIFICATEHOLD: rt = "Certificate hold(6)"; break; case REMOVEFROMCRL: rt = "Remove from CRL(8)"; break; case PRIVILEGEWITHDRAWN: rt = "Privilege withdrawn(9)"; break; case AACOMPROMISE: rt = "AA compromise(10)"; break; } return rt; } /** * Note that this will accept only one certificate at a time. * Add certificate to query * * @param cert Certificate to add to query * @throws OCSPQueryException Certificate does not match with issuer certificate */ public void addCertificate(X509Certificate cert) throws OCSPQueryException { try { cert.verify(issuercert.getPublicKey()); CertificateID certid = new CertificateID(CertificateID.HASH_SHA1, issuercert, cert.getSerialNumber()); m_UserCert = cert; map.put(certid, cert); } catch (Exception e) { // e.printStackTrace(); throw new OCSPQueryException(e.toString()); } } /** * Get DER encoded request * @return DER encoded request * @throws OCSPQueryException */ public byte[] getRequest() throws OCSPQueryException { CertificateID certid; byte[] request = null; try { // Get request generator engine and add certificates OCSPReqGenerator gen = new OCSPReqGenerator(); Iterator iter = map.keySet().iterator(); while (iter.hasNext()) { certid = (CertificateID) iter.next(); gen.addRequest(certid); } // Get DER encoded request request = gen.generate().getEncoded(); } catch (Exception e) { throw new OCSPQueryException(e.toString()); } return request; } /** * Return DER encoded response received from OCSP responder in execute() method * * @return DER encoded response * @throws OCSPQueryException */ public byte[] getResponse() throws OCSPQueryException { return ocspResponse; } /** * Post HTTP request to OCSP responder * * @param urlstring URL of OCSP responder * @param request DER encoded request * @param header Array of custom header field which replace automatic values. Must * be in the format "field: value". * @return DER encoded response * @throws OCSPQueryException */ public byte[] post(String urlstring, byte[] request, String[] header) throws OCSPQueryException { try { URL url = new URL(urlstring); HttpURLConnection con = (HttpURLConnection) url.openConnection(); // Get TLV encoded request and transmitt using HTTP connection con.setAllowUserInteraction(false); con.setDoInput(true); con.setDoOutput(true); con.setUseCaches(false); con.setInstanceFollowRedirects(false); con.setRequestMethod("POST"); if (header == null) { con.setRequestProperty("Content-Length", Integer.toString(request.length)); con.setRequestProperty("Content-Type", "application/ocsp-request"); } else { for (int i = 0; i < header.length; i++) { String field = header[i]; int pos = field.indexOf(':'); if (pos != -1) { String key = field.substring(0, pos); String value = field.substring(pos + 2); con.setRequestProperty(key, value); } else { throw new OCSPQueryException("HTTP header must be in format '<key>: <value>'"); } } } con.connect(); OutputStream os = con.getOutputStream(); os.write(request); os.close(); if (con.getResponseCode() != HttpURLConnection.HTTP_OK) { throw new OCSPQueryException( "Server did not respond with HTTP_OK(200) but with " + con.getResponseCode()); } if ((con.getContentType() == null) || !con.getContentType().equals("application/ocsp-response")) { throw new OCSPQueryException("Response MIME type is not application/ocsp-response"); } // Read response InputStream reader = con.getInputStream(); int resplen = con.getContentLength(); ocspResponse = new byte[resplen]; int offset = 0; int bread; while ((resplen > 0) && (bread = reader.read(ocspResponse, offset, resplen)) != -1) { offset += bread; resplen -= bread; } reader.close(); con.disconnect(); if (resplen > 0) { throw new OCSPQueryException("Could not read full response"); } } catch (OCSPQueryException e) { throw e; } catch (Exception e) { throw new OCSPQueryException("Error talking to OCSP responder: " + e.getMessage()); } cs = null; // Decoding yet to be done; return ocspResponse; } /** * Post HTTP request to OCSP responder * * @param urlstring URL of OCSP responder * @param request DER encoded request * @return DER encoded response * @throws OCSPQueryException */ public byte[] post(String urlstring, byte[] request) throws OCSPQueryException { return post(urlstring, request, null); } /** * Decode OCSP response, verify signature and store certificate status internally * * @param response * @throws OCSPQueryException */ public void decodeResponse(byte[] response) throws OCSPQueryException { int i; try { ocspResponse = response; OCSPResp ocspresp = new OCSPResp(response); switch (ocspresp.getStatus()) { case OCSPRespStatus.SUCCESSFUL: break; case OCSPRespStatus.INTERNAL_ERROR: throw new OCSPQueryException("Internal OCSP server error"); case OCSPRespStatus.MALFORMED_REQUEST: throw new OCSPQueryException("Malformed request"); case OCSPRespStatus.SIGREQUIRED: throw new OCSPQueryException("Signature required for request"); case OCSPRespStatus.TRY_LATER: throw new OCSPQueryException("The server was too busy to answer"); case OCSPRespStatus.UNAUTHORIZED: throw new OCSPQueryException("Not authorised to access server"); default: throw new OCSPQueryException("Unknown OCSPResponse status code"); } // Decode BasicOCSP response (inner structure) BasicOCSPResp bresp = (BasicOCSPResp) ocspresp.getResponseObject(); if (bresp == null) { throw new OCSPQueryException("No BasicOCSPResponse found in response"); } // Obtain certificate chain from response X509Certificate[] ocspcerts = bresp.getCerts("BC"); // Verify all except trusted anchor for (i = 0; i < ocspcerts.length - 1; i++) { ocspcerts[i].verify(ocspcerts[i + 1].getPublicKey()); } if (rootcert != null) { ocspcerts[i].verify(rootcert.getPublicKey()); } /* Die Validierung des Zertifikatspfad schlgt fehl, da das OCSP Zertifikat eine * ExtendedKeyUsage spezifiziert, BouncyCastle die Extension aber nicht untersttzt. CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC"); CertPath cp = cf.generateCertPath(certlist); CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC"); PKIXParameters pkixparam = new PKIXParameters(rootcert); pkixparam.setRevocationEnabled(false); PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)cpv.validate(cp, pkixparam); */ if (!bresp.verify(ocspcerts[0].getPublicKey(), "BC")) { throw new OCSPQueryException("OCSP Signature verification failed"); } // Store result in hashmap for later status queries SingleResp[] certstat = bresp.getResponses(); cs = new HashMap(certstat.length); for (i = 0; i < certstat.length; i++) { CertificateID certID = certstat[i].getCertID(); cs.put(certID, certstat[i]); } } catch (OCSPQueryException e) { throw e; } catch (Exception e) { throw new OCSPQueryException("OCSP decode error: " + e); } } /** * Execute OCSP query using HTTP connection from issuer certificate * and verify respone * * @throws OCSPQueryException */ public void execute() throws OCSPQueryException { try { // Obtain URL from OCSP Responder Extension // byte[] ocspext = issuercert.getExtensionValue("1.3.6.1.5.5.7.1.1"); byte[] ocspext = m_UserCert.getExtensionValue("1.3.6.1.5.5.7.1.1"); ASN1Sequence asn1 = (ASN1Sequence) X509ExtensionUtil.fromExtensionValue(ocspext); asn1 = (ASN1Sequence) asn1.getObjectAt(0); ASN1TaggedObject tasn1 = (ASN1TaggedObject) asn1.getObjectAt(1); DEROctetString ostr = (DEROctetString) tasn1.getObject(); String urlstr = new String(ostr.getOctets()); ocspResponse = post(urlstr, getRequest()); decodeResponse(ocspResponse); } catch (OCSPQueryException e) { throw e; } catch (NullPointerException e) { //means the we don'yt have OCSP or is malformed, //we'll fallover to CRL throw e; } catch (Exception e) { throw new OCSPQueryException("OCSP.execute() error: " + e); } } /** * Query status for a given certificate from previous query * @param cert Certificate in question * @return OCSPQuery.<constant> status value * @throws OCSPQueryException */ public int certStatus(X509Certificate cert) throws OCSPQueryException { if (cs == null) { decodeResponse(ocspResponse); } int rc = GOOD; try { CertificateID certID = new CertificateID(CertificateID.HASH_SHA1, issuercert, cert.getSerialNumber()); SingleResp sr = (SingleResp) cs.get(certID); if (sr == null) { throw new OCSPQueryException("Certificate not contained in query"); } Object ro = sr.getCertStatus(); if (ro != null) { if (ro instanceof RevokedStatus) { RevokedStatus ri = (RevokedStatus) ro; rc = REVOKED; if (ri.hasRevocationReason()) { rc = ri.getRevocationReason() + 100; } } else if (ro instanceof UnknownStatus) { rc = UNKNOWN; } } } catch (Exception e) { throw new OCSPQueryException("OCSP.certStatus() error: " + e); } return rc; } /** * Return time of revocation if specified * * @param cert Certificate to query * @return Date and Time of revocation * * @throws OCSPQueryException */ public Date getRevocationTime(X509Certificate cert) throws OCSPQueryException { if (cs == null) { decodeResponse(ocspResponse); } Date rd = null; try { SingleResp sr = (SingleResp) cs .get(new CertificateID(CertificateID.HASH_SHA1, issuercert, cert.getSerialNumber())); Object ro = sr.getCertStatus(); if (ro != null) { if (ro instanceof RevokedStatus) { RevokedStatus ri = (RevokedStatus) ro; rd = ri.getRevocationTime(); } } } catch (Exception e) { throw new OCSPQueryException("OCSP.certStatus() error: " + e); } return rd; } }