I would like to encapsulate SignedData in EnvelopedData.
I am using BouncyCastle.
After looking in an example, I wrote something like this:
CMSSignedData signedData = gen.generate(new CMSProcessableByteArray(out.toByteArray()), true);
FileInputStream inputStream = new FileInputStream(args[3]);
CertificateFactory factory = CertificateFactory.getInstance("X.509", "BC");
X509Certificate certificate = (X509Certificate) factory.generateCertificate(inputStream);
CMSEnvelopedDataGenerator engen = new CMSEnvelopedDataGenerator();
engen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(certificate).setProvider("BC"));
CMSEnvelopedData envelopedData =engen.generate( new CMSProcessableByteArray(signedData.getEncoded()),
new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES256_CBC).setProvider("BC").build());
But I think this is wrong. Where can I specify the ContentType of ContentInfo? I need to specify the content type of EnvelopedData as SignedData, but it's not possible with the CMSSignedData class.
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 generate a x509certificate with Java, but I need to convert this x509certificate to Hex.
X509Certificate generateX509Certificate() throws Exception
{
X509CertificateStructure x509CertificateStructure = generateX509CertificateHolder().toASN1Structure();
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
InputStream is1 = new ByteArrayInputStream(x509CertificateStructure.getEncoded());
X509Certificate x509Certificate = (X509Certificate)certificateFactory.generateCertificate(is1);
return x509Certificate;
other alternative is the byte[] but I don't know how to convert it to hex.
Thank you for your answers
I found the solution
byte[] hex = x509Certificate.getEncoded();
System.out.println("--------------------------------------");
String hexText = DatatypeConverter.printHexBinary(hex);
System.out.println(hexText);
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 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.
Is there a possibility to generate an java.security.cert.X509Certificate from an byte[]?
Sure.
The certificate objects can be created by an instance of CertificateFactory - in particular, one configured to create X509 certificates. This can be created like so:
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
Then you need to pass it an InputStream containing the bytes of the certificate. This can be achieved by wrapping your byte array in a ByteArrayInputStream:
InputStream in = new ByteArrayInputStream(bytes);
X509Certificate cert = (X509Certificate)certFactory.generateCertificate(in);
You can do something like:
X509Certificate certificate = signature.getKeyInfo().getX509Datas().get(0).getX509Certificates().get(0);
String lexicalXSDBase64Binary = certificate.getValue();
byte[] decoded = DatatypeConverter.parseBase64Binary(lexicalXSDBase64Binary);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(decoded));
InputStream stream = null;
byte[] bencoded = javax.xml.bind.DatatypeConverter.parseBase64Binary(x509CertificateStr);
try {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
cert = (X509Certificate) certFactory.generateCertificate(stream);
} catch (java.security.cert.CertificateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}