Package org.ejbca.core.protocol.ocsp

Source Code of org.ejbca.core.protocol.ocsp.OCSPUnidClient$SimpleVerifier

/*************************************************************************
*                                                                       *
*  EJBCA: 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 java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

import org.apache.commons.lang.ArrayUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.ocsp.BasicOCSPResp;
import org.bouncycastle.ocsp.CertificateID;
import org.bouncycastle.ocsp.OCSPException;
import org.bouncycastle.ocsp.OCSPReq;
import org.bouncycastle.ocsp.OCSPReqGenerator;
import org.bouncycastle.ocsp.OCSPResp;
import org.bouncycastle.ocsp.RespID;
import org.ejbca.util.Base64;
import org.ejbca.util.CertTools;
import org.ejbca.util.keystore.KeyTools;

/** A simple OCSP lookup client used to query the OCSPUnidExtension. Attributes needed to call the client is a keystore
* issued from the same CA as has issued the TLS server certificate of the OCSP/Lookup server.
* The keystore must be a PKCS#12 file.
* If a keystore is not used, regular OCSP requests can still be made, using normal http.
*
* If requesting an Fnr and the fnr rturned is null, even though the OCSP code is good there can be several reasons:
* 1.The client was not authorized to request an Fnr
* 2.There was no Unid Fnr mapping available
* 3.There was no Unid in the certificate (serialNumber DN component)
*
* @author Tomas Gustavsson, PrimeKey Solutions AB
* @version $Id: OCSPUnidClient.java 8261 2009-11-05 16:33:18Z jeklund $
*
*/
public class OCSPUnidClient {

    final public static String requestDirectory = "ocspRequests";
    final private String httpReqPath;
    final private KeyStore ks;
    final private String passphrase;
    final private PrivateKey signKey;
    final private X509Certificate[] certChain;
    final private X509Extensions extensions;
    final private byte nonce[];
    private static final Logger m_log = Logger.getLogger(OCSPUnidClient.class);
 
  /**
   * @param keystore KeyStore client keystore used to authenticate TLS client authentication, or null if TLS is not used
   * @param pwd String password for the key store, or null if no keystore is used
   * @param ocspurl String url to the OCSP server, or null if we should try to use the AIA extension from the cert; e.g. http://127.0.0.1:8080/ejbca/publicweb/status/ocsp (or https for TLS)
   * @param certs certificate chain to signing key
   * @param _signKey signing key
   * @param getfnr true if FNR should be fetched
   * @throws NoSuchAlgorithmException
   */
  private OCSPUnidClient(KeyStore keystore, String pwd, String ocspurl, Certificate[] certs, PrivateKey _signKey, boolean getfnr) throws NoSuchAlgorithmException {
      this.httpReqPath = ocspurl;
      this.passphrase = pwd;
      this.ks = keystore;
      this.signKey = _signKey;
      this.certChain = certs!=null ? Arrays.asList(certs).toArray(new X509Certificate[0]) : null;
        this.nonce = new byte[16];
      {
          final Hashtable exts = new Hashtable();
          final SecureRandom randomSource = SecureRandom.getInstance("SHA1PRNG");
          randomSource.nextBytes(nonce);
          final X509Extension nonceext = new X509Extension(false, new DEROctetString(nonce));
          exts.put(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, nonceext);
          // Don't bother adding Unid extension if we are not using client authentication
          if ( getfnr ) {
              X509Extension ext = new X509Extension(false, new DEROctetString(new FnrFromUnidExtension("1")));
              exts.put(FnrFromUnidExtension.FnrFromUnidOid, ext);
          }
          extensions = new X509Extensions(exts);
      }
      CertTools.installBCProvider();
  }
 
