Validate pkcs7 SignedData by Bouncy Castle in Java - java

I am working on an implementation of C# SignedCms functionality in Java.
I have a pkcs7 SignedData (see my attachement: https://www.dropbox.com/s/yivani7dvh98wpa/SignedData.bin?dl=0), it can be validated in C#:
//signed data is loaded from my attached file.
bool VerifyPKCS7(byte[] signedData)
{
try
{
SignedCms signedCms = new SignedCms();
signedCms.Decode(signedData);
signedCms.CheckSignature(true);
return true;
}
catch
{
}
return false;
}
But it can't be validated using Bouncy Castle libs(bcprov-jdk15on-153.jar, bcpkix-jdk15on-153.jar) in Java:
//encapSigData is loaded from my attached file.
CMSSignedDataParser sp = new CMSSignedDataParser(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build(), encapSigData);
sp.getSignedContent().drain();
Store certStore = sp.getCertificates();
SignerInformationStore signers = sp.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certStore.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
System.out.println("verify returns: " + signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)));
}
I got a exception at the first code line(CMSSignedDataParser constructor):
java.lang.ClassCastException: org.bouncycastle.asn1.DERSequenceParser cannot be cast to org.bouncycastle.asn1.ASN1OctetStringParser
at org.bouncycastle.cms.CMSSignedDataParser.<init>(Unknown Source)
at org.bouncycastle.cms.CMSSignedDataParser.<init>(Unknown Source)
at org.bouncycastle.cms.CMSSignedDataParser.<init>(Unknown Source)
After some analyze, I find that the content of contentInfo in SignedData is a Sequence. It seems that bouncycastle can't accept a Sequence to be the content.
How can I get this SignedData to be validated using bouncycastle in Java?

The issue here is that unlike a regular CMS message, this is really a PKCS7 one. Support for these has now been added to the bcpkix API in Bouncy Castle.
You can find it in the latest beta at http://www.bouncycastle.org/betas 154b12 or later.

Related

BouncyCastle PEMParser readObject Factory/Builder/Adapter Design Pattern or any Utility class?

I have read a lot of bad examples on PEMParser:
int myFunc(String privateKeyFileName, char [] password) {
File privateKeyFile = new File(privateKeyFileName); // private key file in PEM format
PEMParser pemParser = new PEMParser(new FileReader(privateKeyFile));
Object object = pemParser.readObject();
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password);
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
KeyPair kp;
if (object instanceof PEMEncryptedKeyPair) {
System.out.println("Encrypted key - we will use provided password");
kp = converter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv));
} else {
System.out.println("Unencrypted key - no password needed");
kp = converter.getKeyPair((PEMKeyPair) object);
}
}
Bouncy Castle : PEMReader => PEMParser
Also here:
How to read .pem file to get private and public key
https://www.codota.com/code/java/classes/org.bouncycastle.openssl.PEMParser
https://www.programcreek.com/java-api-examples/?api=org.bouncycastle.openssl.PEMParser
https://www.javatips.net/api/org.bouncycastle.util.io.pem.pemreader
Does BouncyCastle provide some factory method for this?
The only thing I found is: org.apache.sshd.common.keyprovider.FileKeyPairProvider (Apache Mina SSH Java client implementation). I think something like this could be a part of BouncyCastle itself or Apache Common libraries (fileutils / stringutils).
Is there any factory/builder method or some design pattern implementation or is everyone doing a production code like if (object instance of xxx) and solving the problems on the go?

Generate valid CMS Signature file adding external PKCS#1 with Java

