Convert message and signature to BouncyCastle CMSSignedData object - java

I have an X509CertificateObject, a matching RSAPublicKey and managed to create a byte array containing a valid digital certificate for some message object also as a byte array.
Unfortunately the system I'm building upon only accepts CMSSignedData objects as input.
How do I convert my basic building blocks into such a valid CMSSignedData object?
Background: I'm experimenting with Java Bouncy Castle RSA blind signatures according to this example (digest is SHA512) and need to feed the result into the standard signature processing.

First, you'll probably want to sign your data with a private key. The idea being that the signature should be something only you can create. One you get that the rest should be as follows:
X509Certificate signingCertificate = getSigningCertificate();
//The chain of certificates that issued your signing certificate and so on
Collection&ltX509Certificate&gt certificateChain = getCertificateChain();
PrivateKey pk = getPrivateKey();
byte[] message = "SomeMessage".getBytes();
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
certificateChain.add(signingCertificate);
generator.addCertificates(new CollectionStore(certificateChain));
JcaDigestCalculatorProviderBuilder jcaDigestProvider = new JcaDigestCalculatorProviderBuilder();
jcaDigestProvider.setProvider(new BouncyCastleProvider());
JcaSignerInfoGeneratorBuilder singerInfoGenerator = new JcaSignerInfoGeneratorBuilder(jcaDigestProvider.build());
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
AsymmetricKeyParameter privateKeyParam = PrivateKeyFactory.createKey(pk.getEncoded());
ContentSigner cs = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(privateKeyParam);
SignerInfoGenerator sig = singerInfoGenerator.build(cs, signingCertificate);
generator.addSignerInfoGenerator(sig);
CMSSignedData data = generator.generate(new CMSProcessableByteArray(message), true);

Related

Bouncy Castle's CMSSignedData PEM produced data causing parsing issues

I'm trying to wrap PKCS#10 request with PKCS#7/CMS signed object, as there almost no examples on how to do that I've started by wrapping a X.509 instead.
I've used Bouncy Castel's Example, produced the CMSSignedData object, decoded it to PEM, and stored it in the file system, and that works.
The issue is that my CA rejects it with "Error Parsing - ASN bad tag value met", also ASN.1 Editor failed to open the file.
private static void generateCMS(X509Certificate signCert, KeyPair signKP, X509Certificate signedCert) {
CMSTypedData msg = new CMSProcessableByteArray("Hello world!".getBytes());
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA256withRSA")
.setProvider("BC").build(signKP.getPrivate());
gen.addCertificate(new X509CertificateHolder(signedCert.getEncoded()));
gen.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
.build(sha1Signer, signCert));
CMSSignedData sigData = gen.generate(msg, true);
ContentInfo cmsSignedDataAsASN1 = sigData.toASN1Structure();
JcaPEMWriter writer = new JcaPEMWriter(new FileWriter("test.p7b"));
writer.writeObject(cmsSignedDataAsASN1);
writer.close();
}
I've noticed something weird, I'm not sure it's related, but when using OpenSSL CMS module for signing certificates the PEM encoded Base 64 always starts with the letters "MII", while my code produced PEM consistently starts with the letters "MIA".
Can someone point me to what I'm missing here?
I figured it out, when org.bouncycastle.asn1.cms.ContentInfo ASN.1 is being written to an OutputStream it's using BER encoding, all I had to do is to get the ASN1Primitive and instruct the decoder to use DER instead.
Here's the code:
ASN1Primitive cmsSignedDataAsASN1 = cmsSignedDataAsASN1.toASN1Primitive()
sigData.toASN1Structure().toASN1Primitive()
PemObject pemObject = new PemObject("CMS",
cmsSignedDataAsASN1.getEncoded(ASN1Encoding.DER));
PemWriter pemWriter = new PemWriter(new FileWriter(fileName));
pemWriter.writeObject(pemObject);
pemWriter.close();

Bouncy Castle - How implementes SignedAndEnveloped data with Bouncy Castle