  /**
     * @param ksfilename String Filename of PKCS#12 keystore used to authenticate TLS client authentication, or null if TLS is not used
     * @param pwd String password for the key store,or null if no keystore is used
     * @param ocspurl String url to the OCSP server, or null if we should try to use the AIA extension from the cert; e.g. http://127.0.0.1:8080/ejbca/publicweb/status/ocsp (or https for TLS)
   * @return the client to use
     * @throws Exception
   */
  public static OCSPUnidClient getOCSPUnidClient(String ksfilename, String pwd, String ocspurl, boolean doSignRequst, boolean getfnr) throws Exception {
      if ( doSignRequst && ksfilename==null ) {
            throw new Exception("You got to give the path name for a keystore to use when using signing.");
      }
        final KeyStore ks;
        if (ksfilename != null) {
          ks = KeyStore.getInstance("PKCS12", "BC");
          ks.load(new FileInputStream(ksfilename), pwd.toCharArray());     
            Enumeration en = ks.aliases();
            String alias = null;
            // If this alias is a trusted certificate entry, we don't want to fetch that, we want the key entry
            while ( (alias==null || ks.isCertificateEntry(alias)) && en.hasMoreElements() ) {
                alias = (String)en.nextElement();
            }
            final Certificate[] certs = KeyTools.getCertChain(ks, alias);
            if (certs == null) {
                throw new IOException("Can not find a certificate entry in PKCS12 keystore for alias "+alias);
            }
            final PrivateKey privateKey = doSignRequst ? (PrivateKey)ks.getKey(alias, null) : null;
            return new OCSPUnidClient(ks, pwd, ocspurl, certs, privateKey, getfnr);
    } else {
            return new OCSPUnidClient(null, null, ocspurl, null, null, getfnr);
    }
  }
  /**
   * @param cert X509Certificate to query, the DN should contain serialNumber which is Unid to be looked up
   * @param cacert CA certificate that issued the certificate to be queried
     * @param useGet if true GET will be used instead of POST as HTTP method
   * @return OCSPUnidResponse conatining the response and the fnr, can contain and an error code and the fnr can be null, never returns null.
   * @throws OCSPException
   * @throws IOException
   * @throws GeneralSecurityException
   */
  public OCSPUnidResponse lookup(Certificate cert, Certificate cacert, boolean useGet) throws OCSPException, IOException, GeneralSecurityException {
        return lookup( CertTools.getSerialNumber(cert), cacert, useGet);
    }
    /**
     * @param serialNr serial number of the certificate to verify
     * @param cacert issuer of the certificate to verify
     * @param useGet if true GET will be used instead of POST as HTTP method
     * @return response can contain and an error code but the fnr is allways null, never returns null.
     * @throws OCSPException
     * @throws IllegalArgumentException
     * @throws IOException
     * @throws GeneralSecurityException
     */
    public OCSPUnidResponse lookup(BigInteger serialNr, Certificate cacert, boolean useGet) throws OCSPException, IOException, GeneralSecurityException {
        if (this.httpReqPath == null) {
            // If we didn't pass a url to the constructor and the cert does not have the URL, we will fail...
            OCSPUnidResponse ret = new OCSPUnidResponse();
            ret.setErrorCode(OCSPUnidResponse.ERROR_NO_OCSP_URI);
            return ret;
        }
        final OCSPReqGenerator gen = new OCSPReqGenerator();
        final CertificateID certId = new CertificateID(CertificateID.HASH_SHA1, (X509Certificate)cacert, serialNr);
//        System.out.println("Generating CertificateId:\n"
//                + " Hash algorithm : '" + certId.getHashAlgOID() + "'\n"
//                + " CA certificate\n"
//                + "      CA SubjectDN: '" + cacert.getSubjectDN().getName() + "'\n"
//                + "      SerialNumber: '" + cacert.getSerialNumber().toString(16) + "'\n"
//                + " CA certificate hashes\n"
//                + "      Name hash : '" + new String(Hex.encode(certId.getIssuerNameHash())) + "'\n"
//                + "      Key hash  : '" + new String(Hex.encode(certId.getIssuerKeyHash())) + "'\n");
        gen.addRequest(certId);
        if (!useGet) {
            // Add a nonce to the request
            gen.setRequestExtensions(this.extensions);         
        }
        final OCSPReq req;
        if ( this.signKey!=null ) {
            final X509Certificate localCertChain[] = this.certChain!=null ? this.certChain : new X509Certificate[] {(X509Certificate)cacert};
            gen.setRequestorName(localCertChain[0].getSubjectX500Principal());
            req = gen.generate("SHA1withRSA", this.signKey, localCertChain, "BC");
        } else {
            req = gen.generate();
        }
        // write request if directory exists.
        File  ocspReqDir = new File(requestDirectory);
        if ( ocspReqDir.isDirectory() ) {
            OutputStream os = new FileOutputStream(new File( ocspReqDir, serialNr.toString()));
            os.write(req.getEncoded());
            os.close();
        }
        // Send the request and receive a BasicResponse
        return sendOCSPRequest(req.getEncoded(), cacert, useGet);
  }

