From 5b05f3a976900b8ac542b3b325f99394c91d1c6f Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Thu, 29 Jan 2015 21:50:46 -0500 Subject: [PATCH] Refactored OCSPClient. The OCSPClient CLI has been refactored into an OCSP utility class such that the functionality can be reused. https://fedorahosted.org/pki/ticket/1250 --- .../src/com/netscape/cmstools/OCSPClient.java | 412 +++++++++------------ .../templates/pki_java_command_wrapper.in | 3 + base/util/src/CMakeLists.txt | 17 +- base/util/src/com/netscape/cmsutil/ocsp/OCSP.java | 170 +++++++++ 4 files changed, 364 insertions(+), 238 deletions(-) create mode 100644 base/util/src/com/netscape/cmsutil/ocsp/OCSP.java diff --git a/base/java-tools/src/com/netscape/cmstools/OCSPClient.java b/base/java-tools/src/com/netscape/cmstools/OCSPClient.java index ce0e853cdfeb98c4521d743e3f092205f342a2ca..f03b89a32508ce68dab47c99a96ee0506b21389b 100644 --- a/base/java-tools/src/com/netscape/cmstools/OCSPClient.java +++ b/base/java-tools/src/com/netscape/cmstools/OCSPClient.java @@ -17,288 +17,226 @@ // --- END COPYRIGHT BLOCK --- package com.netscape.cmstools; -import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.Socket; -import java.security.MessageDigest; - -import netscape.security.x509.X500Name; -import netscape.security.x509.X509CertImpl; -import netscape.security.x509.X509Key; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.UnknownHostException; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.PosixParser; import org.mozilla.jss.CryptoManager; -import org.mozilla.jss.asn1.INTEGER; -import org.mozilla.jss.asn1.NULL; -import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; -import org.mozilla.jss.asn1.OCTET_STRING; -import org.mozilla.jss.asn1.SEQUENCE; -import org.mozilla.jss.crypto.X509Certificate; -import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; import com.netscape.cmsutil.ocsp.BasicOCSPResponse; -import com.netscape.cmsutil.ocsp.CertID; import com.netscape.cmsutil.ocsp.CertStatus; import com.netscape.cmsutil.ocsp.GoodInfo; +import com.netscape.cmsutil.ocsp.OCSP; import com.netscape.cmsutil.ocsp.OCSPRequest; import com.netscape.cmsutil.ocsp.OCSPResponse; -import com.netscape.cmsutil.ocsp.Request; import com.netscape.cmsutil.ocsp.ResponseBytes; import com.netscape.cmsutil.ocsp.ResponseData; import com.netscape.cmsutil.ocsp.RevokedInfo; import com.netscape.cmsutil.ocsp.SingleResponse; -import com.netscape.cmsutil.ocsp.TBSRequest; import com.netscape.cmsutil.ocsp.UnknownInfo; -import com.netscape.cmsutil.util.Utils; /** - * This class implements a OCSP client for testing. + * This class implements an OCSP command line interface. * * @version $Revision$, $Date$ */ public class OCSPClient { - private String _host = null; - private int _port = 0; - - public OCSPClient(String host, int port, String dbdir) - throws Exception { - _host = host; - _port = port; - CryptoManager.initialize(dbdir); - } - public void send(String uri, String nickname, int serialno, String output) - throws Exception { - CryptoManager manager = CryptoManager.getInstance(); - X509Certificate caCert = manager.findCertByNickname(nickname); - OCSPRequest request = getOCSPRequest(caCert, serialno); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - request.encode(os); - byte request_data[] = os.toByteArray(); - sendOCSPRequest(uri, _host, _port, request_data, output); + public static Options createOptions() throws UnknownHostException { + + Options options = new Options(); + + Option option = new Option("d", true, "Security database location (default: current directory)"); + option.setArgName("database"); + options.addOption(option); + + option = new Option("h", true, "OCSP server hostname (default: "+ InetAddress.getLocalHost().getCanonicalHostName() + ")"); + option.setArgName("hostname"); + options.addOption(option); + + option = new Option("p", true, "OCSP server port number (default: 8080)"); + option.setArgName("port"); + options.addOption(option); + + option = new Option("t", true, "OCSP service path (default: /ocsp/ee/ocsp)"); + option.setArgName("path"); + options.addOption(option); + + option = new Option("c", true, "CA certificate nickname (default: CA Signing Certificate)"); + option.setArgName("nickname"); + options.addOption(option); + + option = new Option("n", true, "Number of submission times (default: 1)"); + option.setArgName("times"); + options.addOption(option); + + option = new Option(null, "serial", true, "Serial number of certificate to be checked"); + option.setArgName("serial"); + options.addOption(option); + + option = new Option(null, "input", true, "Input file containing DER-encoded OCSP request"); + option.setArgName("input"); + options.addOption(option); + + option = new Option(null, "output", true, "Output file to store DER-encoded OCSP response"); + option.setArgName("output"); + options.addOption(option); + + options.addOption("v", "verbose", false, "Run in verbose mode."); + options.addOption(null, "help", false, "Show help message."); + + return options; } - public void sendRequestData(String uri, String nickname, byte request_data[], String output) - throws Exception { - sendOCSPRequest(uri, _host, _port, request_data, output); + public static void printHelp() throws Exception { + System.out.println("Usage: OCSPClient [OPTIONS]"); + System.out.println(); + System.out.println("Options:"); + System.out.println(" -d Security database location (default: current directory)"); + System.out.println(" -h OCSP server hostname (default: "+ InetAddress.getLocalHost().getCanonicalHostName() + ")"); + System.out.println(" -p OCSP server port number (default: 8080)"); + System.out.println(" -t OCSP service path (default: /ocsp/ee/ocsp)"); + System.out.println(" -c CA certificate nickname (defaut: CA Signing Certificate)"); + System.out.println(" -n Number of submission times (default: 1)"); + System.out.println(); + System.out.println(" --serial Serial number of certificate to be checked"); + System.out.println(" --input Input file containing DER-encoded OCSP request"); + System.out.println(" --output Output file to store DER-encoded OCSP response"); + System.out.println(); + System.out.println(" -v, --verbose Run in verbose mode."); + System.out.println(" --help Show help message."); } - public OCSPRequest getOCSPRequest(X509Certificate caCert, int serialno) - throws Exception { - MessageDigest md = MessageDigest.getInstance("SHA"); - - // calculate issuer key hash - X509CertImpl x509Cert = new X509CertImpl(caCert.getEncoded()); - X509Key x509key = (X509Key) x509Cert.getPublicKey(); - byte issuerKeyHash[] = md.digest(x509key.getKey()); - - // calculate name hash - X500Name name = (X500Name) x509Cert.getSubjectDN(); - byte issuerNameHash[] = md.digest(name.getEncoded()); - // constructing the OCSP request - CertID certid = new CertID( - new AlgorithmIdentifier( - new OBJECT_IDENTIFIER("1.3.14.3.2.26"), new NULL()), - new OCTET_STRING(issuerNameHash), - new OCTET_STRING(issuerKeyHash), - new INTEGER(serialno)); - Request request = new Request(certid, null); - SEQUENCE requestList = new SEQUENCE(); - requestList.addElement(request); - TBSRequest tbsRequest = new TBSRequest(null, null, requestList, null); - return new OCSPRequest(tbsRequest, null); + public static void printError(String message) { + System.err.println("ERROR: " + message); + System.err.println("Try 'OCSPClient --help' for more information."); } - public void sendOCSPRequest(String uri, String host, int port, - byte request_data[], String output) throws Exception { - Socket socket = null; - DataOutputStream dos = null; - InputStream iiss = null; - FileOutputStream fof = null; - BufferedInputStream fis = null; + public static void main(String args[]) throws Exception { + + Options options = createOptions(); + CommandLine cmd = null; + try { - socket = new Socket(host, port); - - // send request - System.out.println("URI: " + uri); - - dos = new DataOutputStream(socket.getOutputStream()); - dos.writeBytes("POST " + uri + " HTTP/1.0\r\n"); - dos.writeBytes("Content-length: " + request_data.length + "\r\n"); - dos.writeBytes("\r\n"); - dos.write(request_data); - dos.flush(); - - System.out.println("Data Length: " + request_data.length); - System.out.println("Data: " + Utils.base64encode(request_data)); - - iiss = socket.getInputStream(); - fof = new FileOutputStream(output); - boolean startSaving = false; - int sum = 0; - boolean hack = false; - try { - while (true) { - int r = iiss.read(); - if (r == -1) - break; - if (r == 10) { - sum++; - } - if (sum == 6) { - startSaving = true; - continue; - } - if (startSaving) { - if (hack) { - fof.write(r); - } - if (hack == false) { - hack = true; - } - } - } // while - } catch (IOException e) { - } - // parse OCSPResponse - fis = new BufferedInputStream( - new FileInputStream(output)); - OCSPResponse resp = (OCSPResponse) - OCSPResponse.getTemplate().decode(fis); - ResponseBytes bytes = resp.getResponseBytes(); - BasicOCSPResponse basic = (BasicOCSPResponse) - BasicOCSPResponse.getTemplate().decode( - new ByteArrayInputStream(bytes.getResponse().toByteArray())); - ResponseData rd = basic.getResponseData(); - for (int i = 0; i < rd.getResponseCount(); i++) { - SingleResponse rd1 = rd.getResponseAt(i); - if (rd1 == null) { - throw new Exception("No OCSP Response data."); - } - System.out.println("CertID.serialNumber=" + - rd1.getCertID().getSerialNumber()); - CertStatus status1 = rd1.getCertStatus(); - if (status1 instanceof GoodInfo) { - System.out.println("CertStatus=Good"); - } - if (status1 instanceof UnknownInfo) { - System.out.println("CertStatus=Unknown"); - } - if (status1 instanceof RevokedInfo) { - System.out.println("CertStatus=Revoked"); - } - } - } finally { - if (socket != null) - socket.close(); - if (dos != null) - dos.close(); - if (iiss != null) - iiss.close(); - if (fof != null) - fof.close(); - if (fis != null) - fis.close(); + CommandLineParser parser = new PosixParser(); + cmd = parser.parse(options, args); + } catch (Exception e) { + printError(e.getMessage()); + System.exit(1); } - } - - public static void printUsage() { - System.out.println("Usage: OCSPClient " + - " "); - System.out.println(" = OCSP server hostname"); - System.out.println(" = OCSP server port number"); - System.out.println(" = Certificate Database Directory"); - System.out.println(" = Nickname of CA Certificate"); - System.out.println( - " = Serial Number Being Checked, Or Name of file that contains the request"); - System.out.println(" = Filename of Response in DER encoding"); - System.out.println(" = Submit Request Multiple Times"); - System.out.println(" [] = OCSP Service URI (i.e. /ocsp/ee/ocsp)"); - } - public static void main(String args[]) { - if (args.length != 7 && args.length != 8) { - System.out.println("ERROR: Invalid number of arguments - got " - + args.length + " expected 7!"); - for (int i = 0; i < args.length; i++) { - System.out.println("arg[" + i + "]=" + args[i]); - } - printUsage(); + if (cmd.hasOption("help")) { + printHelp(); System.exit(0); } - String host = args[0]; - int port = -1; - try { - port = Integer.parseInt(args[1]); - } catch (Exception e) { - System.out.println("Error: Invalid Port Number"); - printUsage(); - System.exit(0); + boolean verbose = cmd.hasOption("v"); + + String databaseDir = cmd.getOptionValue("d", "."); + String hostname = cmd.getOptionValue("h", InetAddress.getLocalHost().getCanonicalHostName()); + int port = Integer.parseInt(cmd.getOptionValue("p", "8080")); + String path = cmd.getOptionValue("t", "/ocsp/ee/ocsp"); + String caNickname = cmd.getOptionValue("c", "CA Signing Certificate"); + int times = Integer.parseInt(cmd.getOptionValue("n", "1")); + + String input = cmd.getOptionValue("input"); + String serial = cmd.getOptionValue("serial"); + String output = cmd.getOptionValue("output"); + + if (times < 1) { + printError("Invalid number of submission times"); + System.exit(1); } - String dbdir = args[2]; - String nickname = args[3]; - int serialno = -1; - byte data[] = null; + try { - serialno = Integer.parseInt(args[4]); - } catch (Exception e) { - FileInputStream fis = null; - try { - System.out.println("Warning: Serial Number not found. It may be a filename."); - /* it could be a file name */ - fis = new FileInputStream(args[4]); - System.out.println("File Size: " + fis.available()); - data = new byte[fis.available()]; - fis.read(data); - } catch (Exception e1) { - System.out.println("Error: Invalid Serial Number or File Name"); - printUsage(); - System.exit(0); - } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e1) { - e1.printStackTrace(); - } + if (verbose) System.out.println("Initializing security database"); + CryptoManager.initialize(databaseDir); + + String url = "http://" + hostname + ":" + port + path; + + OCSP ocsp = new OCSP(); + ocsp.setVerbose(verbose); + + OCSPRequest request; + if (serial != null) { + if (verbose) System.out.println("Creating request for serial number " + serial); + + BigInteger serialNumber = new BigInteger(serial); + request = ocsp.createRequest(caNickname, serialNumber); + + } else if (input != null) { + if (verbose) System.out.println("Loading request from " + input); + + try (FileInputStream in = new FileInputStream(input)) { + byte[] data = new byte[in.available()]; + in.read(data); + request = ocsp.createRequest(data); } + + } else { + throw new Exception("Missing serial number of input file."); } - } - String output = args[5]; - int times = 1; - try { - times = Integer.parseInt(args[6]); - } catch (Exception e) { - System.out.println("Error: Invalid Times"); - printUsage(); - System.exit(0); - } - String uri = "/ocsp/ee/ocsp"; - if (args.length > 7) { - uri = args[7]; - } - try { - OCSPClient client = - new OCSPClient(host, port, dbdir); + + OCSPResponse response = null; for (int i = 0; i < times; i++) { - if (data != null) { - client.sendRequestData(uri, nickname, data, output); - } else { - client.send(uri, nickname, serialno, output); + + if (verbose) System.out.println("Submitting OCSP request"); + response = ocsp.submitRequest(url, request); + + ResponseBytes bytes = response.getResponseBytes(); + BasicOCSPResponse basic = (BasicOCSPResponse)BasicOCSPResponse.getTemplate().decode( + new ByteArrayInputStream(bytes.getResponse().toByteArray())); + + ResponseData rd = basic.getResponseData(); + for (int j = 0; j < rd.getResponseCount(); j++) { + SingleResponse sr = rd.getResponseAt(j); + + if (sr == null) { + throw new Exception("No OCSP Response data."); + } + + System.out.println("CertID.serialNumber=" + + sr.getCertID().getSerialNumber()); + + CertStatus status = sr.getCertStatus(); + if (status instanceof GoodInfo) { + System.out.println("CertStatus=Good"); + + } else if (status instanceof UnknownInfo) { + System.out.println("CertStatus=Unknown"); + + } else if (status instanceof RevokedInfo) { + System.out.println("CertStatus=Revoked"); + } } } - System.out.println("Success: Output " + output); + + if (output != null) { + if (verbose) System.out.println("Storing response into " + output); + + try (FileOutputStream out = new FileOutputStream(output)) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + response.encode(os); + out.write(os.toByteArray()); + } + + System.out.println("Success: Output " + output); + } + } catch (Exception e) { - System.out.println("Error: " + e.toString()); - printUsage(); - System.exit(0); + if (verbose) e.printStackTrace(); + printError(e.getMessage()); + System.exit(1); } } } diff --git a/base/java-tools/templates/pki_java_command_wrapper.in b/base/java-tools/templates/pki_java_command_wrapper.in index 4474084cfe519d0adb1e1eb42ce0ddf323509666..09e059edbbca5d90c1a89261d1f300c87d9c05b4 100644 --- a/base/java-tools/templates/pki_java_command_wrapper.in +++ b/base/java-tools/templates/pki_java_command_wrapper.in @@ -134,6 +134,9 @@ CP=${JNI_JAR_DIR}/jss4.jar CP=/usr/share/java/commons-cli.jar:${CP} CP=/usr/share/java/commons-codec.jar:${CP} CP=/usr/share/java/commons-io.jar:${CP} +CP=/usr/share/java/commons-logging.jar:${CP} +CP=/usr/share/java/httpcomponents/httpclient.jar:${CP} +CP=/usr/share/java/httpcomponents/httpcore.jar:${CP} CP=/usr/share/java/ldapjdk.jar:${CP} CP=/usr/share/java/${PRODUCT}/pki-nsutil.jar:${CP} CP=/usr/share/java/${PRODUCT}/pki-cmsutil.jar:${CP} diff --git a/base/util/src/CMakeLists.txt b/base/util/src/CMakeLists.txt index 35b8e0a5b55f903876c2f48b1d23feef43ea1bdf..efef8af536bad8a2410c7b64abbfa8868e501612 100644 --- a/base/util/src/CMakeLists.txt +++ b/base/util/src/CMakeLists.txt @@ -30,6 +30,20 @@ find_file(COMMONS_CODEC_JAR /usr/share/java ) +find_file(HTTPCLIENT_JAR + NAMES + httpclient.jar + PATHS + /usr/share/java/httpcomponents +) + +find_file(HTTPCORE_JAR + NAMES + httpcore.jar + PATHS + /usr/share/java/httpcomponents +) + find_file(XALAN_JAR NAMES xalan-j2.jar @@ -92,7 +106,8 @@ javac(pki-cmsutil-classes SOURCES com/netscape/cmsutil/*.java CLASSPATH - ${APACHE_COMMONS_LANG_JAR} ${LDAPJDK_JAR} ${XALAN_JAR} ${XERCES_JAR} + ${APACHE_COMMONS_LANG_JAR} ${HTTPCORE_JAR} ${HTTPCLIENT_JAR} + ${LDAPJDK_JAR} ${XALAN_JAR} ${XERCES_JAR} ${JSS_JAR} ${COMMONS_CODEC_JAR} OUTPUT_DIR ${CMAKE_BINARY_DIR}/classes diff --git a/base/util/src/com/netscape/cmsutil/ocsp/OCSP.java b/base/util/src/com/netscape/cmsutil/ocsp/OCSP.java new file mode 100644 index 0000000000000000000000000000000000000000..64614ceacf7473e9bcc66b1e76c47df42f993dab --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/ocsp/OCSP.java @@ -0,0 +1,170 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program 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 this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.ocsp; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.math.BigInteger; +import java.security.MessageDigest; + +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CertImpl; +import netscape.security.x509.X509Key; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.util.EntityUtils; +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.NULL; +import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.crypto.X509Certificate; +import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; + +import com.netscape.cmsutil.util.Utils; + +/** + * This class implements an OCSP utility. + * + * @version $Revision$, $Date$ + */ +public class OCSP { + + public boolean verbose; + + public OCSP() { + } + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + public boolean isVerbose() { + return verbose; + } + + /** + * Create OCSP request from binary data. + */ + public OCSPRequest createRequest(byte[] data) throws Exception { + OCSPRequest.Template template = new OCSPRequest.Template(); + return (OCSPRequest)template.decode(new ByteArrayInputStream(data)); + } + + /** + * Create OCSP request from nickname of CA certificate and serial number + * of certificate to be checked. + */ + public OCSPRequest createRequest(String caNickname, BigInteger serialNumber) + throws Exception { + + CryptoManager manager = CryptoManager.getInstance(); + X509Certificate caCert = manager.findCertByNickname(caNickname); + X509CertImpl cert = new X509CertImpl(caCert.getEncoded()); + + X500Name issuerName = (X500Name)cert.getSubjectDN(); + X509Key issuerKey = (X509Key)cert.getPublicKey(); + + return createRequest(issuerName, issuerKey, serialNumber); + } + + /** + * Create OCSP request from issuer name, issuer public key, and serial number + * of certificate to be checked. + */ + public OCSPRequest createRequest(X500Name issuerName, X509Key issuerKey, BigInteger serialNumber) + throws Exception { + + MessageDigest md = MessageDigest.getInstance("SHA"); + + // calculate hashes + byte issuerNameHash[] = md.digest(issuerName.getEncoded()); + byte issuerKeyHash[] = md.digest(issuerKey.getKey()); + + // constructing the OCSP request + CertID certID = new CertID( + new AlgorithmIdentifier( + new OBJECT_IDENTIFIER("1.3.14.3.2.26"), new NULL()), + new OCTET_STRING(issuerNameHash), + new OCTET_STRING(issuerKeyHash), + new INTEGER(serialNumber)); + + Request request = new Request(certID, null); + + SEQUENCE requestList = new SEQUENCE(); + requestList.addElement(request); + + TBSRequest tbsRequest = new TBSRequest(null, null, requestList, null); + + return new OCSPRequest(tbsRequest, null); + } + + public OCSPResponse submitRequest(String url, OCSPRequest request) throws Exception { + + if (verbose) System.out.println("URL: " + url); + + HttpClient httpClient = new DefaultHttpClient(); + + try { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + request.encode(os); + byte[] requestData = os.toByteArray(); + + if (verbose) { + System.out.println("Data Length: " + requestData.length); + System.out.println("Data: " + Utils.base64encode(requestData)); + } + + ByteArrayEntity requestEntity = new ByteArrayEntity(requestData); + requestEntity.setContentType(ContentType.APPLICATION_OCTET_STREAM.getMimeType()); + + HttpPost httpPost = new HttpPost(url); + httpPost.setEntity(requestEntity); + + HttpResponse response = httpClient.execute(httpPost); + HttpEntity responseEntity = response.getEntity(); + + try (InputStream is = responseEntity.getContent()) { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + int b; + while ((b = is.read()) != -1) { + buffer.write(b); + } + + // construct OCSP response + return (OCSPResponse)OCSPResponse.getTemplate().decode( + new ByteArrayInputStream(buffer.toByteArray())); + + } finally { + EntityUtils.consume(responseEntity); + } + + } finally { + httpClient.getConnectionManager().shutdown(); + } + } +} -- 1.8.4.2