Showing posts with label bouncy-castle. Show all posts
Showing posts with label bouncy-castle. Show all posts

Wednesday, April 1, 2015

Create CMS Signature using Bouncy Castle

P
This post today will show you guys how to generate Cryptographic Message Syntax (CMS) signature. CMS is the IETF's standard for cryptographically protected messages. It can be used to digitally sign, digest, authenticate or encrypt any form of digital data. CMS is based on the syntax of PKCS#7 standard.
In this tutorial, I will use Bouncy Castle (BC) library. BC is a strong cryptographic library in Java, it has enough API to do this. The version of BC I am going to use is 1.47, you can download and use this version or higher at bouncycastle.org.

Keystore is used in this post is in PKCS#12 format

String passwordP12 = "p12KeystorePass";
String pathToP12 = "/path/to/your.p12";

OK, there is complete sample

package com.it4shared.pkcs7;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;

import javax.xml.bind.DatatypeConverter;

import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Base64;

public final class PKCS7Signer {

    public PKCS7Signer() {
    }

    public KeyStore loadKeyStore(String path, String password) throws Exception {

        KeyStore keystore = KeyStore.getInstance("PKCS12");
        InputStream is = new FileInputStream(path);
        keystore.load(is, password.toCharArray());
        return keystore;
    }

    public CMSSignedDataGenerator setUpProvider(final KeyStore keystore, String pkcs12Password, String algorithm) throws Exception {
     
     Enumeration e = keystore.aliases();
     String aliasName="";
        while(e.hasMoreElements())
        {
         aliasName = e.nextElement().toString();
        }
     
     Security.addProvider(new BouncyCastleProvider());

        Certificate[] certchain = (Certificate[]) keystore.getCertificateChain(aliasName);
        
        final List certlist = new ArrayList();

        for (int i = 0, length = certchain == null ? 0 : certchain.length; i < length; i++) {
            certlist.add(certchain[i]);
        }

        Store certstore = new JcaCertStore(certlist);

        Certificate cert = keystore.getCertificate(aliasName);
        
        ContentSigner signer = new JcaContentSignerBuilder(algorithm).setProvider("BC").
                build((PrivateKey)(keystore.getKey(aliasName, pkcs12Password.toCharArray())));
        CMSSignedDataGenerator generator = new CMSSignedDataGenerator();

        generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").
                build()).build(signer, (X509Certificate) cert));

        generator.addCertificates(certstore);

        return generator;
    }

    public String SignData(final byte[] content, final CMSSignedDataGenerator generator) throws Exception {

        CMSTypedData cmsdata = new CMSProcessableByteArray(content);
        CMSSignedData signeddata = generator.generate(cmsdata, true);
        return new String(Base64.encode(signeddata.getEncoded()));
    }

    public static void main(String[] args) throws Exception {
        String passwordP12 = "p12KeystorePass";
        String pathToP12 = "/path/to/your.p12";
        String algorithm = "SHA1withRSA";
        
        PKCS7Signer signer = new PKCS7Signer();
        KeyStore keyStore = signer.loadKeyStore(pathToP12, passwordP12);
        
        CMSSignedDataGenerator signatureGenerator = signer.setUpProvider(keyStore, passwordP12, algorithm);
        String content = "signdata";
        String signedData = signer.SignData(content.getBytes("UTF-8"), signatureGenerator);
        System.out.println(signedData);
    }
To create detached signature
CMSSignedData signeddata = generator.generate(cmsdata, false);
Output:
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

Sunday, March 15, 2015

Check online certificate status through OCSP

P
The Online Certificate Status Protocol (OCSP) is an Internet protocol used for obtaining the revocation status of an X.509 digital certificate. It is described in RFC 6960 and is on the Internet standards track. It was created as an alternative to certificate revocation lists (CRL), specifically addressing certain problems associated with using CRLs in a public key infrastructure (PKI). Messages communicated via OCSP are encoded in ASN.1 and are usually communicated over HTTP. The "request/response" nature of these messages leads to OCSP servers being termed OCSP responders.

Today, I will show you how to implement that in Java. In this post, I will create a simple project and use third-party Bouncy Castle 1.47. You can download it from here.
Now we go:


package com.it4shared.ocsp;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
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.security.cert.X509Certificate;
import java.util.Vector;