    //
    // Private helper methods
    //
   
    private OCSPUnidResponse sendOCSPRequest(byte[] ocspPackage, Certificate cacert, boolean useGet) throws IOException, OCSPException, GeneralSecurityException {
      final HttpURLConnection con;
      if (useGet) {
          String b64 = new String(Base64.encode(ocspPackage, false));
          //String urls = URLEncoder.encode(b64, "UTF-8");
          //URL url = new URL(httpReqPath + '/' + urls);
          URL url = new URL(httpReqPath + '/' + b64);
            con = (HttpURLConnection)url.openConnection();
      } else {
            // POST the OCSP request
            URL url = new URL(httpReqPath);
            con = (HttpURLConnection)getUrlConnection(url);
            // we are going to do a POST
            con.setDoOutput(true);
            con.setRequestMethod("POST");
            // POST it
            con.setRequestProperty("Content-Type", "application/ocsp-request");
            OutputStream os = null;
            try {
                os = con.getOutputStream();
                os.write(ocspPackage);
            } finally {
                if (os != null) {
                  os.close();
                }
            }
      }
        final OCSPUnidResponse ret = new OCSPUnidResponse();
        ret.setHttpReturnCode(con.getResponseCode());
        if (ret.getHttpReturnCode() != 200) {
          if (ret.getHttpReturnCode() == 401) {
            ret.setErrorCode(OCSPUnidResponse.ERROR_UNAUTHORIZED);
          } else {
            ret.setErrorCode(OCSPUnidResponse.ERROR_UNKNOWN);
          }
          return ret;
        }
        final OCSPResp response; {
            final InputStream in = con.getInputStream();
            if ( in!=null ) {
                try {
                    response = new OCSPResp(in);
                } finally {
                    in.close();
                }
            } else {
                response = null;
            }
        }
        if (response == null) {
          ret.setErrorCode(OCSPUnidResponse.ERROR_NO_RESPONSE);
          return ret;
        }
        ret.setResp(response);
        final BasicOCSPResp brep = (BasicOCSPResp) response.getResponseObject();
        if ( brep==null ) {
            ret.setErrorCode(OCSPUnidResponse.ERROR_NO_RESPONSE);
            return ret;
        }
        // Compare nonces to see if the server sent the same nonce as we sent
      final byte[] noncerep = brep.getExtensionValue(OCSPObjectIdentifiers.id_pkix_ocsp_nonce.getId());
      if (noncerep != null) {
          ASN1InputStream ain = new ASN1InputStream(noncerep);
          ASN1OctetString oct = ASN1OctetString.getInstance(ain.readObject());
          boolean eq = ArrayUtils.isEquals(this.nonce, oct.getOctets());       
            if (!eq) {
              ret.setErrorCode(OCSPUnidResponse.ERROR_INVALID_NONCE);
              return ret;
            }
      }

    final RespID id = brep.getResponderId();
    final DERTaggedObject to = (DERTaggedObject)id.toASN1Object().toASN1Object();
    final RespID respId;
        final X509Certificate[] chain = brep.getCerts("BC");
        final PublicKey signerPub = chain[0].getPublicKey();
    if (to.getTagNo() == 1) {
      // This is Name
      respId = new RespID(chain[0].getSubjectX500Principal());
    } else {
      // This is KeyHash
      respId = new RespID(signerPub);
    }
    if (!id.equals(respId)) {
      // Response responderId does not match signer certificate responderId!
      ret.setErrorCode(OCSPUnidResponse.ERROR_INVALID_SIGNERID);
    }
        if (!brep.verify(signerPub, "BC")) {
          ret.setErrorCode(OCSPUnidResponse.ERROR_INVALID_SIGNATURE);
          return ret;
        }
        // Verify the certificate chain.
        for (int i=0; i<chain.length; i++) {
//            final X509Certificate cert1 = (X509Certificate)java.security.cert.CertificateFactory.getInstance("X509").generateCertificate(new ByteArrayInputStream(chain[i].getEncoded()));
            final X509Certificate cert1 = chain[i];
            final X509Certificate cert2 = chain[Math.min(i+1, chain.length-1)];
            try {
                cert1.verify(cert2.getPublicKey());         
            } catch (GeneralSecurityException e) {
                m_log.debug("verifying problem with", e);
                m_log.debug("cert to be verified: "+cert1);
                m_log.debug("verifying cert: "+cert2);
                ret.setErrorCode(OCSPUnidResponse.ERROR_INVALID_SIGNERCERT);
                return ret;        
            }
        }
        // the CA could either have signed the respons directly or else it might have been signed a special ocsp responder signing key with a certificate signed by the CA.
        if ( !chain[0].equals(cacert) && !chain[1].equals(cacert) ) {
            ret.setErrorCode(OCSPUnidResponse.ERROR_INVALID_SIGNERCERT);
            return ret;        
        }
        String fnr = getFnr(brep);
        if (fnr != null) {
          ret.setFnr(fnr);
        }
        return ret;
    }

