Signed pdf with selfsigned digital id in PdfBox is blank - pdfbox

I'm trying to sign existing pdf document with pdfbox. My class implements SignatureInterface interface, uses bouncycastle as provider and selfsigned cert stored in .jks file. But in output I get a blank page. What's wrong with my code?
public class CreateSignature implements SignatureInterface {
private PrivateKey privateKey;
private Certificate[] cert;
private CreateSignature(KeyStore keyStore, String certPassword) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException {
privateKey = (PrivateKey) keyStore.getKey("1", certPassword.toCharArray()); // "1" - alias default name
cert = keyStore.getCertificateChain("1");
}
#Override
public byte[] sign(InputStream inputStream) throws IOException {
byte[] c = IOUtils.toByteArray(inputStream);
List<Certificate> certList = new ArrayList<>();
certList.add(cert[0]);
try {
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
org.bouncycastle.asn1.x509.Certificate certificate = org.bouncycastle.asn1.x509.Certificate.getInstance(ASN1Primitive.fromByteArray(cert[0].getEncoded()));
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA256WithRSA").build(privateKey);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(sha1Signer, new X509CertificateHolder(certificate)));
gen.addCertificates(certs);
CMSTypedData msg = new CMSProcessableByteArray(c);
CMSSignedData signedData = gen.generate(msg,false);
return signedData.getEncoded();
} catch (CertificateEncodingException | OperatorCreationException | CMSException e) {
throw new RuntimeException(e);
}
}
public static byte[] signPdfDocument(PDDocument document) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException {
String keystorePassword = AppVars.getPdfCertificationKeystorePass();
String adobeDigitalIDPassword = AppVars.getPdfCertificationAdobePass();
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(CreateSignature.class.getResourceAsStream("/pdf/certificate/SelfSignedCert.jks"), keystorePassword.toCharArray());
CreateSignature singing = new CreateSignature(ks, adobeDigitalIDPassword);
return singing.doSign(document);
}
private byte[] doSign(PDDocument document) throws IOException {
PDSignature signature = new PDSignature();
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
signature.setName("name");
signature.setLocation("location");
signature.setReason("reason");
signature.setSignDate(Calendar.getInstance());
document.addSignature(signature, this);
ByteArrayOutputStream out = new ByteArrayOutputStream();
document.saveIncremental(out);
document.close();
return out.toByteArray();
}
}

As discussed in the comments - the cause was that the document was built in the application itself after loading, and not loaded from a PDF file (or stream). The incremental signing would not identify these changes as such, so the result was the PDF at the time of loading, plus a signature that was not shown, probably becaus the document did not even have a page. What gave it away was that the signed PDF was much smaller than the unsigned PDF.
The solution is
Create your PDF, create your content
Save it
Close it
Reload the PDF and sign it.

Related

pdfbox - document getting corrupted after adding signed attributes