I would like to created an signedAndEnvelopedData (PKCS #7) data implemented with Bouncy Castle (Version 1.59).
In the Bouncy Castle the interface CMSObjectIdentifiers includes the type signedAndEnvelopedData.
However, when tried many times, it can't be created correctly. Could you please give some suggestion and following are my core implemented
Signing data first
CMSTypedData msg = (CMSTypedData) new CMSProcessableByteArray(
new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()),
srcMsg.getBytes(charSet));
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner signer = new JcaContentSignerBuilder(
signatureSchema).setProvider("BC").build(privateKey);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC")
.build()).build(signer, cerx509));
gen.addCertificates(certs);
CMSSignedData sigData = gen.generate(msg, true);
sigData = new CMSSignedData(msg,sigData.getEncoded())
return sigData.getEncoded()
Here I set the input data to CMSTypeData as CMSObjectIdentifiers.data.getId()
CMSTypedData msg = (CMSTypedData) new CMSProcessableByteArray(
new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()),
srcMsg.getBytes(charSet));
the output of signed data would be used to be the input of enveloping
CMSTypedData msg = new CMSProcessableByteArray(new ASN1ObjectIdentifier(CMSObjectIdentifiers.signedAndEnvelopedData.getId()),srcMsg.getBytes(charSet));
CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
JcaAlgorithmParametersConverter paramsConverter = new JcaAlgorithmParametersConverter();
edGen.addRecipientInfoGenerator(
new JceKeyTransRecipientInfoGenerator(cert,paramsConverter.getAlgorithmIdentifier(PKCSObjectIdentifiers.id_RSAES_OAEP,OAEPParameterSpec.DEFAULT))
.setProvider(new BouncyCastleProvider()));
OutputEncryptor encryptor = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES256_CBC)
.setProvider(new BouncyCastleProvider())
.build()
CMSEnvelopedData ed = edGen.generate(msg,encryptor)
encryptedContent = ed.getEncoded()
String result = new String(Base64.encode(ed.getEncoded()));
return result;
Here I set the input data to CMSTypedData as CMSObjectIdentifiers.signedAndEnvelopedData.getId()
CMSTypedData msg = new CMSProcessableByteArray(new ASN1ObjectIdentifier(CMSObjectIdentifiers.signedAndEnvelopedData.getId()),srcMsg.getBytes(charSet));
Questions:
Is Bouncy Castle (1.59) supported PKCS#7 SignedAndEnevloped
If the first question is YES, are my steps correct creating SignedAndEnevloped Data ?
If the first question is NO, are the some way to implement it ?
I just wrote a demo about using Bouncy Caslte Provider to do RSA in XMLSignatrure(SignedAndEnevloped),see this post, https://honwhy.wang/2018/09/07/use-bc-provider-xmlsignature/
demo code,
1, https://github.com/Honwhy/xml-sec/blob/master/src/main/java/com/honey/xmlsec/BcSignatureAlgorithm.java#L37
2, https://github.com/Honwhy/xml-sec/blob/master/src/main/java/com/honey/xmlsec/MyUtil.java#L107
maybe you have to ajust some lines to meet your request.

Generate a CSR request in java via SCEP