I'm generating CMS signature files with external PKCS#1 based on this thread.
The first step is obtain the signed attributes from the original file to be signed in external application which is returning PKCS#1 byte array.
Then build standard org.bouncycastle.cms.SignerInfoGenerator with original file hash, signed data (PKCS#1) and certificate to add to CMS, and finally create the attached signature.
But when i'd tried to validate it using this code:
String originalFile = "aG9sYQ0KYXNkYXMNCg0KYWZzDQo=";
String cmsSignedFile = "MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBg...j2Dwytp6kzQNwtXGO8QbWty1lOo8oYm+6LR8EWba3ikO/m9ol/G808vit9gAAAAAAAA==";
byte[] signedByte = DatatypeConverter.parseBase64Binary(cmsSignedFile);
Security.addProvider(new BouncyCastleProvider());
CMSSignedData s = new CMSSignedData(new CMSProcessableByteArray(DatatypeConverter.parseBase64Binary(originalFile)), signedByte);
SignerInformationStore signers = s.getSignerInfos();
SignerInformation signerInfo = (SignerInformation)signers.getSigners().iterator().next();
FileInputStream fis = new FileInputStream("C:/myCertificate.cer");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificates(fis).iterator().next();
boolean result = signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert.getPublicKey()));
System.out.println("Verified: "+result);
I get Verified: false
I'm adding Content Type, Signing time, Message digest and OCSP as signed attributes and TSP Token as unsigned attribute (I'm not sure if this is right).
I'm also trying to recover data from CMS signature, using the code below:
//load cms signed file with attached data
CMSSignedData cms = new CMSSignedData(FileUtils.readFileToByteArray(new File("C:/tmp/tempFile1864328163858309463.cms")));
System.out.println(cms.getSignerInfos().getSigners().iterator().next().getDigestAlgorithmID().getAlgorithm().getId());
System.out.println(Hex.encodeHexString(cms.getSignerInfos().getSigners().iterator().next().getSignature()));
//recover signer certificate info
Store certs = cms.getCertificates();
Collection<X509CertificateHolder> col = certs.getMatches(null);
X509CertificateHolder []h1 = col.toArray(new X509CertificateHolder[col.size()]);
X509CertificateHolder firmante = h1[0];
System.out.println(firmante.getSubject());
System.out.println(h1[1].getSubject());
SignerInformation sinfo = cms.getSignerInfos().getSigners().iterator().next();
//recover OCSP information
//THIS FAILS :(
// Store infocspbasic = cms.getOtherRevocationInfo(OCSPObjectIdentifiers.id_pkix_ocsp_basic);
// Object basic = infocspbasic.getMatches(null).iterator().next();
//recover signing time
if (sinfo.getSignedAttributes() != null) {
Attribute timeStampAttr = sinfo.getSignedAttributes().get(PKCSObjectIdentifiers.pkcs_9_at_signingTime);
ASN1Encodable attrValue = timeStampAttr.getAttrValues().getObjectAt(0);
final Date signingDate;
if (attrValue instanceof ASN1UTCTime) {
ASN1UTCTime time = ASN1UTCTime.getInstance(attrValue);
Date d = time.getDate();
System.out.println("ASN1UTCTime:" + d);
} else if (attrValue instanceof Time) {
signingDate = ((Time) attrValue).getDate();
} else if (attrValue instanceof ASN1GeneralizedTime) {
System.out.println("ASN1GeneralizedTimeASN1GeneralizedTime");
} else {
signingDate = null;
}
}
//recover timestamp TOken
//unsigned attributes are null :(
if (sinfo.getUnsignedAttributes() != null) {
Attribute timeStampAttr = sinfo.getUnsignedAttributes().get(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken);
for (ASN1Encodable value : timeStampAttr.getAttrValues().toArray()) {
TimeStampToken token = new TimeStampToken(new CMSSignedData(value.toASN1Primitive().getEncoded()));
System.out.println(token.getTimeStampInfo().getGenTime());
}
}
But I can't retrieve OCSP response nor TSP Token information. Additionally I've downloaded this viewer software to help verify it:
Any help would be very appreciated.
I found a project named j4sign which implements CMS signature with external PKCS#1. The link goes to the project's forum where I posted the code sample using their classes and the final correction to make the validation works.

Adding Certificates to CMS Signed Data