I am trying to digitally sign document using pdfbox. I am using filter as FILTER_ADOBE_PPKLITE and subfilter as SUBFILTER_ETSI_CADES_DETACHED. For ETSI_CADES_Detached, a signing attribute is needs to be added. I am fetching signed hash and certificates from CSC> But after adding signing attribute, it is making the document corrupted. Sharing the screenshot for the reference error image.
Seems like hash is getting chagned. Sharing code for the reference.
PDDocument document = PDDocument.load(inputStream);
outFile = File.createTempFile("signedFIle", ".pdf");
Certificate[] certificateChain = retrieveCertificates(requestId, providerId, credentialId, accessToken);//Retrieve certificates from CSC.
setCertificateChain(certificateChain);
// sign
FileOutputStream output = new FileOutputStream(outFile);
IOUtils.copy(inputStream, output);
// create signature dictionary
PDSignature signature = new PDSignature();
// signature.setType(COSName.SIG);
// PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm(null);
int accessPermissions = SigUtils.getMDPPermission(document);
if (accessPermissions == 1)
{
throw new IllegalStateException("No changes to the document are permitted due to DocMDP transform parameters dictionary");
}
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ETSI_CADES_DETACHED);
signature.setName("Test Name");
// signature.setLocation("Bucharest, RO");
// signature.setReason("PDFBox Signing");
signature.setSignDate(Calendar.getInstance());
Rectangle2D humanRect = new Rectangle2D.Float(location.getLeft(), location.getBottom(), location.getRight(), location.getTop());
PDRectangle rect = createSignatureRectangle(document, humanRect);
SignatureOptions signatureOptions = new SignatureOptions();
signatureOptions.setVisualSignature(createVisualSignatureTemplate(document, 0, rect, signature));
signatureOptions.setPage(0);
document.addSignature(signature, signatureOptions);
ExternalSigningSupport externalSigning =
document.saveIncrementalForExternalSigning(output);
InputStream content = externalSigning.getContent();
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
X509Certificate cert = (X509Certificate) certificateChain[0];
gen.addCertificates(new JcaCertStore(Arrays.asList(certificateChain)));
MessageDigest digest = MessageDigest.getInstance("SHA-256");
// Use a buffer to read the input stream in chunks
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = content.read(buffer)) != -1) {
digest.update(buffer, 0, bytesRead);
}
byte[] hashBytes = digest.digest();
ESSCertIDv2 certid = new ESSCertIDv2(
new AlgorithmIdentifier(new ASN1ObjectIdentifier("*****")),
MessageDigest.getInstance("SHA-256").digest(cert.getEncoded())
);
SigningCertificateV2 sigcert = new SigningCertificateV2(certid);
final DERSet attrValues = new DERSet(sigcert);
Attribute attr = new Attribute(PKCSObjectIdentifiers.id_aa_signingCertificateV2, attrValues);
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(attr);
AttributeTable atttributeTable = new AttributeTable(v);
//Create a standard attribute table from the passed in parameters - certhash
CMSAttributeTableGenerator attrGen = new DefaultSignedAttributeTableGenerator(atttributeTable);
final byte[] signedHash = signHash(requestId, providerId, accessToken, hashBytes); //Retrieve signed hash from CSC.
ContentSigner nonSigner = new ContentSigner() {
#Override
public byte[] getSignature() {
return signedHash;
}
#Override
public OutputStream getOutputStream() {
return new ByteArrayOutputStream();
}
#Override
public AlgorithmIdentifier getAlgorithmIdentifier() {
return new DefaultSignatureAlgorithmIdentifierFinder().find( "SHA256WithRSA" );
}
};
org.bouncycastle.asn1.x509.Certificate cert2 = org.bouncycastle.asn1.x509.Certificate.getInstance(ASN1Primitive.fromByteArray(cert.getEncoded()));
JcaSignerInfoGeneratorBuilder sigb = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build());
// RevocationInfoResponse revocationInfoResponse = sealingService.getRevocationInfo(requestId, accessToken, revocationInfoRequest);
sigb.setSignedAttributeGenerator(attrGen);
// sigb.setDirectSignature( true );
gen.addSignerInfoGenerator(sigb.build(nonSigner, new X509CertificateHolder(cert2)));
CMSTypedData msg = new CMSProcessableInputStream( inputStream);
CMSSignedData signedData = gen.generate((CMSTypedData)msg, false);
byte[] cmsSignature = signedData.getEncoded();
inputStream.close();
externalSigning.setSignature(cmsSignature);
IOUtils.closeQuietly(signatureOptions);
return new FileInputStream(outFile);
If I use subfilter as SUBFILTER_ADBE_PKCS7_DETACHED and don't add addtibutesTable, then it works fine. But for SUBFILTER_ETSI_CADES_DETACHED, attributes needs to be added.

Itext7 PDF Sign Problem - The document has been altered or corrupted since the signature was applied

