Introduction

The Version 2 APIs are a result of us having to work around the idiosyncrasies of multiple providers as well as trying to find ways to accommodate some of the newer cryptography APIs that are appearing for small devices. These are often not JCE/JCA provider based, and are generally targeted to specific functionality. Requests for this kind of functionality have been increasing in respect to the lightweight APIs as well, largely due to the market evolving from the devices with single threaded VMs and a 32k footprint of 10 years ago to substantially larger and fully featured VMs we have today.

Broadly speaking the Version 2 APIs are built around the idea of operators, which are interfaces that provide specific types of cryptographic functionality. CMS, CRMF, CMP, TSP, and OCSP messages, to name but a few, as well as certificates, can then be built by providing the appropriate operators to meet the requirements of the protocol.

The result of this is that it is now possible to create things like certificates and CMS messages using the BC lightweight API, and this document is meant to provide some help in seeing how that is done.

For the purposes of the final 1.* release of BC, version 1.46, the version 2 APIs are included in the bcmail package. From 1.47, the version 2 APIs are in the pcpkix packages, and bcmail now only contains the S/MIME package.

A Simple Operator Example

Core to creating a certificate is the ability to create a signature.

In the version 2 API an example of certificate creation looks as follows:

    ContentSigner sigGen = ...;
    SubjectPublicKeyInfo subPubKeyInfo = ....;

    Date startDate = new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000);
    Date endDate = new Date(System.currentTimeMillis() + 365 * 24 * 60 * 60 * 1000);

    X509v1CertificateBuilder v1CertGen = new X509v1CertificateBuilder(
              new X500Name("CN=Test"), 
              BigInteger.ONE, 
              startDate, endDate, 
              new X500Name("CN=Test"), 
              subPubKeyInfo);
        
    X509CertificateHolder certHolder = v1CertGen.build(sigGen); 

We'll start by looking at sigGen first. In a situation where you're using the JCA a builder is provider for creating objects with the ContentSigner interface which have underlying implementations using the JCA. This class is the JcaContentSignerBuilder and in the case where we wanted to create a SHA1withRSA signer, we might create sigGen as follows:

    sigGen = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privKey); 

In the above case privKey is simply the PrivateKey object representing the private key we are signing with.

Of course we may actually be wanting to do this using the BC lightweight API. In this case we can use the BcRSAContentSignerBuilder. In this case the
code for creating a SHA1withRSA signature might look as follows:

        AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
        AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
 
        sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(lwPrivKey);

where lwPrivKey is an RSA private key as described in the BC lightweight API.

Where this gets more interesting is we can now also apply our sigGen value to signing a CMS message as follows:

   X509CertificateHolder sigCert = ...;

   CMSSignedDataGenerator gen = new CMSSignedDataGenerator();

   gen.addSignerInfoGenerator(
            new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider())
                .build(sigGen, sigCert));

Of course it might be the case that you do not wish to use either the JCA or the BC lightweight API. If this is the case, then it is just a matter of implementing the ContentSigner interface.

Verifying a Signature

Of course having created a signature it's useful to be able to verify it.

Verification is a little bit more involved than signature creation, the issue being that you really need to know what kind of signature you are trying to verify before you can make sense of it.

In the case of certificates the X509CertificateHolder class provides a method, isSignatureValid which allows you to verify the signature on the certificate the holder class contains. The isSignatureValid method takes a ContentVerifierProvider class, and queries it to create a content verifier capable of verifying the content (in the this case the TBSCertificate structure) and the signature match.

Standard implementations providing the core functionality are provided for both lightweight and provider based implementations.

In the case of the lightweight API, assuming we have a suitable public key in the lwPubKey object, the code for doing the signature verification looks as follows:

    ContentVerifierProvider contentVerifierProvider = new BcRSAContentVerifierProviderBuilder(
        new DefaultDigestAlgorithmIdentifierFinder())
      .build(lwPubKey);
    
    if (!certHolder.isSignatureValid(contentVerifierProvider))
    {
        System.err.println("signature invalid");
    }

A more general class is provided for use with the JCA - in the simple case all you need to do is specify which provider you want to use as follows:

    ContentVerifierProvider contentVerifierProvider = new JcaContentVerifierProviderBuilder()
        .setProvider("BC").build(pubKey);


    if (!certHolder.isSignatureValid(contentVerifierProvider))
    {
        System.err.println("signature invalid");
    }

Verifying a SignerInformation object

Verifying the signature on a SignerInformation object is a little more complicated as for the most part it requires a separate digest calculator to be available as well. Code for doing this using the general lightweight API to process RSA signatures looks like this:

if (signer.verify(new BcRSASignerInfoVerifierBuilder(new DefaultDigestAlgorithmIdentifierFinder(), new BcDigestCalculatorProvider()).build(cert))))
{
    System.err.println("signer verified");
}

Of course if you know the digest algorithms to expect it is possible to implement your own DigestCalculatorProvider that is restricted to the necessary algorithms.

In the case of a JCA/JCE provider based solution, where the same provider can be used for both signature verification and digest calculation, you can use the following:

if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC).build(cert))
{
    System.err.println("signer verified);
}

In the case where you need to use different providers or even a provider/lightweight combination you can use the SignerInformationVerifier class to create a verifier.

Other shortcuts if dealing with the JCA/JCE

The certificate generation example about could also have been written as:

    ContentSigner sigGen = ...;
    PublicKey publicKey = ....;

    Date startDate = new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000);
    Date endDate = new Date(System.currentTimeMillis() + 365 * 24 * 60 * 60 * 1000);

    X509v1CertificateBuilder v1CertGen = new JcaX509v1CertificateBuilder(
              new X500Principal("CN=Test"), 
              BigInteger.ONE, 
              startDate, endDate, 
              new X500Principal("CN=Test"), 
              publicKey);
        
    X509CertificateHolder certHolder = v1CertGen.build(sigGen);

likewise in the CMS signing example the required X509CertificateHolder could be created from a JCA X509Certificate so the declaration of sigCert would appear
as:

    X509Certificate origCert = ...;

    X509CertificateHolder sigCert = new JcaX509CertificateHolder(origCert); 

On the other hand you might only have a byte code representation of the certificate available. In this case you might have said:

    byte[] sigCertEnc = ....;

    X509CertificateHolder sigCert = new X509CertificateHolder(sigCertEnc); 

Organisation

The following packages contain version 2 implementations:

Examples of Use in Test Code

Certificate/CRL Generation

CMP

CMS

CRMF

OCSP

PKCS#10

S/MIME

TSP