7
votes

I want to generate a simple CMS signature using bouncycastle. This code works!

  Security.addProvider(new BouncyCastleProvider());
  String password = "123456";
  KeyStore ks = KeyStore.getInstance("PKCS12");
  ks.load(new FileInputStream("c:/cert_123456.p12"), password.toCharArray());
  String alias = (String)ks.aliases().nextElement();
  PrivateKey key = (PrivateKey)ks.getKey(alias, password.toCharArray());
  Certificate[] chain = ks.getCertificateChain(alias);

  CMSSignedDataGenerator generator = new CMSSignedDataGenerator();

  generator.addSigner(key, (X509Certificate)chain[0], CMSSignedDataGenerator.DIGEST_SHA1);
  ArrayList list = new ArrayList();
  for (int i = 0; i < chain.length; i++) {
       list.add(chain[i]);
  }
  CertStore chainStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(list), "BC");
  generator.addCertificatesAndCRLs(chainStore);
  CMSProcessable content = new CMSProcessableByteArray("test".getBytes());
  CMSSignedData signedData = generator.generate(content, false, "BC");

  byte[] pk = signedData.getEncoded();

But, how to add signed attributes?
I want to remove default signed attributes and add signature-policy-identifier.

Articles are very welcome.

1

1 Answers

10
votes

First of all you appear to be using constructs that are deprecated in the latest versions of Bouncy Castle. To add authenticated/signed attributes you have to package them into an AttributeTable Signed attributes are added to the signer like so:

ASN1EncodableVector signedAttributes = new ASN1EncodableVector();
signedAttributes.add(new Attribute(CMSAttributes.contentType, new DERSet(new ASN1ObjectIdentifier("1.2.840.113549.1.7.1"))));
signedAttributes.add(new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(digestBytes))));
signedAttributes.add(new Attribute(CMSAttributes.signingTime, new DERSet(new DERUTCTime(signingDate))));

AttributeTable signedAttributesTable = new AttributeTable(signedAttributes);

Then use it in one of the addSigner methods. As I already mentioned in the beginning this method is deprecated and you are encouraged to use Generators and Generator Builders. Here's a short example:

    /* Construct signed attributes */
    ASN1EncodableVector signedAttributes = new ASN1EncodableVector();
    signedAttributes.add(new Attribute(CMSAttributes.contentType, new DERSet(new ASN1ObjectIdentifier("1.2.840.113549.1.7.1"))));
    signedAttributes.add(new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(digestBytes))));
    signedAttributes.add(new Attribute(CMSAttributes.signingTime, new DERSet(new DERUTCTime(signingDate))));

    AttributeTable signedAttributesTable = new AttributeTable(signedAttributes);
    signedAttributesTable.toASN1EncodableVector();
    DefaultSignedAttributeTableGenerator signedAttributeGenerator = new DefaultSignedAttributeTableGenerator(signedAttributesTable);

    /* Build the SignerInfo generator builder, that will build the generator... that will generate the SignerInformation... */
    SignerInfoGeneratorBuilder signerInfoBuilder = new SignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build());
    signerInfoBuilder.setSignedAttributeGenerator(signedAttributeGenerator);
    CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
    JcaContentSignerBuilder contentSigner = new JcaContentSignerBuilder("SHA1withRSA");
    contentSigner.setProvider("BC");

    generator.addSignerInfoGenerator(signerInfoBuilder.build(contentSigner.build(this.signingKey), new X509CertificateHolder(this.signingCert.getEncoded())));

    ArrayList<X509CertificateHolder> signingChainHolder = new ArrayList<X509CertificateHolder>();
    Iterator i = this.signingChain.iterator();
    while (i.hasNext()) {
        X509CertificateObject cert = (X509CertificateObject)i.next();
        signingChainHolder.add(new X509CertificateHolder(cert.getEncoded()));
    }

    generator.addCertificates(new JcaCertStore(signingChainHolder));
    generator.generate(new CMSAbsentContent(), "BC").getEncoded();

It's quite bulky and probably doesn't work yet (I'm in the process of writing it and stumbled upon your question while researching some stuff), especially the signingDate part, it probably has to be new DERSet(new Time(new Date)) (update: it works with DERUTCTime).

A bit of offtopic: I still can't get my head around the difference between Signed and Authenticated attributes, Bouncy Castle has got both DefaultAuthenticatedAttributeTableGenerator, DefaultSignedAttributeTableGenerator classes which work perfectly well with Signers. There seem to be some minor differences between the two in regards to signingTime, SignedAttributes adds the signingTime by default if not present. The RFCs mention both attribute types, but I couldn't find anything definite.