I have a program that signs pdf's via smartcard. Right now there is a new card that I need to integrate, but there is an error with the signature of the PDF (The document has been altered or corrupted since the signature was applied).
Error
I thought this is strange since there is no such error with the other cards..
The only difference between the cards is that this new one I use iaik to get the sign hash and not direct APDU commands, so, I'm in doubt if the signing problem is related with my implementation of the IAIK or I just need to change the way of signing with Itext 7 on this particular card.
Here is some code:
public byte[] signPDFDocument(byte[] pdfData, String requestKey, String requestIV, UserCertificates uc, String xLocation, String yLocation, String height, String width, String pageToSign) throws IOException{
int numPageToSign = Integer.parseInt(pageToSign);
InputStream inputStream = new ByteArrayInputStream(pdfData);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
PdfReader reader = new PdfReader(inputStream);
PdfSigner pdfSigner = new PdfSigner(reader, outStream, new StampingProperties());
//check page to sign not outOfBounds
if(numPageToSign > pdfSigner.getDocument().getNumberOfPages() || numPageToSign <= 0)
{
numPageToSign = 1;
}
//Certs Chain
Certificate[] certChain = new Certificate[1];
certChain[0] = uc.getCert();
// Create the signature appearance
PdfFont font = PdfFontFactory.createFont(FontConstants.TIMES_ROMAN);
PdfFont fontBold = PdfFontFactory.createFont(FontConstants.TIMES_BOLD);
Rectangle rect = new Rectangle(Integer.parseInt(xLocation), Integer.parseInt(yLocation), Integer.parseInt(width), Integer.parseInt(height));
PdfSignatureAppearance signatureAppearance = pdfSigner.getSignatureAppearance();
signatureAppearance
// Specify if the appearance before field is signed will be used
// as a background for the signed field. The "false" value is the default value.
.setReuseAppearance(false)
.setPageRect(rect)
.setPageNumber(numPageToSign)
.setReasonCaption("")
.setLocationCaption("");
pdfSigner.setFieldName("signature");
PdfFormXObject n0 = signatureAppearance.getLayer0();
PdfCanvas n0Canvas = new PdfCanvas(n0, pdfSigner.getDocument());
PdfFormXObject n2 = signatureAppearance.getLayer2();
Canvas n2Canvas = new Canvas(n2, pdfSigner.getDocument());
CertificateInfo.X500Name x500name = CertificateInfo.getSubjectFields((X509Certificate)certChain[0]);
String name = null;
if (x500name != null) {
name = x500name.getField("CN");
if (name == null)
name = x500name.getField("E");
}
//Signature
Text first = new Text("Assinado por: ").setFont(font).setFontSize(9);
Text second = new Text(name).setFontSize(8).setFont(fontBold);
Paragraph paragraph = new Paragraph().add(first).add(second);
paragraph.setMarginBottom(0.07f);
n2Canvas.add(paragraph);
//Date
Text date = new Text("Data: ").setFont(font).setFontSize(9);
Text date2 = new Text(new SimpleDateFormat("dd-MM-yyyy HH:mm Z").format(Calendar.getInstance().getTime())).setFont(fontBold).setFontSize(8);
paragraph = new Paragraph().add(date).add(date2);
n2Canvas.add(paragraph);
n2Canvas.close();
IExternalDigest digest = new BouncyCastleDigest();
IExternalSignature externalSignature = new SmartCardSignaturePDF();
// IExternalSignature externalSignature = new PrivateKeySignature(pk,"SHA-256",p.getName());
// OCSPVerifier ocspVerifier = new OCSPVerifier(null, null);
// IOcspClient ocspClient = new OcspClientBouncyCastle(ocspVerifier);
try {
pdfSigner.signDetached(digest, externalSignature, certChain, null, null, null, 0, CryptoStandard.CADES);
} catch (IOException | GeneralSecurityException e) {
}
return outStream.toByteArray();
}
And the External Signature
public class SmartCardSignaturePDF implements IExternalSignature{
#Override
public String getHashAlgorithm() {
return DigestAlgorithms.SHA256;
}
#Override
public String getEncryptionAlgorithm() {
return "RSA";
}
#Override
public byte[] sign(byte[] message) throws GeneralSecurityException {
try {
return signData(MessageDigest.getInstance("SHA-256").digest(message));
} catch (HardwareException | SignatureException e) {
}
return null;
}
}
And the code for the signature from the card
private byte[] signData(byte[] input_data) throws HardwareException {
//Key Mechanism, Label and ID
Mechanism KeyPairGenerationMechanism = Mechanism.get(PKCS11Constants.CKM_RSA_PKCS);
char [] signTemplate = "Sign".toCharArray();
byte[] id = "Sign".getBytes();
//Private Key Template
RSAPrivateKey rsaPrivateKeyTemplate = new RSAPrivateKey();
//rsaPrivate
rsaPrivateKeyTemplate.getToken().setBooleanValue(Boolean.TRUE);
rsaPrivateKeyTemplate.getId().setByteArrayValue(id);
rsaPrivateKeyTemplate.getLabel().setCharArrayValue(signTemplate);
//Public Key Template
RSAPublicKey rsaPublicKeyTemplate = new RSAPublicKey();
//rsaPublicKeyTemplate.getVerify().setBooleanValue(value);
rsaPublicKeyTemplate.getToken().setBooleanValue(Boolean.TRUE);
rsaPublicKeyTemplate.getId().setByteArrayValue(id);
rsaPublicKeyTemplate.getLabel().setCharArrayValue(signTemplate);
try {
this.cegerCardsession.findObjectsInit(rsaPrivateKeyTemplate);
PKCS11Object[] key = this.cegerCardsession.findObjects(1024);
if(key.length == 0)
{
KeyPairGenerationMechanism = Mechanism.get(PKCS11Constants.CKM_RSA_PKCS_KEY_PAIR_GEN);
KeyPair keyPair = this.cegerCardsession.generateKeyPair(KeyPairGenerationMechanism, rsaPublicKeyTemplate, rsaPrivateKeyTemplate);
KeyPairGenerationMechanism = Mechanism.get(PKCS11Constants.CKM_RSA_PKCS);
this.cegerCardsession.signInit(KeyPairGenerationMechanism, keyPair.getPrivateKey());
}
else
{
PrivateKey PKey = new PrivateKey();
PKey = (PrivateKey) key[0];
this.cegerCardsession.signInit(KeyPairGenerationMechanism, PKey);
}
byte[] signedData = this.cegerCardsession.sign(input_data);
return signedData;
} catch (TokenException e) {
throw new HardwareException(e.getMessage(), e.getCause());
}
}
Also, a link with a signed pdf file and the original one
Pdf's
I'm completly stuck for several days now and I have no idea on how to fix this :S

iText7 deferred signed pdf document shows “the document has been altered or corrupted since the signature was applied”

I checked with other similar issues on Stackoverflow, but it doesn’t work on my case.
Situation: I am developing an application which needs to sign the pdf document. The signing key is held by another company, let’s say it’s CompanyA.
I did the following steps:
Got the pdf document to sign ready.
Created a Temp pdf file which added an Empty Signature in the original pdf.
Read the Temp pdf to get the message digest. (Encode it to base64)
Send the message digest (Base64 encoded) to the CompanyA to get signed.
Get the signed digest (base64 encoded) from CompanyA.
Do the base64 decoding. And embedded the result into the Temp pdf to get the final signed pdf.
Everything goes well and I can get the final signed pdf. But When I open it in Adobe reader, it says “the document has been altered or corrupted since the signature was applied”.
I used this getHashBase64Str2Sign to get the message digest (in base64). This method calls the emptySignature() method to create the Temp file with empty signature, and then calls the getSignatureHash() method to read the Temp file to get the message digest.
public static String getHashBase64Str2Sign() {
try {
// Add BC provider
BouncyCastleProvider providerBC = new BouncyCastleProvider();
Security.addProvider(providerBC);
// Create parent path of dest pdf file, if not exist
File file = new File(DEST).getParentFile();
if (!file.exists()) {
file.mkdirs();
}
CertificateFactory factory = CertificateFactory.getInstance("X.509");
Certificate[] chain = new Certificate[1];
try (InputStream certIs = new FileInputStream(CERT)) {
chain[0] = factory.generateCertificate(certIs);
}
// Get byte[] hash
DeferredSigning app = new DeferredSigning();
app.emptySignature(SRC, TEMP, "sig", chain);
byte[] sh = app.getSignatureHash(TEMP, "SHA256", chain);
// Encode byte[] hash to base64 String and return
return Base64.getEncoder().encodeToString(sh);
} catch (IOException | GeneralSecurityException e) {
e.printStackTrace();
return null;
}
}
private void emptySignature(String src, String dest, String fieldname, Certificate[] chain)
throws IOException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), new StampingProperties());
PdfSignatureAppearance appearance = signer.getSignatureAppearance();
appearance.setPageRect(new Rectangle(100, 500, 200, 100));
appearance.setPageNumber(1);
appearance.setCertificate(chain[0]);
appearance.setReason("For test");
appearance.setLocation("HKSAR");
signer.setFieldName(fieldname);
/*
* ExternalBlankSignatureContainer constructor will create the PdfDictionary for
* the signature information and will insert the /Filter and /SubFilter values
* into this dictionary. It will leave just a blank placeholder for the
* signature that is to be inserted later.
*/
IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.Adobe_PPKLite,
PdfName.Adbe_pkcs7_detached);
// Sign the document using an external container.
// 8192 is the size of the empty signature placeholder.
signer.signExternalContainer(external, 100000);
}
private byte[] getSignatureHash(String src, String hashAlgorithm, Certificate[] chain)
throws IOException, GeneralSecurityException {
InputStream is = new FileInputStream(src);
// Get the hash
BouncyCastleDigest digest = new BouncyCastleDigest();
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, null, digest, false);
byte hash[] = DigestAlgorithms.digest(is, digest.getMessageDigest(sgn.getHashAlgorithm()));
return sgn.getAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, null, null);
}
private void createSignature(String src, String dest, String fieldName, byte[] sig)
throws IOException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
try (FileOutputStream os = new FileOutputStream(dest)) {
PdfSigner signer = new PdfSigner(reader, os, new StampingProperties());
IExternalSignatureContainer external = new MyExternalSignatureContainer(sig);
// Signs a PDF where space was already reserved. The field must cover the whole
// document.
PdfSigner.signDeferred(signer.getDocument(), fieldName, os, external);
}
}
Then, the message digest is sent to CompanyA for signing. After I got the signed digest from CompanyA (which is base64 encoded), I call the embedSignedHashToPdf() method to get the signed pdf document.
public static void embedSignedHashToPdf(String signedHash) {
try {
byte[] sig = Base64.getDecoder().decode(signedHash);
// Get byte[] hash
DeferredSigning app = new DeferredSigning();
app.createSignature(TEMP, DEST, "sig", sig);
} catch (IOException | GeneralSecurityException e) {
e.printStackTrace();
}
}
class MyExternalSignatureContainer implements IExternalSignatureContainer {
protected byte[] sig;
public MyExternalSignatureContainer(byte[] sig) {
this.sig = sig;
}
#Override
public void modifySigningDictionary(PdfDictionary signDic) {
}
#Override
public byte[] sign(InputStream arg0) throws GeneralSecurityException {
return sig;
}
}
At last, I can get the signed pdf document, but it shows error in Adobe Reader, like this:
Please check the original pdf, temp pdf, and the final signed pdf file as follows:
Original pdf - helloworld.pdf
Temp pdf - helloworld_empty_signed.pdf
Final pdf - helloworld_signed_ok.pdf
Ok, I see a number of issues in your code:
You determine the hash of the wrong bytes
In getSignatureHash with src containing the path of the intermediary PDF prepared for signing you do
InputStream is = new FileInputStream(src);
...
byte hash[] = DigestAlgorithms.digest(is, ...);
I.e. you calculate the hash value of the whole intermediary PDF.
This is incorrect!
The hash must be calculated for the PDF except the placeholder for the signature container which shall later be embedded:
The easiest way to hash that range, is to already calculate the hash in emptySignature and return it from there by using a hash-calculating IExternalSignatureContainer implementation instead of the dumb ExternalBlankSignatureContainer.
For example use this implementation:
public class PreSignatureContainer implements IExternalSignatureContainer {
private PdfDictionary sigDic;
private byte hash[];
public PreSignatureContainer(PdfName filter, PdfName subFilter) {
sigDic = new PdfDictionary();
sigDic.put(PdfName.Filter, filter);
sigDic.put(PdfName.SubFilter, subFilter);
}
#Override
public byte[] sign(InputStream data) throws GeneralSecurityException {
String hashAlgorithm = "SHA256";
BouncyCastleDigest digest = new BouncyCastleDigest();
try {
this.hash = DigestAlgorithms.digest(data, digest.getMessageDigest(hashAlgorithm));
} catch (IOException e) {
throw new GeneralSecurityException("PreSignatureContainer signing exception", e);
}
return new byte[0];
}
#Override
public void modifySigningDictionary(PdfDictionary signDic) {
signDic.putAll(sigDic);
}
public byte[] getHash() {
return hash;
}
}
like this:
PreSignatureContainer external = new PreSignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
signer.signExternalContainer(external, 16000);
byte[] documentHash = external.getHash();
You process the hash as if your CompanyA only returned naked signature bytes but you embed the bytes returned by CompanyA as if they were a full CMS signature container
In getSignatureHash you eventually don't return the alleged document hash but instead start constructing a CMS signature container and return its signed attributes:
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, null, digest, false);
...
return sgn.getAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, null, null);
Calculating PdfPKCS7.getAuthenticatedAttributeBytes(...) would only make sense if you then retrieved naked signature bytes for those attribute bytes and created a CMS signature container using the same PdfPKCS7 object:
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, sigtype, ocspList, crlBytes);
byte[] extSignature = RETRIEVE_NAKED_SIGNATURE_BYTES_FOR(sh);
sgn.setExternalDigest(extSignature, null, ENCRYPTION_ALGORITHM_USED_FOR_SIGNING);
byte[] encodedSig = sgn.getEncodedPKCS7(hash, sigtype, tsaClient, ocspList, crlBytes);
In particular your approach of calculating the signed attributes and then forgetting the PdfPKCS7 object does never make any sense at all.
But it actually looks like your CompanyA can return full CMS signature containers, not merely naked signature bytes as you immediately embed the returned bytes in embedSignedHashToPdf and createSignature and your example PDF does contain a full CMS signature container.
In such a case you don't need to use PdfPKCS7 at all but directly send the pre-calculated document digest to CompanyA to sign.
Thus, most likely you don't need PdfPKCS7 at all but instead send the document hash determined as explained above to CompanyA and embed their returned signature container.

How to retrieve my public and private key from the keystore we created

My task is the following:
Retrieve my public and private key from the keystore I created.
Use these keys to encrypt a paragraph using my RSA 2048-bit public key.
Digitally sign the result using the DSA-SHA-1 signature algorithm.
Save the digital signature output on a file called output.dat.
The program below is throwing error : "java.security.InvalidKeyException: No installed provider supports this key: sun.security.provider.DSAPublicKeyImpl".
import java.security.*;
import java.security.KeyStore.*;
import java.io.*;
import java.security.PublicKey;
import java.security.PrivateKey;
import javax.crypto.Cipher;
import java.nio.charset.*;
import sun.security.provider.*;
import javax.crypto.*;
public class Code {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
try {
/* getting data for keystore */
File file = new File(System.getProperty("user.home") + File.separatorChar + ".keystore");
FileInputStream is = new FileInputStream(file);
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
/*Information for certificate to be generated */
String password = "abcde";
String alias = "mykeys";
String alias1 = "skeys";
String filepath ="C:\\email.txt";
/* getting the key*/
keystore.load(is, password.toCharArray());
PrivateKey key = (PrivateKey)keystore.getKey(alias, "bemylife".toCharArray());
//PrivateKey key = cert1.getPrivateKey();
//PublicKey key1= (PrivateKey)key;
/* Get certificate of public key */
java.security.cert.Certificate cert = keystore.getCertificate(alias);
/* Here it prints the public key*/
System.out.println("Public Key:");
System.out.println(cert.getPublicKey());
/* Here it prints the private key*/
System.out.println("\nPrivate Key:");
System.out.println(key);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE,cert.getPublicKey());
String cleartextFile = "C:\\email.txt";
String ciphertextFile = "D:\\ciphertextRSA.png";
FileInputStream fis = new FileInputStream(cleartextFile);
FileOutputStream fos = new FileOutputStream(ciphertextFile);
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
byte[] block = new byte[32];
int i;
while ((i = fis.read(block)) != -1) {
cos.write(block, 0, i);
}
cos.close();
/* computing the signature*/
Signature dsa = Signature.getInstance("SHA1withDSA", "SUN");
dsa.initSign(key);
FileInputStream f = new FileInputStream(ciphertextFile);
BufferedInputStream in = new BufferedInputStream(f);
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) >= 0) {
dsa.update(buffer, 0, len);
};
in.close();
/* Here it prints the signature*/
System.out.println("Digital Signature :");
System.out.println( dsa.sign());
/* Now Exporting Certificate */
System.out.println("Exporting Certificate. ");
byte[] buffer_out = cert.getEncoded();
FileOutputStream os = new FileOutputStream(new File("d:\\signedcetificate.cer"));
os.write(buffer_out);
os.close();
/* writing signature to output.dat file */
byte[] buffer_out1 = dsa.sign();
FileOutputStream os1 = new FileOutputStream(new File("d:\\output.dat"));
os1.write(buffer_out1);
os1.close();
} catch (Exception e) {System.out.println(e);}
}
}
You have to read it from the keystore file (which probably ends in .jks) into a java.security.KeyStore object.
/**
* Reads a Java keystore from a file.
*
* #param keystoreFile
* keystore file to read
* #param password
* password for the keystore file
* #param keyStoreType
* type of keystore, e.g., JKS or PKCS12
* #return the keystore object
* #throws KeyStoreException
* if the type of KeyStore could not be created
* #throws IOException
* if the keystore could not be loaded
* #throws NoSuchAlgorithmException
* if the algorithm used to check the integrity of the keystore
* cannot be found
* #throws CertificateException
* if any of the certificates in the keystore could not be loaded
*/
public static KeyStore loadKeyStore(final File keystoreFile,
final String password, final String keyStoreType)
throws KeyStoreException, IOException, NoSuchAlgorithmException,
CertificateException {
if (null == keystoreFile) {
throw new IllegalArgumentException("Keystore url may not be null");
}
LOG.debug("Initializing key store: {}", keystoreFile.getAbsolutePath());
final URI keystoreUri = keystoreFile.toURI();
final URL keystoreUrl = keystoreUri.toURL();
final KeyStore keystore = KeyStore.getInstance(keyStoreType);
InputStream is = null;
try {
is = keystoreUrl.openStream();
keystore.load(is, null == password ? null : password.toCharArray());
LOG.debug("Loaded key store");
} finally {
if (null != is) {
is.close();
}
}
return keystore;
}
Once you have the KeyStore, you can get to the Certificate and the public and private keys.
But using that to sign text and save it in a file is more involved, and easy to do wrong. Take a look at Sign string using given Public Key and replace the getKeyPair method with one that uses the KeyStore. Something along the lines of
public static KeyPair getKeyPair(final KeyStore keystore,
final String alias, final String password) {
final Key key = (PrivateKey) keystore.getKey(alias, password.toCharArray());
final Certificate cert = keystore.getCertificate(alias);
final PublicKey publicKey = cert.getPublicKey();
return KeyPair(publicKey, (PrivateKey) key);
}
(obviously a little rougher, I didn't have a sample handy)
The problem is that a DSA key is unsuitable for RSA encryption. You need an RSA key for encryption, maybe you can switch your signature algorithm to RSA/SHA1 to avoid the need for two keys..
In Spring Boot(my example is using 2.4.x), you can use a bean for your KeyStore, like so:
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
#Configuration
public class KeyStoreConfiguration {
private static final String KEY_STORE = "keystore.p12";
private static final String KEY_STORE_TYPE = "PKCS12";
private static final String KEY_STORE_PASSWORD = "password";
#Bean
public KeyStore keyStore() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE);
keyStore.load(new ClassPathResource(KEY_STORE).getInputStream(), KEY_STORE_PASSWORD.toCharArray());
return keyStore;
}
}
and you can then retrieve the public and private key like so:
import java.security.PrivateKey;
import java.security.PublicKey;
PrivateKey privateKey = (PrivateKey) keyStore.getKey("my-alias", "my-password".toCharArray());
PublicKey publicKey = keyStore.getCertificate("my-alias").getPublicKey();
trusted.load(in, ((PBCApplication) context.getApplicationContext()).getBuildSettings().getCertificatePass());
Enumeration enumeration = trusted.aliases();
while (enumeration.hasMoreElements()) {
String alias = (String) enumeration.nextElement();
System.out.println("alias name: " + alias);
Certificate certificate = trusted.getCertificate(alias);
certificate.getPublicKey();
}
I don't have the Java code stored at the top of my brain, but some general sanity checks are:
is the public certificate you want stored where you want it? In particular, my recollection is that the certificate with the public key and the private key are stored together under a single alias, so the two alias setting you have there seems really odd. Try storing both under the same alias and referencing it in both the private and public key calls.
can you get anything else out of the certificate - for example, subject DN or issuer DN are both must-have fields in a certificate. That gives you a good proof that the certificate is being read as expected.
in almost any crypto transaction, be very careful with how you read from files and transfer your encoding methods. If you've created your File IO and pulled from it in a weird way, you can corrupt the encoding of the key material. That's a last thing to check - usually Java and JKS haven't been so bad for this, but it happens. On the same vein, be clear about the format of the file - JKS files are different from PKCS 12 files, for example.

itext signing pdf

I'm currently developing an ERP application which uses iText for creating and signing PDF files. The idea is that the app can generate a PDF file with a bill, then use PdfStamper or any other class to sign it with a digital signature. Here's my code:
CREATING AND EDITING THE BILL
File f1 = null;
f1 = new File("myFilePath");
f1.delete();
if ((f1 != null) && (f1.createNewFile()))
{
//Here I call the procedure that creates the bill
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(f1));
PdfFormField field = PdfFormField.createSignature(writer);
PdfSigner pdfSigner = new PdfSigner();
pdfSigner.setFileIn(f1.getAbsolutePath());
pdfSigner.setFileKey("myKeyPath.pfx");
pdfSigner.setFileKeyPassword("myPassword");
pdfSigner.setFileOut("myOutputPath");
if (pdfGenerator.factura(writer, pdfSigner, document))
{
//Here I show the File to the user
}
}
else
{
//Here I show an error
}
PROCEDURE "factura"
public boolean factura (PdfWriter writer, PdfSigner signer, Document document) throws NullPointerException
{
try
{
//Here I set a PdfPageEvent before attaching it to the PdfWriter
writer.setPageEvent(myPdfPageEvent);
document.open();
//Here I manipulate the Document to generate the bill
signer.signPdf();
document.close();
return true;
}
//catch 4 or 5 different types of exceptions and return false if needed
}
CLASS PdfSigner
public class PdfSigner
{
private String fileKey = null;
private String fileKeyPassword = null;
private String fileIn = null;
private String fileOut = null;
public PdfSigner() {}
public boolean signPdf() throws IOException, DocumentException, Exception
{
if (fileKey == null || fileKeyPassword == null || fileIn == null || fileOut == null) return false;
try
{
KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(new FileInputStream(fileKey), fileKeyPassword.toCharArray());
String alias = (String) ks.aliases().nextElement();
PrivateKey key = (PrivateKey) ks.getKey(alias, fileKeyPassword.toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
//BOOOOOM!
PdfReader pdfReader = new PdfReader((new File(fileIn)).getAbsolutePath());
FileOutputStream outputFile = new FileOutputStream(fileOut);
PdfStamper pdfStamper = PdfStamper.createSignature(pdfReader, outputFile, '?');
PdfSignatureAppearance sap = pdfStamper.getSignatureAppearance();
sap.setCrypto(key, chain, null, PdfSignatureAppearance.SELF_SIGNED);
sap.setVisibleSignature(new Rectangle(10, 10, 50, 30), 1, "sign_rbl");
pdfStamper.close();
return true;
}
catch (Exception key)
{
throw new Exception(key);
}
}
//getters and setters
}
Well, this is wrong, but I don't know where does it fail. If I try to run it, it usually throw an exception in the line that I've marked with a "BOOOOM!", before setting the PdfReader. But if I try to sign it outside the procedure "factura", after closing the document, the exception is usually thrown almost in the last line, when I close the PdfStamper. In both cases, the cause is always the same: "PDF header signature not found."
Anyone has an idea of what's happening? I am 99% sure the paths I'm giving to the program are right, and the digital signature and password are also right...
Thanks
PS: I swear I've tried to find a solution among the multiple answers in this page, but none of them proved to be of any use to me