Adobe - Signature is timestamped but the timestamp could not be verified - java

I'm fighting with creating signature with timestamp on my pdf file. After many attempts we succeeded and signed PDF file. Adobe verified this file but there is one mistake with timestamp. There is information about:
Signature is timestamped but the timestamp could not be verified
Is this signature was created inproperly?
There is a code
public String signByPfxCert(String filePath) {
String postfix = "-signed";
try {
PdfReader reader = new PdfReader(filePath);
OutputStream os = new FileOutputStream(filePath + postfix);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason("REASON");
appearance.setLocation("LOCATION");
Security.addProvider(new BouncyCastleProvider());
FileInputStream fis = new FileInputStream(getClass().getClassLoader().
getResource("clientcert.pfx").getFile());
String password = "pwd12345";
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(fis, password.toCharArray());
String alias = ks.aliases().nextElement();
PrivateKey pk = (PrivateKey) ks.getKey(alias, password.toCharArray());
X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
com.itextpdf.text.pdf.security.TSAClient tsc = new TSAClientBouncyCastle(tsaUrl);
ExternalDigest digest = new BouncyCastleDigest();
ExternalSignature signature = new PrivateKeySignature(pk, "SHA-1", "BC");
MakeSignature.signDetached(appearance, digest, signature, new Certificate[]{cert}, null, null, tsc, 0,
MakeSignature.CryptoStandard.CMS);
if (fis.available() != 0) {
fis.close();
}
File originalFile = new File(filePath);
File signedFile = new File(filePath + postfix);
boolean deleteOriginal = originalFile.delete();
File destination = new File(filePath);
boolean rename = signedFile.renameTo(destination);
if(deleteOriginal && rename){
return destination.getName();
}else {
return "";
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}

Related

Signing a XFA-based PDF using iText

I have a XFA-based PDF with some fields need to be signed. However, these fields are embedded in the XFA form, so I can't sign it using the following codes
public void sign(String keystore, char[] password, String src, String name, String dest)
throws GeneralSecurityException, IOException, DocumentException {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(new FileInputStream(keystore), password);
String alias = (String)ks.aliases().nextElement();
PrivateKey pk = (PrivateKey) ks.getKey(alias, password);
Certificate[] chain = ks.getCertificateChain(alias);
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', null, true);
// Creating the appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(name);
// Creating the signature
PrivateKeySignature pks = new PrivateKeySignature(pk, DigestAlgorithms.SHA256, "BC");
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, pks, chain, null, null, null, 0, MakeSignature.CryptoStandard.CMS);
}
For example, I have a field named "root[0].mainpage[0].root[2].DefaultPage[0].Page1[0].SignArea[0].GSA[0].GSF-shinfo_sh-tmp03_sh_sf[0]". When I tried to put it to the argument "name", I got the error message
"Exception in thread "main" java.lang.IllegalArgumentException: The field root[0].mainpage[0].root[2].DefaultPage[0].Page1[0].SignArea[0].GSA[0].GSF-shinfo_sh-tmp03_sh_sf[0] does not exist."
I've worked on this problem for a week, but I haven't come up with a solution. Is there any ideas about this issue? Thanks!
PS:This is the download url of the file.

CAdES Digital Signature

I've been trying to implement digital signing (CAdES) for PDF files using Portuguese Citizen Card, however I'm having a hard time figuring out the perfectly working solution. Currently I have two sets of code.
First one:
public void signCAdES(...)
{
String pkcs11Config = "name=GemPC" + "\n" + "library=C:\\WINDOWS\\SysWOW64\\pteidpkcs11.dll";
ByteArrayInputStream configStream = new ByteArrayInputStream(pkcs11Config.getBytes());
Provider pkcs11Provider = new sun.security.pkcs11.SunPKCS11(configStream);
//provider_name: SunPKCS11-GemPC
Security.addProvider(pkcs11Provider);
javax.security.auth.callback.CallbackHandler cmdLineHdlr = new DialogCallbackHandler();
KeyStore.Builder builder = KeyStore.Builder.newInstance("PKCS11", pkcs11Provider,
new KeyStore.CallbackHandlerProtection(cmdLineHdlr));
KeyStore ks= builder.getKeyStore();
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', new File(tempPath), true);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(reason);
appearance.setLocation(location);
appearance.setCertificationLevel(level);
String alias = "CITIZEN SIGNATURE CERTIFICATE";
//certificates from electronic card and resources folder
Certificate[] certs = getSignatureCertificatesChain(ks);
PrivateKey pk = (PrivateKey) ks.getKey(alias, null);
ExternalSignature es = new PrivateKeySignature(pk, "SHA-1", pkcs11Provider.getName());
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, es, certs, null, null, null, 0, MakeSignature.CryptoStandard.CADES);
}
The first one works, however I have a validator given to me that verifies if the signatures of a PDF satisfies the standards, and it seems that one of the attributes is missing (sigining certificate issuer's serial number).
The second one is different, and I have to add the attributes manually, however the generated PDF is corrupted (and then I might need to add the issuer serial attribute too):
private static void signCAdES(byte[] aDocument, PrivateKey aPrivateKey, Certificate[] certChain, String outputPath) {
try {
Security.addProvider(new BouncyCastleProvider());
ArrayList<X509Certificate> certsin = new ArrayList<X509Certificate>();
for (Certificate certChain1 : certChain) {
certsin.add((X509Certificate) certChain1);
}
X509Certificate signingCertificate= certsin.get(0);
MessageDigest dig = MessageDigest.getInstance("SHA-1");
byte[] certHash = dig.digest(signingCertificate.getEncoded());
ESSCertID essCertid = new ESSCertID(certHash);
DERSet set = new DERSet(new SigningCertificate(essCertid));
Attribute certHAttribute = new Attribute(PKCSObjectIdentifiers.id_aa_signingCertificate, set);
AttributeTable at = getAttributeTableWithSigningCertificateAttribute(certHAttribute);
CMSAttributeTableGenerator attrGen = new DefaultSignedAttributeTableGenerator(at);
SignerInfoGeneratorBuilder genBuild = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider());
genBuild.setSignedAttributeGenerator(attrGen);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner shaSigner = new JcaContentSignerBuilder("SHA1withRSA").build(aPrivateKey);
SignerInfoGenerator sifGen = genBuild.build(shaSigner, new X509CertificateHolder(signingCertificate.getEncoded()));
gen.addSignerInfoGenerator(sifGen);
JcaCertStore jcaCertStore = new JcaCertStore(certsin);
gen.addCertificates(jcaCertStore);
CMSTypedData msg = new CMSProcessableByteArray(aDocument);
CMSSignedData sigData = gen.generate(msg, false); // false=detached
byte[] encoded = sigData.getEncoded();
ASN1InputStream in = new ASN1InputStream(encoded);
CMSSignedData sigData2 = new CMSSignedData(new CMSProcessableByteArray(aDocument), in);
byte[] encoded2 = sigData2.getEncoded();
FileOutputStream fos = new FileOutputStream(outputPath);
fos.write(encoded2);
// fos.write(encoded);
fos.flush();
fos.close();
} catch (CMSException | IOException | OperatorCreationException | CertificateEncodingException ex) {
log("signCAdES", "Error: " + ex.toString());
}
}
Is there anyone who understands CAdES digital signature using Java? Any help would be appreciated!
The 'issuer-serial' attribute is absent or does not match!
It means that your cades signature has not signed attribute: the signed reference to the signing certificate or that this reference is tampered.
Please check: ETSI TS 101 733 V2.2.1 (2013-04) for more information:
5.7.3 Signing Certificate Reference Attributes
The Signing certificate reference attributes are supported by using either the
ESS signing-certificate attribute or the ESS-signing-certificate-v2 attribute...

