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 ");
Related
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'm trying to verify the signature of a document, but I am getting the following error:
Exception in thread "main" java.security.SignatureException: Signature
encoding error at
sun.security.rsa.RSASignature.engineVerify(RSASignature.java:204) at
java.security.Signature$Delegate.engineVerify(Signature.java:1219) at
java.security.Signature.verify(Signature.java:652) at
VerSign.VerSign.main(VerSign.java:78)
Caused by: java.io.IOException: Sequence tag error at sun.security.util.DerInputStream.getSequence(DerInputStream.java:297)
at
sun.security.rsa.RSASignature.decodeSignature(RSASignature.java:229)
at sun.security.rsa.RSASignature.engineVerify(RSASignature.java:195)
... 3 more
I'm using a smartcard with SUNPKCS11 provider.
-- Sign --
String configName = "/media/sf_Share_SI_Mint/conf";
Provider p = new sun.security.pkcs11.SunPKCS11(configName);
Security.addProvider(p);
KeyStore ks = KeyStore.getInstance("PKCS11",p);
ks.load(null,null);
String assinaturaCertifLabel = "CITIZEN SIGNATURE CERTIFICATE";
PrivateKey pk = (PrivateKey) ks.getKey(assinaturaCertifLabel, null);
Certificate cert = ks.getCertificate(assinaturaCertifLabel);
byte[] sig = signDocument((PrivateKey)pk,p);
FileOutputStream sigfos = new FileOutputStream("sig");
sigfos.write(sig);
sigfos.close();
byte[] certificado = cert.getEncoded();
FileOutputStream fos = new FileOutputStream("cert");
fos.write(certificado);
fos.close();
private static byte[] signDocument(PrivateKey privateKey, Provider p) throws GeneralSecurityException, FileNotFoundException, IOException {
Signature signatureAlgorithm = Signature.getInstance("SHA256withRSA", p);
signatureAlgorithm.initSign(privateKey);
FileInputStream fich = new FileInputStream("Documento");
byte[] doc = new byte[fich.available()];
fich.read(doc);
signatureAlgorithm.update(doc);
byte[] digitalSignature = signatureAlgorithm.sign();
return digitalSignature;
}
-- Verify --
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStream certfis = new FileInputStream(args[0]);
X509Certificate cert = (X509Certificate)cf.generateCertificate(certfis);
certfis.close();
PublicKey pubKey = cert.getPublicKey();
FileInputStream sigfis = new FileInputStream(args[1]);
byte[] sigToVerify = new byte[sigfis.available()];
sigfis.read(sigToVerify);
sigfis.close();
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(pubKey);
FileInputStream datafis = new FileInputStream(args[2]);
byte[] doc = new byte[datafis.available()];
datafis.read(doc);
sig.update(doc);
datafis.close();
boolean verifies = sig.verify(sigToVerify);
System.out.println("signature verifies: " + verifies);
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.
I have a PDF file and I use this code to sign this file:
certificate = (X509Certificate) loadKeyStore(certificateFile, password).getCertificate(alias);
privateKey = (PrivateKey) loadKeyStore(certificateFile, password).getKey(alias, alias.toCharArray());
Security.addProvider(new BouncyCastleProvider());
BufferedInputStream inFile = new BufferedInputStream(new FileInputStream(origem));
byte[] dates = new byte[inFile.available()];
entrada.read(dates);
entrada.close();
CMSSignedDataGenerator genetateSign = new CMSSignedDataGenerator();
geradorAss.addSigner(privateKey, certificate, CMSSignedDataGenerator.DIGEST_SHA1);
List certList = new ArrayList();
certList.add(certificate);
CertStore certs = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList));
geradorAss.addCertificatesAndCRLs(certs);
// Efetivamente assinar os dados de entrada
CMSProcessable content = new CMSProcessableByteArray(dates);
String providerName;
if (ks.getType().equalsIgnoreCase("JKS")) {
providerName = BouncyCastleProvider.PROVIDER_NAME;
} else {
providerName = ks.getProvider().getName();
}
CMSSignedData signedDate = genetateSign.generate(content, providerName);
signedDate = new CMSSignedData(content, signedDate.getEncoded());
File f = Converter.converter("signedFile.pdf", signedDate.getEncoded());
But, the file f no open on reader. When I get the file f and run this code:
CMSSignedData data = new CMSSignedData(new FileInputStream(f));
Occur this error:
org.bouncycastle.cms.CMSException: Malformed content.
Someone can help me?
Summarizing:
I need to generate the final file after signing. For example, I have a test.pdf file, I want to sign and generate test_signed.pdf file. And this test_signed.pdf file must have the signature and should still be readable in your reader.
I'm waiting...
PDF has an embedded signature inside the document, while CMS is signature itself. To extract and verify signature from PDF use iText library. Here is an example.
I haven't seen any examples on the internet for this so as far as I know this is the first time someone is trying this in Java which I find hard to believe.
I'm just trying to work with the .pem, .p12 and .cer files I've been given to generate a signature file for my manifest.json. Here is what I have, which gives me an InvalidKeyException version mismatch: (supported: 00, parsed: 03
See the comment in the code below where the error is happening. I've viewed a few examples in another languages of how people are doing this with openssl but there must be a Java equivalent??
File pemFile = new File("AWWdevCert.pem");
File passCer = new File("pass.cer");
File passP12 = new File("pass.p12");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
KeySpec ks = new PKCS8EncodedKeySpec(FileUtils.readFileToByteArray(passP12));
PrivateKey privKey = keyFactory.generatePrivate(ks); // ERROR HERE
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new ByteArrayInputStream(FileUtils.readFileToByteArray(passCer));
X509Certificate passCert = (X509Certificate)certFactory.generateCertificate(in); //don't know what to do with this
File inputFile = new File("WebContent/WEB-INF/Lowes.raw/manifest.json");
FileInputStream freader = null;
int sizecontent = ((int) inputFile.length());
byte[] contentbytes = new byte[sizecontent];
freader = new FileInputStream(inputFile);
System.out.println("\nContent Bytes: " + freader.read(contentbytes, 0, sizecontent));
freader.close();
Signature signature = Signature.getInstance("Sha1WithRSA");
signature.initSign(privKey);
signature.update(contentbytes);
byte[] signedData = signature.sign();
//create signature file
File signatureFile = new File(passDirectory.getAbsolutePath()+File.separator+"signature");
Check this jpasskit project on github
You can also generate signature by using only native sun.security package. Here is an example in Scala (can be easily rewritten for Java)
import java.security.cert.X509Certificate
import java.security.{MessageDigest, PrivateKey, Signature}
import java.util.Date
import sun.security.pkcs._
import sun.security.util.DerOutputStream
import sun.security.x509.{AlgorithmId, X500Name}
object PKPassSigner {
def sign(
signingCert: X509Certificate,
privateKey: PrivateKey,
intermediateCert: X509Certificate,
dataToSing: Array[Byte]
): Array[Byte] = {
val digestAlgorithmId = new AlgorithmId(AlgorithmId.SHA_oid)
val md = MessageDigest.getInstance(digestAlgorithmId.getName)
val attributes = new PKCS9Attributes(Array(
new PKCS9Attribute(PKCS9Attribute.SIGNING_TIME_OID, new Date()),
new PKCS9Attribute(PKCS9Attribute.MESSAGE_DIGEST_OID, md.digest(dataToSign)),
new PKCS9Attribute(PKCS9Attribute.CONTENT_TYPE_OID, ContentInfo.DATA_OID)
))
val signature = Signature.getInstance("Sha1WithRSA")
signature.initSign(privateKey)
signature.update(attributes.getDerEncoding)
val signedData = signature.sign()
val signerInfo = new SignerInfo(
X500Name.asX500Name(signingCert.getIssuerX500Principal),
signingCert.getSerialNumber,
digestAlgorithmId,
attributes,
AlgorithmId.get(privateKey.getAlgorithm),
signedData,
null
)
val p7 = new PKCS7(
Array(digestAlgorithmId),
new ContentInfo(ContentInfo.DATA_OID, null),
Array(signingCert, intermediateCert),
Array(signerInfo)
)
val out = new DerOutputStream()
p7.encodeSignedData(out)
out.flush()
val res = out.toByteArray
out.close()
res
}
}