I am currently using the java Bouncy Castle libraries in order to create CMS signed data (or PKCS7 signed data). I seem however to be stuck with adding certificates (even though the certificate signer is properly added).
I checked out this question about properly signing data, but it didn't respond the needs of my SCEP server. The code I used was from EJBCA but doesn't seem to add certificates to the PKCS7 signed data.
When I parse the signed data with the openssl cms tool, I see that the "certificates" field is "EMPTY". Additionally, when I try to the print the certs with openssl pkcs7 [...] -print_certs, I get nothing.
Here is how I sign my data with Bouncy Castle (it's a lot code but enough to reproduce the issue):
CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
CMSTypedData msg;
List<X509Certificate> certList = new ArrayList<>();
// Make sure the certificate is not null
if (this.certificate != null) {
certList.add((X509Certificate) this.certificate);
}
/**
* Create the signed CMS message to be contained inside the envelope
* this message does not contain any message, and no signerInfo
**/
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
Collection<JcaX509CertificateHolder> x509CertificateHolder = new ArrayList<>();
try {
for (X509Certificate certificate : certList) {
x509CertificateHolder.add(new JcaX509CertificateHolder(certificate));
}
CollectionStore<JcaX509CertificateHolder> store = new CollectionStore<>(x509CertificateHolder);
gen.addCertificates(store);
} catch (Handle all exceptions) {}
This snippet of code above should normally add certificates. I took this from EJBCA.
Here is how I complete the signed data:
CMSSignedDataGenerator gen1 = new CMSSignedDataGenerator();
// I add ALL of my attributes here
// Once they're added...
Certificate caCert = this.caCertificate;
try {
String provider = BouncyCastleProvider.PROVIDER_NAME;
ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithmName).
setProvider(provider).
build(signerKey);
JcaDigestCalculatorProviderBuilder calculatorProviderBuilder = new JcaDigestCalculatorProviderBuilder().
setProvider(provider);
JcaSignerInfoGeneratorBuilder builder = new JcaSignerInfoGeneratorBuilder(calculatorProviderBuilder.build());
builder.setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(attributes)));
gen1.addSignerInfoGenerator(builder.build(contentSigner, (X509Certificate) ca));
} catch (Handle all exceptions) {}
// Create the signed data
CMSSignedData sd = gen1.generate(msg, true);
byte[] results = sd.getEncoded();
The bytes array results is the DER formatted PKCS7 signed data... but no certificate is added.
Am I missing something? Thank you for your help!
The CMSSignedDataGenerator gen1 has to explicitly add the certificate, which I wasn't aware of.
It can simply be done by:
Adding the certificates to a List of X509Certificates;
Converting that List into a Collection of JcaX509CertificateHolder;
Adding this collection to a CollectionStore of JcaX509CertificateHolder;
Adding the store the CMSSignedDataGenerator.
Code sample:
CMSSignedDataGenerator gen1 = new CMSSignedDataGenerator();
List<X509Certificate> certificates = new ArrayList<>();
// I chose to add the CA certificate
certificates.add((X509Certificate) this.caCertificate);
// In this case, this is a certificate that I need to add
if (this.certificate != null)
certificates.add((X509Certificate) this.certificate);
// This is the recipient certificate
if (this.recipientCert != null)
certificates.add((X509Certificate) this.recipientCert);
Collection<JcaX509CertificateHolder> x509CertificateHolder = new ArrayList<>();
// Of course, we need to handle the exceptions...
for (X509Certificate certificate : certificates) {
x509CertificateHolder.add(new JcaX509CertificateHolder(certificate));
}
CollectionStore<JcaX509CertificateHolder> store = new CollectionStore<>(x509CertificateHolder);
// The final stage.
gen1.addCertificates(store);
Hope this helps anyone in the future.

Verify PKCS 7 with key from file fails

I have some Bouncy Castle code that verifies a PKCS 7 signatures:
CMSSignedData s = new CMSSignedData(new CMSProcessableByteArray(toVerifyBytes), signedByte);
Store certStore = s.getCertificates();
SignerInformationStore signers = s.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certStore.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)))
{
System.out.println("Verified OK");
}
}
This works perfectly and signature is verified OK.
BUT - then I save the PKCS 7 signature element as a p7b file in Windows, opens it in Windows, and use Windows functionality to extract the two certificates to .cer-files and store on disk. I then write some code to load the pub keys from file instead of using the certificates directly from the signature.
The code looks like this:
FileInputStream fis = new FileInputStream("c:\\path\\pubkey.cer");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificates(fis).iterator().next();
fis.close();
CMSSignedData s = new CMSSignedData(new CMSProcessableByteArray(toVerifyBytes), signedByte);
SignerInformationStore signers = s.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext())
{
System.out.println(cert.getPublicKey());
SignerInformation signerInfo = (SignerInformation)it.next();
if (signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert.getPublicKey())))
{
System.out.println("Verified OK");
} else {
System.out.println("NOT Veried OK");
}
}
This verification always fails. What can be the reason? Is the key export doing something? Am I using wrong object when loading from file?
If I print the certificate in code (.toString()) the key shows as expected.

Explore a bouncy castle store object

My question is strictly related to Bouncy Castle i cannot get all certificate.
I use the BC code https://www.bouncycastle.org/docs/pkixdocs1.4/org/bouncycastle/cms/CMSSignedData.html whith some little variation.
Store certStore = s.getCertificates();
SignerInformationStore signers = s.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certStore.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)))
{
verified++;
}
}
My purpose is to extract all certificate(signers + them issuers) from the "certStore" , and verify them against a specicific keystore.
But to extract a certificate form certStore, there is only "certStore.getMatches".
The signers extract obviously only the signers and used in "certStore.getMatches" extract only the certificate of the signer(one or more).
I have to control each certificate, his CRL his date, not only the signer.
The first step to obtain allcertificate is to use a null selector
ArrayList<X509CertificateHolder> listCertDatFirm = new ArrayList(store.getMatches(null));
Then you have a group of certificate; looping recoursively you can rebuild the correct chain.

Categories