    private String getFnr(BasicOCSPResp brep) throws IOException {
        byte[] fnrrep = brep.getExtensionValue(FnrFromUnidExtension.FnrFromUnidOid.getId());
        if (fnrrep == null) {
            return null;           
        }
        ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(fnrrep));
        ASN1OctetString octs = (ASN1OctetString) aIn.readObject();
        aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets()));
        FnrFromUnidExtension fnrobj = FnrFromUnidExtension.getInstance(aIn.readObject());
        return fnrobj.getFnr();
    }

    private SSLSocketFactory getSSLFactory() throws GeneralSecurityException, IOException {

        final KeyManager km[];
        final TrustManager tm[];

        // Put the key and certs in the user keystore (if available)
        if (this.ks != null) {
            final KeyManagerFactory kmf;
            kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(this.ks, this.passphrase.toCharArray());
            km = kmf.getKeyManagers();
        } else {
            km= null;
        }
        // Now make a truststore to verify the server
        if ( this.certChain!=null && this.certChain.length>0) {
            final KeyStore trustks = KeyStore.getInstance("jks");
            trustks.load(null, "foo123".toCharArray());
            // add trusted CA cert
            trustks.setCertificateEntry("trusted", this.certChain[this.certChain.length-1]);
            final TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
            tmf.init(trustks);
            tm = tmf.getTrustManagers();
        } else {
            tm = null;
        }
        if ( km==null && tm==null ) {
            return (SSLSocketFactory)SSLSocketFactory.getDefault();
        }
        final SSLContext ctx = SSLContext.getInstance("TLS");
        ctx.init(km, tm, null);

        return ctx.getSocketFactory();
    }

    /**
     *
     * @param url
     * @return URLConnection
     * @throws IOException
     * @throws GeneralSecurityException
     */
    private URLConnection getUrlConnection(URL url) throws IOException, GeneralSecurityException {
        final URLConnection orgcon = url.openConnection();
        if (orgcon instanceof HttpsURLConnection) {
            HttpsURLConnection con = (HttpsURLConnection) orgcon;
            con.setHostnameVerifier(new SimpleVerifier());
            con.setSSLSocketFactory(getSSLFactory());
        }
        return orgcon;
    }

    class SimpleVerifier implements HostnameVerifier {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }
 
}
TOP

Related Classes of org.ejbca.core.protocol.ocsp.OCSPUnidClient$SimpleVerifier

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.