I am trying to send a Certificate Signing Request from an Android device to a server. The server is working properly with iOS devices and follows a SCEP procedure with OpenSSL.
So here is my problem :
I can send the signed enveloped CSR but the server can't read the enveloped CSR. I have the following error from the server :
pki.rb:26:in initialize: Could not parse the PKCS7: header too long (ArgumentError)
Related ruby server code :
#receive object and put it in object data
[...]
# Verify Input Data
p7sign = OpenSSL::PKCS7.new(data)
store = OpenSSL::X509::Store.new
p7sign.verify(nil, store, nil, OpenSSL::PKCS7::NOVERIFY)
signers = p7sign.signers
# Encrypted data (LINE 26 :)
p7enc = OpenSSL::PKCS7.new(p7sign.data)
# Certificate Signing Request
csr = p7enc.decrypt(ssl.key, ssl.certificate)
# Signed Certificate
request = OpenSSL::X509::Request.new(csr)
Java Code (Android) :
I am using Bouncy Castle to generate the CSR and Volley (Google) to send it.
Main :
//Generate PEM formated CSR
byte[] pemCsr = getPemFromCsr(generateCSR());
//Envelop it in a PKCS#7 object
byte[] envelopedData = getDerFromCMSEnvelopedData(envelopData(pemCsr));
//Sign it in a PKCS#7 object
byte[] signedData = getDerFromCMSSignedData(signData(envelopedData));
sendCsrRequest(signedData);
CSR :
//Generate the CSR
private static PKCS10CertificationRequest genrateCertificationRequest(){
// Build the CN for the cert we
X500NameBuilder nameBld = new X500NameBuilder(BCStyle.INSTANCE);
nameBld.addRDN(BCStyle.CN, "cn");
nameBld.addRDN(BCStyle.O, "o");
nameBld.addRDN(BCStyle.NAME, "name");
X500Name principal = nameBld.build();
// Generate the certificate signing request (csr = PKCS10)
String sigAlg = "SHA1withRSA";
JcaContentSignerBuilder csb = new JcaContentSignerBuilder(sigAlg);
ContentSigner cs = csb.build(privateKey);
DERPrintableString password = new DERPrintableString("mychallenge");
PKCS10CertificationRequestBuilder crb = new JcaPKCS10CertificationRequestBuilder(principal, publicKey);
crb.addAttribute((ASN1ObjectIdentifier) PKCSObjectIdentifiers.pkcs_9_at_challengePassword, password);
PKCS10CertificationRequest csr = crb.build(cs);
return csr;
}
//Envelop the CSR
private static CMSEnvelopedData envelopData(byte[] pemCsr) {
CMSTypedData msg = new CMSProcessableByteArray(pemCsr);
CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(x509Certificate).setProvider("BC"));
CMSEnvelopedData ed = edGen.generate(msg,new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC).setProvider("BC").build());
return ed;
}
//Sign the enveloped CSR
private static CMSSignedData signData(byte[] data){
ContentSigner signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privateKey);
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(signer, (X509Certificate) x509Certificate));
CMSTypedData cmsdata = new CMSProcessableByteArray(data);
CMSSignedData signedData = generator.generate(cmsdata, true);
return signedData;
}
I have other code ready to paste (the Volley request, the utils converter) but maybe it is enough for the moment.
SCEP is already working with iOS devices, so the server is clean.
Ruby can create the signed PKCS#7 so I guess that my signing step is OK.
But if I send an empty signed PKCS#7, I surprisingly have the same error.
Thanks in advance for any help.
It seems that the ASN1 of the envelop isn't correct for OpenSSL.
In parallel, Google Volley automatically adds "\n" in the response which also raises problems.

Generating the CSR using BouncyCastle API

I am new to the security side of Java and stumbled across this library called BouncyCastle. But the examples that they provide and the ones out on the internet ask to use
return new PKCS10CertificationRequest("SHA256withRSA", new X500Principal(
"CN=Requested Test Certificate"), pair.getPublic(), null, pair.getPrivate()
But when I use PKCS10CertificationRequest, it looks like it is deprecated. So I started looking at another method where I use CertificationRequest class. But I am really confused, the constructor does not take the same parameters instead it takes CertificationRequestInfo class which I am not sure how to fill up.
CertificationRequest request = new CertificationRequest(...);
It would be awesome if someone could help me figure out how to make a CSR so that I can send it to the server for getting it signed.
With the recent versions of BouncyCastle it is recommended to create the CSR using the org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder class.
You can use this code snipppet:
KeyPair pair = generateKeyPair();
PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
new X500Principal("CN=Requested Test Certificate"), pair.getPublic());
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA");
ContentSigner signer = csBuilder.build(pair.getPrivate());
PKCS10CertificationRequest csr = p10Builder.build(signer);
It's really simmilar to Jcs's answer, it is just a little bit supplemented.
Dont forget to add:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
And the csr generate:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
keyPairGenerator.initialize(4096);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
new X500Principal("OU=Try, C=US## Heading ##"), keyPair.getPublic());
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA");
ContentSigner signer = csBuilder.build(keyPair.getPrivate());
PKCS10CertificationRequest csr = p10Builder.build(signer);
JcaPEMWriter jcaPEMWriter = new JcaPEMWriter(new FileWriter("cert/test.csr"));
jcaPEMWriter.writeObject(csr);
jcaPEMWriter.close();
I think a useful link

Add signed/authenticated attributes to CMS signature using BouncyCastle

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.
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.

Categories

Resources