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.
Related
I am using bcprov-jdk16-1.46.jar and bcmail-jdk16-1.46.jar to verify the signature embedded inside a JSON file.
Code is as below:
try
{
Security.addProvider(new BouncyCastleProvider());
InputStream objInputStream= new ByteArrayInputStream(signData);
CMSSignedData objCMSSignedData =null;
CMSProcessableByteArray cms_data = new CMSProcessableByteArray(actualData);
objCMSSignedData= new CMSSignedData(cms_data,objInputStream);
CertStore certs = objCMSSignedData.getCertificatesAndCRLs("Collection", "BC");
SignerInformationStore signers = objCMSSignedData.getSignerInfos();
Collection<?> c = signers.getSigners();
Iterator<?> it = c.iterator();
while(it.hasNext())
{
X509Certificate cert = null;
SignerInformation signer = (SignerInformation)it.next();
Collection<?> certCollection = certs.getCertificates(signer.getSID());
if(!certCollection.isEmpty())
{
for(Iterator<?> certIt = certCollection.iterator(); certIt.hasNext();)
{
cert = (X509Certificate)certIt.next();
PublicKey publicKey = cert.getPublicKey();
String str=new String(publicKey.getEncoded());
String sha256hex = DigestUtils.sha256Hex(new String(Base64.encodeBase64(publicKey.getEncoded())));
if(verfiyHexadecimalKey(sha256hex,entityid) {//end
if(signer.verify(publicKey, "BC"))
{
verified =true;
verifyCounter++;
}
else{
verifyCounter=0;
}
}
}
}
}
}
On executing this code as a runnable jar on one of my servers, I am getting
"java.lang.NoSuchFieldError: id_TA_ECDSA_SHA_1" at line objCMSSignedData= new CMSSignedData(cms_data,objInputStream);
But on executing the same code on Eclipse the signature is verified successfully.
Help me in resolving this issue.
This question already has answers here:
iterator hasnext() returns true but next() throws NoSuchElementException
(2 answers)
Closed 5 years ago.
I want to verify a signature made with bouncycastle library. This is my code for verifying the cmssigneddata object.
public static void verifySignature(CMSSignedData sigData){
Store store = sigData.getCertificates();
SignerInformationStore signers = sigData.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext()) {
SignerInformation signer = (SignerInformation) it.next();
Collection certCollection = store.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
X509Certificate cert = new JcaX509CertificateConverter().getCertificate(certHolder);
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
System.out.println("verified correct");
}
System.out.println("not verified");
}
I get the exception Exception in thread "main" java.util.NoSuchElementException
at java.util.ArrayList$Itr.next(ArrayList.java:854)
What is the problem here? Seems like the problem is the ArrayList?
EDIT
Inserted a while loop for certIt.next()
while (it.hasNext()) {
SignerInformation signer = (SignerInformation) it.next();
Collection certCollection = store.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
while (certIt.hasNext()) {
X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
X509Certificate cert = new JcaX509CertificateConverter().getCertificate(certHolder);
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
System.out.println("verified correct");
} else {
System.out.println("not verified");
}
}
EDIT
generating CMSSignedData
public static CMSSignedData sign() throws Exception {
byte[] file = fileChooser();
store = KeyStore.getInstance(storeType);
FileInputStream in = new FileInputStream(new File(storePathKey));
store.load(in, storePassword);
in.close();
Key priv = store.getKey("Subject", storePassword);
System.out.println(priv.toString() + "priv string");
X509Certificate cert = (X509Certificate) store.getCertificate("Subject");
ContentSigner signer = new JcaContentSignerBuilder(sigAlgo).build((RSAPrivateKey) priv);
// Build cms (sign data) - Cryptographic Message Syntax
CMSTypedData data = new CMSProcessableByteArray(file);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build())
.build(signer, cert));
CMSSignedData sigData = gen.generate(data, true);
return sigData;
}
Iterator certIt = certCollection.iterator();
X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
You are calling next without checking for hasNext
Collection certCollection = store.getMatches(signer.getSID()); returns an empty Collection. That's why you cannot iterate it.
Are you sure the argument CMSSignedData sigData was constructed correctly? Try to see what do get from store.getMatches(null);?
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.
I have a pkcs7 file, and I want to load it and extract its contents.
I tried these two methods:
byte[] bytes = Files.readAllBytes(Paths.get("myfile.p7b"));
FileInputStream fi = new FileInputStream(file);
//Creating PKCS7 object
PKCS7 pkcs7Signature = new PKCS7(bytes);
or this
FileInputStream fis = new FileInputStream(new File("myfile.p7b"));
PKCS7 pkcs7Signature = new PKCS7(fis);
but I got IOException: Sequence tag error
So how can I load this .p7b file ?
Finally I did it with BouncyCastle library.
PKCS#7 is a complex format, also called CMS. Sun JCE has no direct support to PKCS#7.
This is the code that I used to extract my content:
// Loading the file first
File f = new File("myFile.p7b");
byte[] buffer = new byte[(int) f.length()];
DataInputStream in = new DataInputStream(new FileInputStream(f));
in.readFully(buffer);
in.close();
//Corresponding class of signed_data is CMSSignedData
CMSSignedData signature = new CMSSignedData(buffer);
Store cs = signature.getCertificates();
SignerInformationStore signers = signature.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
//the following array will contain the content of xml document
byte[] data = null;
while (it.hasNext()) {
SignerInformation signer = (SignerInformation) it.next();
Collection certCollection = cs.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder) certIt.next();
CMSProcessable sc = signature.getSignedContent();
data = (byte[]) sc.getContent();
}
If you want to verify the signature of this PKCS7 file against X509 certificate, you must add the following code to the while loop:
// ************************************************************* //
// ********************* Verify signature ********************** //
//get CA public key
// Create a X509 certificat
CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509");
// Open the certificate file
FileInputStream fileinputstream = new FileInputStream("myCA.cert");
//get CA public key
PublicKey pk = certificatefactory.generateCertificate(fileinputstream).getPublicKey();
X509Certificate myCA = new JcaX509CertificateConverter().setProvider("BC").getCertificate(cert);
myCA.verify(pk);
System.out.println("Verfication done successfully ");
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.