How to convert pkcs8 to pkcs12 in Java

I know this is possible with openssl.
But I wonder if there are PKCS converting possibilities(pkcs8 to 12) in Java using any library.
First you read PKCS#8 encoded key as a file and create PrivateKey object
public PrivateKey loadPrivateKey(String keyFile)
throws Exception {
File f = new File(keyFile);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
Then this key is being saved into PKCS#12 key store
public void createKeyStore(String keyStorePwd, String keyStoreFile,
PrivateKey privateKey, X509Certificate certificate)
throws Exception {
char[] pwd = keyStorePwd.toCharArray();
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(null, pwd);
KeyStore.ProtectionParameter protParam =
new KeyStore.PasswordProtection(pwd);
Certificate[] certChain =
new Certificate[]{ certificate };
KeyStore.PrivateKeyEntry pkEntry =
new KeyStore.PrivateKeyEntry(privateKey, certChain);
ks.setEntry("keypair", pkEntry, protParam);
FileOutputStream fos = new FileOutputStream(keyStoreFile);
ks.store(fos, pwd);
fos.close();
}

RSA - bouncycastle PEMReader returning PEMKeyPair instead of AsymmetricCipherKeyPair for reading private key

I have a function that successfully reads a openssl formatted private key:
static AsymmetricKeyParameter readPrivateKey(string privateKeyFileName)
{
AsymmetricCipherKeyPair keyPair;
using (var reader = File.OpenText(privateKeyFileName))
keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
return keyPair.Private;
}
and returns an AsymmetricKeyParameter which is then used to decrypt an encrypted text.
Below is the decrypt code:
public static byte[] Decrypt3(byte[] data, string pemFilename)
{
string result = "";
try {
AsymmetricKeyParameter key = readPrivateKey(pemFilename);
RsaEngine e = new RsaEngine();
e.Init(false, key);
//byte[] cipheredBytes = GetBytes(encryptedMsg);
//Debug.Log (encryptedMsg);
byte[] cipheredBytes = e.ProcessBlock(data, 0, data.Length);
//result = Encoding.UTF8.GetString(cipheredBytes);
//return result;
return cipheredBytes;
} catch (Exception e) {
Debug.Log ("Exception in Decrypt3: " + e.Message);
return GetBytes(e.Message);
}
}
These work in C# using bouncy castle library and I get the correct decrypted text. However, when I added this to Java, the PEMParser.readObject() returns an object of type PEMKeyPair instead of AsymmetricCipherKeyPair and java throws an exception trying to cast it. I checked in C# and it is actually returning AsymmetricCipherKeyPair.
I don't know why Java behaves differently but I hope someone here can help how to cast this object or read the privatekey file and decrypt successfully. I used the same public and privatekey files in both C# and Java code so I don't think the error is from them.
Here for reference the Java version of how I'm reading the privatekey:
public static String readPrivateKey3(String pemFilename) throws FileNotFoundException, IOException
{
AsymmetricCipherKeyPair keyParam = null;
AsymmetricKeyParameter keyPair = null;
PEMKeyPair kp = null;
//PrivateKeyInfo pi = null;
try {
//var fileStream = System.IO.File.OpenText(pemFilename);
String absolutePath = "";
absolutePath = Encryption.class.getProtectionDomain().getCodeSource().getLocation().getPath();
absolutePath = absolutePath.substring(0, (absolutePath.lastIndexOf("/")+1));
String filePath = "";
filePath = absolutePath + pemFilename;
File f = new File(filePath);
//return filePath;
FileReader fileReader = new FileReader(f);
PEMParser r = new PEMParser(fileReader);
keyParam = (AsymmetricCipherKeyPair) r.readObject();
return keyParam.toString();
}
catch (Exception e) {
return "hello: " + e.getMessage() + e.getLocalizedMessage() + e.toString();
//return e.toString();
//return pi;
}
}
The Java code has been updated to a new API, which is yet to be ported across to C#. You could try the equivalent (but now deprecated) Java PEMReader class. It will return a JCE KeyPair though (part of the reason for the change was because the original version worked only with JCE types, not BC lightweight classes).
If using PEMParser, and you get back a PEMKeyPair, you can use org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter.getKeyPair to get a JCE KeyPair from it. Ideally there would be a BCPEMKeyConverter, but it doesn't appear to have been written yet. In any case, it should be easy to make an AsymmetricCipherKeyPair:
PEMKeyPair kp = ...;
AsymmetricKeyParameter privKey = PrivateKeyFactory.createKey(kp.getPrivateKeyInfo());
AsymmetricKeyParameter pubKey = PublicKeyFactory.createKey(kp.getPublicKeyInfo());
new AsymmetricCipherKeyPair(pubKey, privKey);
Those factory classes are in the org.bouncycastle.crypto.util package.

verifying detached signature with BC

How can I verify a detached signature (CMS/pkcs #7 signature) using the BouncyCastle provider in Java?
Currently, my code below throws an exception with the message message-digest attribute value does not match calculated value
Security.addProvider(new BouncyCastleProvider());
File f = new File(filename);
byte[] buffer = new byte[(int)f.length()];
DataInputStream in = new DataInputStream(new FileInputStream(f));
in.readFully(buffer);
in.close();
CMSSignedData signature = new CMSSignedData(buffer);
SignerInformation signer = (SignerInformation) signature.getSignerInfos().getSigners().iterator().next();
CertStore cs = signature.getCertificatesAndCRLs("Collection", "BC");
Iterator iter = cs.getCertificates(signer.getSID()).iterator();
X509Certificate certificate = (X509Certificate) iter.next();
CMSProcessable sc = signature.getSignedContent();
signer.verify(certificate, "BC");
You can verify detached signature by the following code :
public static boolean verif_Detached(String signed_file_name,String original_file_name) throws IOException, CMSException, NoSuchAlgorithmException, NoSuchProviderException, CertStoreException, CertificateExpiredException, CertificateNotYetValidException{
boolean result= false;
Security.addProvider(new BouncyCastleProvider());
File f = new File(signed_file_name);
byte[] Sig_Bytes = new byte[(int)f.length()];
DataInputStream in = new DataInputStream(new FileInputStream(f));
in.readFully(Sig_Bytes);
in.close();
File fi = new File(original_file_name);
byte[] Data_Bytes = new byte[(int)fi.length()];
DataInputStream input = new DataInputStream(new FileInputStream(fi));
input.readFully(Data_Bytes);
input.close();
try{
CMSSignedData cms = new CMSSignedData(new CMSProcessableByteArray(Data_Bytes), Sig_Bytes);
CertStore certStore = cms.getCertificatesAndCRLs("Collection", "BC");
SignerInformationStore signers = cms.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext()) {
SignerInformation signer = (SignerInformation) it.next();
Collection certCollection = certStore.getCertificates(signer.getSID());
Iterator certIt = certCollection.iterator();
X509Certificate cert = (X509Certificate) certIt.next();
cert_signer=cert;
result=signer.verify(cert, "BC");
}
}catch(Exception e){
e.printStackTrace();
result=false;
}
return result;
}
the key for verify detached pKCS7 is use of CMSTypedStream ,like code bellow:
public void verifySign(byte[] signedData,byte[]bPlainText) throws Exception {
InputStream is = new ByteArrayInputStream(bPlainText);
CMSSignedDataParser sp = new CMSSignedDataParser(new CMSTypedStream (is),signedData);
CMSTypedStream signedContent = sp.getSignedContent();
signedContent.drain();
//CMSSignedData s = new CMSSignedData(signedData);
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 certHolder = (X509CertificateHolder)certIt.next();
if ( !signer.verify(new
JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certHolder)))
{
throw new DENException("Verification FAILED! ");
}
else
{
logger.debug("verify success" );
}
}
}
You can find the answer to this post here. This happening because how bouncy castle/open ssl treats the S/MIME message when S/MIME headers are not present.Solution is to add S/MIME headers to the message before signimg

Categories

Resources