import org.bouncycastle.asn1.DEROctetString;
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.OCSPReq;
import org.bouncycastle.ocsp.OCSPReqGenerator;
import org.bouncycastle.ocsp.OCSPResp;
import org.bouncycastle.ocsp.OCSPRespStatus;
import org.bouncycastle.ocsp.SingleResp;

public class Ocsp {

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  X509Certificate issuer; // the certificate which issued client certificate
  X509Certificate clientCertificate; // the client certificate which is checked online status
  boolean result = checkRevocationStatus("http://ocsp.ca.vn", clientCertificate.getSerialNumber(), issuer);
 }
 
    public static boolean checkRevocationStatus(String ocspURL, BigInteger serialNumber, X509Certificate issuerCert) {

     try {
         OCSPReq request = generateOCSPRequest(issuerCert, serialNumber); 
 
            OCSPResp ocspResponse = getOCSPResponce(ocspURL, request);
            if(OCSPRespStatus.SUCCESSFUL==ocspResponse.getStatus())
                System.out.println("server gave response fine");

            BasicOCSPResp basicResponse = (BasicOCSPResp) ocspResponse.getResponseObject();
            SingleResp[] responses = (basicResponse==null) ? null : basicResponse.getResponses();

            if (responses!=null && responses.length == 1) {
                SingleResp resp = responses[0];
                Object status = resp.getCertStatus();
                if (status instanceof org.bouncycastle.ocsp.RevokedStatus) {
                    System.out.println("OCSP Status is revoked!");
                    return false;
                }  else if (status instanceof org.bouncycastle.ocsp.UnknownStatus) {
                    System.out.println("OCSP Status is unknown!");
                    return false;
                } else {
                 System.out.println("OCSP Status is good!");
                 return true;
                }
            }
     }
     catch (Exception e) {
         e.printStackTrace();
     }
     return false;
    }
    private static OCSPResp getOCSPResponce(String serviceUrl, OCSPReq request) {
     try {
         byte[] array = request.getEncoded();
         if (serviceUrl.startsWith("http")) {
             HttpURLConnection con;
             URL url = new URL(serviceUrl);
             con = (HttpURLConnection) url.openConnection();
             con.setRequestProperty("Content-Type", "application/ocsp-request");
             con.setRequestProperty("Accept", "application/ocsp-response");
             con.setDoOutput(true);
             OutputStream out = con.getOutputStream();
             DataOutputStream dataOut = new DataOutputStream(new BufferedOutputStream(out));
             dataOut.write(array);
 
             dataOut.flush();
             dataOut.close();
 
             //Get Response
             InputStream in = (InputStream) con.getContent();
             OCSPResp ocspResponse = new OCSPResp(in);
             return ocspResponse;
         }
         else {
             System.out.println("Only http is supported for ocsp calls");
         }
     } catch (IOException e) {
         e.printStackTrace();
         System.out.println("Cannot get ocspResponse from url: "+ serviceUrl);
     }
     return null;
    }
    
    private static OCSPReq generateOCSPRequest(X509Certificate issuerCert, BigInteger serialNumber) {

        try {
            //  CertID structure is used to uniquely identify certificates that are the subject of
            // an OCSP request or response and has an ASN.1 definition. CertID structure is defined in RFC 2560
            CertificateID id = new CertificateID(CertificateID.HASH_SHA1, issuerCert, serialNumber);

            // basic request generation with nonce
            OCSPReqGenerator generator = new OCSPReqGenerator();
            generator.addRequest(id);

            // create details for nonce extension. The nonce extension is used to bind
            // a request to a response to prevent replay attacks. As the name implies,
            // the nonce value is something that the client should only use once within a reasonably small period.
            BigInteger nonce = BigInteger.valueOf(System.currentTimeMillis());
            Vector objectIdentifiers = new Vector();
            Vector values = new Vector();

            //to create the request Extension
            objectIdentifiers.add(OCSPObjectIdentifiers.id_pkix_ocsp_nonce);
            values.add(new X509Extension(false, new DEROctetString(nonce.toByteArray())));
            generator.setRequestExtensions(new X509Extensions(objectIdentifiers, values));

            return generator.generate();
        }
        catch (org.bouncycastle.ocsp.OCSPException e) {
            e.printStackTrace();
            System.out.println("Cannot generate OSCP Request with the given certificate");
        }
        return null;
    }
}



References: