Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
230 views
Welcome To Ask or Share your Answers For Others

1 Answer

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...