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);?
Related
I am working on an application which will do digital signatures on PDF. Every thing is working fine till we are putting the pdfRevocationInfoArchival(1.2.840.113583.1.1.8) attribute in CMSSignedData(PKCS#7) as signed attribute. After putting the signature(PKCS#7) the document we are getting and empty signature field in the signed PDF.
For Reference: Empty Signature filed
We are getting valid signed document if we are not adding the pdfRevocationInfoArchival(1.2.840.113583.1.1.8) attribute in the CMSSignedData(PKCS#7) object.
For Reference: Valid Signature
Source code reference:
1. Adding CRLs in ASN1EncodableVector:
private static ASN1EncodableVector genPdfInfoArchival(List<X509CRL> crls) {
ASN1EncodableVector v1 = new ASN1EncodableVector();
try {
if (!crls.isEmpty()) {
ASN1EncodableVector v11 = new ASN1EncodableVector();
for (Iterator<X509CRL> i = crls.iterator(); i.hasNext();) {
ASN1InputStream t = new ASN1InputStream(new ByteArrayInputStream(i.next().getEncoded()));
v11.add(t.readObject());
}
// 0 for CRL
v1.add(new DERTaggedObject(true, 0, new DERSequence(v11)));
}
} catch (Exception ex) {
}
return v1;
}
2. Adding CRL revocation attribute in CMSSignedData as signed attribute
CMSSignedData cmsSignedData = null;
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
Store certStore = new JcaCertStore(Arrays.asList(dscCert.getCertChain()));
ASN1EncodableVector signedAttr = new ASN1EncodableVector();
ASN1EncodableVector crlVector = genPdfInfoArchival(crlDetails);
Attribute pdfRevocationAttr = new Attribute(new ASN1ObjectIdentifier(ID_ADBE_REVOCATION), new DERSet (new DERSequence(crlVector)));
signedAttr.add(pdfRevocationAttr);
Attribute attrHash = new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(pdfHash)));
signedAttr.add(attrHash);
// Adding signed atribute in the CMSSignedData
ContentSigner sha1Signer = new JcaContentSignerBuilder(dscCert.getSigAlgName()) .build(dscCert.getPrivateKey());
gen.addSignerInfoGenerator( new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()) .setSignedAttributeGenerator( new DefaultSignedAttributeTableGenerator(new AttributeTable(signedAttr))).build(sha1Signer, dscCert.getCertificate()));
gen.addCertificates(certStore);
cmsSignedData = gen.generate(new CMSAbsentContent());
Please help us in this issue.
We have tried every thing but no luck.
I have two clients,
first client expect CMS/PKCS to verify signature
second client expect only signature (EncryptedDigestMessage). Original file and certificate separately to verify signature
So, I want to create one method that should return the appropriate output based on format (PKCS or SignOnly)
public static byte[] digitalSign(byte[] fileContent , PrivateKey privkey , X509Certificate x509Certificate , String format) throws Exception{
Security.addProvider(new BouncyCastleProvider());
List<X509Certificate> certList = new ArrayList<X509Certificate>();
CMSTypedData data = new CMSProcessableByteArray(fileContent);
certList.add(x509Certificate);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder("MD5WithRSA").setProvider("BC").build(privkey);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(sha1Signer,x509Certificate));
gen.addCertificates(certs);
CMSSignedData signedData = gen.generate(data, true);
//based on the format this method should return expected byte[]
if(format.equals("PKCS")){//should return CMS/PKCS package
return signedData.getEncoded();
}else{//should return only Signature (Encrypted Digest of fileContent)
SignerInformation signer = signedData.getSignerInfos().getSigners().iterator().next();
return signer.getSignature();// returns the Signature
}
}
I verified PKCS using below method. signature is verified
it returns true.
//CMS/PKCS signature verification
public static boolean verfySignaturePKCS(byte[] cmsSignedBytes ) throws Exception{
boolean result = false;
Security.addProvider(new BouncyCastleProvider());
CMSSignedData cms =new CMSSignedData(cmsSignedBytes );
Store<X509CertificateHolder> certificates = cms.getCertificates();
SignerInformationStore signerInfos = cms.getSignerInfos();
Collection<SignerInformation> signers = signerInfos.getSigners();
Iterator<SignerInformation> iterator = signers.iterator();
while (iterator.hasNext()) {
SignerInformation signer = iterator.next();
Collection<X509CertificateHolder> certCollection = certificates.getMatches(signer.getSID());
Iterator<X509CertificateHolder> certIt = certCollection.iterator();
X509CertificateHolder certHolder = certIt.next();
X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) {
result = true;
}
}
return result;
}
To verify that SignOnly data, I'm using normal Java security MessageDigest and Cipher classes.
parameters:
signedData--> Signature
fileBytes--> Original File content
cert--> X509Certificate to decrypt the signature
method process:
generate messageHash from original file content
decrypt Signature with cert and get the decryptedMessageHash
compare messageHash and decryptedMessageHash
In this way decrypt signature is going well without error. So I understand this is correct signature which is signed by my privatekey related to the given certificate.
but, both messageHash and decryptedMessageHash having different data.
public static boolean verifySignOnly(byte[] signedData,byte[] fileBytes ,X509Certificate cert) throws Exception{
Security.addProvider(new BouncyCastleProvider());
MessageDigest md = MessageDigest.getInstance("MD5" , "BC");
byte[] messageHash = md.digest(fileBytes);
Cipher cipher = Cipher.getInstance("RSA" , "BC");
cipher.init(Cipher.DECRYPT_MODE, cert);
byte[] decryptedMessageHash = cipher.doFinal(signedData);
return Arrays.equals(decryptedMessageHash, messageHash);
}
Can anyone explain what mistake I made?
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.
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 ");
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.