In my app I have an option for signing pdf files . There are two options for pdf signing , one where a new signature is created and the second where I need to sign empty signature field. I did the part of creating a new signature field and it works fine , now I am having problems with signing empty signature fields . This is my code
KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, null) ;
//ovo smo ubacili
Enumeration en = ks.aliases();
// String alias = (String)en.nextElement();
PrivateKey key = (PrivateKey)ks.getKey(alias, "password".toCharArray());
java.security.cert.Certificate[] chain = ks.getCertificateChain(alias);
//location of pdf document to sign
PdfReader reader = new PdfReader(jTextField1.getText());
String [] delovi=jTextField1.getText().split("\\\\");
String potisaniFajl=delovi[delovi.length-1];
new File(System.getProperty("user.home") + "\\Desktop\\Potpisani Fajlovi\\").mkdirs();
//signed pdf location
FileOutputStream fout = new FileOutputStream(System.getProperty("user.home") + "\\Desktop\\Potpisani Fajlovi\\"+potisaniFajl);
PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0',null,true);
PdfSignatureAppearance appearance = stp.getSignatureAppearance();
appearance.setCrypto(null, chain, null, PdfSignatureAppearance.SELF_SIGNED);
//appearance.setCrypto(key, chain, null,PdfSignatureAppearance.WINCER_SIGNED);
//appearance.setCrypto(null, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
appearance.setReason("Potpis kompenzacije");
appearance.setLocation("Foobar");
//lokacija potpisa
appearance.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, "dva");
appearance.setExternalDigest(new byte[128], null, "RSA");
appearance.preClose();
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initSign(key);
byte buf[] = new byte[8192];
int n;
InputStream inp = appearance.getRangeStream();
while ((n = inp.read(buf)) > 0) {
signature.update(buf, 0, n);
}
PdfPKCS7 sig = appearance.getSigStandard().getSigner();
sig.setExternalDigest(signature.sign(), null, "RSA");
PdfDictionary dic = new PdfDictionary();
dic.put(PdfName.CONTENTS,new PdfString(sig.getEncodedPKCS1()).setHexWriting(true));
appearance.close(dic);
This code adds a new signature , what changes I need to do to sign empty signature field name "GoodSignature"
what changes I need to do to sign empty signature field name "GoodSignature"
In your current code you call an overload of PdfSignatureAppearance.setVisibleSignature for creating a new signature field:
appearance.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, "dva");
It is documented (JavaDocs) as:
/**
* Sets the signature to be visible. It creates a new visible signature field.
* #param pageRect the position and dimension of the field in the page
* #param page the page to place the field. The fist page is 1
* #param fieldName the field name or <CODE>null</CODE> to generate automatically a new field name
*/
public void setVisibleSignature(Rectangle pageRect, int page, String fieldName)
For your new task you have to use instead this overload:
/**
* Sets the signature to be visible. An empty signature field with the same name must already exist.
* #param fieldName the existing empty signature field name
*/
public void setVisibleSignature(String fieldName)
In your case the call would be :
appearance.setVisibleSignature("GoodSignature");
That being said I'd advise you read Digital Signatures for PDF documents, A White Paper by Bruno Lowagie (iText Software). Your code seems to make use of numerous outdated techniques and you should update.
Related
I am trying to add Key Usage to certificateSigningInfo in Java without using BouncyCastle. I have the following method which will create certificateSigningInfo that can be further signed to create PKCS#10:
public static byte[] createCertificationRequestInfo(X500Name x500Name, PublicKey publicKey) throws IOException {
final DerOutputStream der1 = new DerOutputStream();
der1.putInteger(BigInteger.ZERO);
x500Name.encode(der1);
der1.write(publicKey.getEncoded());
// der encoded certificate request info
final DerOutputStream der2 = new DerOutputStream();
der2.write((byte) 48, der1);
byte[] toReturn = der2.toByteArray();
der2.close();
return toReturn;
}
I am trying to add Key Usage with adding the following:
KeyUsageExtension ku = new KeyUsageExtension();
ku.set(KeyUsageExtension.NON_REPUDIATION, true);
ku.set(KeyUsageExtension.KEY_ENCIPHERMENT, true);
ku.set(KeyUsageExtension.DIGITAL_SIGNATURE, true);
ku.encode(der1);
The certificateSigningInfo is created but it can't be validate as it has some missing values...
For example, the following CSR is created:
-----BEGIN NEW CERTIFICATE REQUEST-----
MIIErDCCApQCAQAwWjENMAsGA1UEBhMEVGVzdDENMAsGA1UECBMEVGVzdDENMAsGA1UEBxMEVGVz
dDENMAsGA1UEChMEVGVzdDENMAsGA1UECxMEVGVzdDENMAsGA1UEAxMEVGVzdDCCAiEwDQYJKoZI
hvcNAQEBBQADggIOADCCAgkCggIAr11wXkShGHeEVRxl1K4D/7Ow8uIgVro35h/WMBKl5UWOEqBc
ajTnU9AMN+u/rVa5uRt8HcHhWF8Y4RIMoNxlMuxBu36UbxKBnPza8Y1/Dbn0HGwzematfnFYS7B7
HdyVoj6yRcSo2tM/p8bmUGpxr1NSXsEtbxVINFhuyMZnwMpuVUsqTgB58Uo/+sjGtIxDVkLaMNs4
d/HCe2rwb5rYhkvAEgXtAtoWtDD9PPZTpxhKqO+cMYGZ0HqwFyQEu687ONUpVlA068DsgzwM6oh5
w3y2OJ8PiCXW+ojqxGr5mm7ig//mbgkk7QiwoZ3taYoqAdRfGlQsOZsVzHPzAIH84CHlF2LMRcIO
M8dA3eQSopjPDPOZixo8PGQ/cYtQPgyBVGmiQt/93JQ/BAypu9olO70y91nd+cEsEzoZdGkuFnTR
KVT67ye0GnxFik6Y/VJxpE36NPbofOXVRxy8jbazJGg/AzETAGmkKXoNAbzqvVCaV2zeAOZZmTFx
rgjwQxvgW2oxpv5SCBDxtsH+/JpuqEtg5y3jzLFuibSmGs6qNDATEDIG1F1Xl0+1I6ygIE+tcycI
m/9SmlIX9UsiSEvSJe0kZOwOIrByzYhezeXdbgSHiD2WiuMe1XvXJBkqpV0wuwhw74vRKAJtz3vC
n1DNVBmBWMPWfxlVczG+HwgLBSECAwEAATAOBgNVHQ8BAf8EBAMCBeAwDQYJKoZIhvcNAQENBQAD
ggIBAHCEHA47S0jO7AXvF7SDXtPPV2zlgUFtvCx0DfRerElYcmQFg5ylspfvkbQnlUFOcpKgoi1/
Kgq5vchLjKVEJPJK9066NsNTRy1Ayt4f3Ne7yQ46cnrL729x7TLUcWijDgmfJE/Cp4eC0qTF87mH
rYeOK0+1azki0r6/ToM9EliDU497Tsl2CWmAJlP+hkWQRa80uPFXkEV+UH0vQwOc8mf/aW+q3LHf
BXvPHcH3J9lPtDHMZgKav7Vi4sFU8tlCzm7QE6/jU49BN+Ptgp34k8Hw+VV+4lpZX2QpkDMvaPg5
Zd7LYNGhayP//EevCftiliWsNJSHa8aA6zqYvsTnBoEPFPL4sVfXsWp18AbK294WhF5vIe+8AFks
RHMLE+Nl13SXO3wHlNDT5jXKUGFmbCOASsUjyrpfQcwVMJ0/muAX43r6zo2YatZKgmCtVrBtKVuE
U7KMJW2aEDO7ML7+47VER8r1LYWSt96OY/9Pre+UehMuDloUC5B9nSf5TKyCShnzxXoNAE7izr2A
l3x9O071c9d/pRKjUBu9Q/IrSFT/blg94zGov+9FplcXc2Ygnblt5UNjAs5XcoC6ckhGPkrcZbw6
3Qk5R9TTJGX9wpzKTFPWJF735TaFqkJNwn1U5kTKWCTgbGUi3U98gTCCV4OkeT+Fo+pWuOqz5NV5
Q778
-----END NEW CERTIFICATE REQUEST-----
Any help is appreciated.
PKCS10 type CertificationRequestInfo can't directly contain an Extension, or even Extensions (which is SEQUENCE OF Extension). Instead it can contain Attributes which is SET OF Attribute each of which is SEQUENCE of OID and SET OF values constrained by a notional InfoSet, or in the old syntax ANY DEFINED BY --metaspec. One possible Attribute is PKCS9 5.4.2 extensionRequest which does contain Extensions which can include the one for KeyUsage.
Thus to add this to a PKCS10 you need something like the following. And as long as you are using the undocumented and possibly unreliable sun.security classes, there is one for PKCS10 that replaces most of the code you posted.
import sun.security.pkcs.*;
import sun.security.pkcs10.*; // separate in j8 (and later? not checked)
import sun.security.util.*;
import sun.security.x509.*;
// dummy setup; replace as appropriate
X500Name name = new X500Name("O=Widgets Inc, CN=testcert");
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(1024); KeyPair pair = gen.generateKeyPair();
KeyUsageExtension ku = new KeyUsageExtension();
ku.set(KeyUsageExtension.NON_REPUDIATION, true);
ku.set(KeyUsageExtension.KEY_ENCIPHERMENT, true);
ku.set(KeyUsageExtension.DIGITAL_SIGNATURE, true);
CertificateExtensions exts = new CertificateExtensions();
exts.set(KeyUsageExtension.IDENT,ku);
PKCS10Attribute extreq = new PKCS10Attribute (PKCS9Attribute.EXTENSION_REQUEST_OID, exts);
PKCS10 csr = new PKCS10 (pair.getPublic(), new PKCS10Attributes (new PKCS10Attribute[]{ extreq }));
Signature signer = Signature.getInstance("SHA256withRSA"); // or adapt to key
signer.initSign(pair.getPrivate());
csr.encodeAndSign(name, signer);
// dummy output; replace
FileOutputStream out = new FileOutputStream ("SO49985805.der");
out.write(csr.getEncoded()); out.close();
For a project I have to digitally sign PDFs on an additionally created page by multiple people in a workflow. To realize this we're using the iText 7 libraries with following code, based on Bruno Lowagie's examples:
public static void main(String[] args) throws IOException, GeneralSecurityException, XMPException {
String path = "F:/Java/keystores/testPdfSign";
char[] pass = "test".toCharArray();
KeyStore ks = KeyStore.getInstance("pkcs12", "SunJSSE");
ks.load(new FileInputStream(path), pass);
String alias = "";
Enumeration<String> aliases = ks.aliases();
while (alias.equals("tester")==false && aliases.hasMoreElements())
{
alias = aliases.nextElement();
}
PrivateKey pk = (PrivateKey) ks.getKey(alias, pass);
Certificate[] chain = ks.getCertificateChain(alias);
PDFSign app = new PDFSign();
app.sign(SRC, DEST, chain, pk, DigestAlgorithms.SHA1, "SunJSSE", PdfSigner.CryptoStandard.CMS, "Test", "Test", null, null, null, 0);
}
public void sign(String src, String dest,
Certificate[] chain, PrivateKey pk,
String digestAlgorithm, String provider, PdfSigner.CryptoStandard subfilter,
String reason, String location,
Collection<ICrlClient> crlList,
IOcspClient ocspClient,
ITSAClient tsaClient,
int estimatedSize)
throws GeneralSecurityException, IOException, XMPException {
// Creating the reader and the signer
PdfDocument document = new PdfDocument(new PdfReader(SRC), new PdfWriter(DEST+"_temp"));
if (initial == true)
{
document.addNewPage();
}
int pageCount = document.getNumberOfPages();
document.close();
PdfSigner signer = new PdfSigner(new PdfReader(DEST+"_temp"), new FileOutputStream(DEST), true);
// Creating the appearance
if (initial == true)
{
signer.setCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS);
}
PdfSignatureAppearance appearance = signer.getSignatureAppearance()
.setReason(reason)
.setLocation(location)
.setReuseAppearance(false);
Rectangle rect = new Rectangle(10, 400, 100, 100);
appearance
.setPageRect(rect)
.setPageNumber(pageCount);
appearance.setRenderingMode(RenderingMode.NAME_AND_DESCRIPTION);
signer.setFieldName(signer.getNewSigFieldName());
// Creating the signature
IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
ProviderDigest digest = new ProviderDigest(provider);
signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
}
This results in an invalid signature in the new signed version of the PDF, as Adobe Acrobat Reader says it has been edited after signing. Surprisingly when I open the file in Foxit Reader it says it hasn't been modified and is valid.
Also what I tried was to leave out the first step of adding a new page and just sign on the last page of the original document, then the signature is valid in Adobe Reader, but no solution for my situation, as an extra page is a must have.
The other thing I tried was not setting the certificationLevel to CERTIFIED_FORM_FILLING_AND_ANNOTATIONS, but just leaving it at the default NOT_CERTIFIED, this way I also had a valid signature on a new page, but this is not a solution either, because it won't let me add any additional signatures later on.
Does someone have an idea what could be the reason for Adobe Reader rating the signature as invalid and/or has a solution to this problem?
Thanks in Advance
David
In short
I could not reproduce the OP's issue. Running his code (with slight adaptions to local circumstances) resulted in a java.security.NoSuchAlgorithmException: no such algorithm: SHA1 for provider SunJSSE. Having replaced the provider argument "SunJSSE" to the sign call with "BC", on the other hand, the code creates a properly certified PDF.
The adapted code
I usually examine code from stackoverflow in the form of a JUnit test; this implies a few changes. Furthermore, the OP's code contained a number of variables which were referenced but not defined; these had to be given a definition. Finally i load the file to sign from a resource as stream, not from the file system as file.
Thus:
final static File RESULT_FOLDER = new File("target/test-outputs", "signature");
#BeforeClass
public static void setUpBeforeClass() throws Exception
{
RESULT_FOLDER.mkdirs();
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
}
#Test
public void testSignLikeXinDHA() throws GeneralSecurityException, IOException, XMPException
{
String path = "keystores/demo-rsa2048.p12";
char[] pass = "demo-rsa2048".toCharArray();
KeyStore ks = KeyStore.getInstance("pkcs12", "SunJSSE");
ks.load(new FileInputStream(path), pass);
String alias = "";
Enumeration<String> aliases = ks.aliases();
while (alias.equals("demo") == false && aliases.hasMoreElements())
{
alias = aliases.nextElement();
}
PrivateKey pk = (PrivateKey) ks.getKey(alias, pass);
Certificate[] chain = ks.getCertificateChain(alias);
try ( InputStream resource = getClass().getResourceAsStream("/mkl/testarea/itext7/content/test.pdf"))
{
sign(resource, new File(RESULT_FOLDER, "test_XinDHA_signed_initial.pdf").getAbsolutePath(),
chain, pk, DigestAlgorithms.SHA1, /*"SunJSSE"*/"BC", PdfSigner.CryptoStandard.CMS, "Test", "Test",
null, null, null, 0, true);
}
}
public void sign(InputStream src, String dest, Certificate[] chain, PrivateKey pk, String digestAlgorithm,
String provider, PdfSigner.CryptoStandard subfilter, String reason, String location,
Collection<ICrlClient> crlList, IOcspClient ocspClient, ITSAClient tsaClient, int estimatedSize,
boolean initial)
throws GeneralSecurityException, IOException, XMPException
{
// Creating the reader and the signer
PdfDocument document = new PdfDocument(new PdfReader(src), new PdfWriter(dest + "_temp"));
if (initial == true)
{
document.addNewPage();
}
int pageCount = document.getNumberOfPages();
document.close();
PdfSigner signer = new PdfSigner(new PdfReader(dest + "_temp"), new FileOutputStream(dest), true);
// Creating the appearance
if (initial == true)
{
signer.setCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS);
}
PdfSignatureAppearance appearance = signer.getSignatureAppearance().setReason(reason).setLocation(location)
.setReuseAppearance(false);
Rectangle rect = new Rectangle(10, 400, 100, 100);
appearance.setPageRect(rect).setPageNumber(pageCount);
appearance.setRenderingMode(RenderingMode.NAME_AND_DESCRIPTION);
signer.setFieldName(signer.getNewSigFieldName());
// Creating the signature
IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
ProviderDigest digest = new ProviderDigest(provider);
signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
}
(AddPageAndSign.java)
Running the code
I ran the code using a fairly recent Oracle Java 8 with Unlimited Strength JavaTM Cryptography Extension Policy Files, BouncyCastle 1.49, and iText either in version 7.0.0 or 7.0.1-SNAPSHOT (the current development branch).
(Definitively use an Oracle Java as downloaded from their web site, some variants of the Oracle JDK (supplied by some Linux distributions) contains changes in the security providers which can break your code.)
Running the code using the provider argument "SunJSSE" to the sign call results in
java.security.NoSuchAlgorithmException: no such algorithm: SHA1 for provider SunJSSE
at sun.security.jca.GetInstance.getService(GetInstance.java:87)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:206)
at java.security.Security.getImpl(Security.java:698)
at java.security.MessageDigest.getInstance(MessageDigest.java:227)
at com.itextpdf.signatures.SignUtils.getMessageDigest(SignUtils.java:134)
at com.itextpdf.signatures.DigestAlgorithms.getMessageDigest(DigestAlgorithms.java:182)
at com.itextpdf.signatures.ProviderDigest.getMessageDigest(ProviderDigest.java:69)
at com.itextpdf.signatures.SignUtils.getMessageDigest(SignUtils.java:127)
at com.itextpdf.signatures.PdfSigner.signDetached(PdfSigner.java:528)
at mkl.testarea.itext7.signature.AddPageAndSign.sign(AddPageAndSign.java:125)
at mkl.testarea.itext7.signature.AddPageAndSign.testSignLikeXinDHA(AddPageAndSign.java:81)
Running the code using the provider argument "BC" to the sign call results in a properly certified PDF with the signature visualization on an extra page:
Why using SunJSSE doesn't make sense
The exception I get with the "SunJSSE" provider actually is not surprising as that provider does not provide a SHA1 algorithm.
According to its documentation by Oracle, it provides no MessageDigest algorithm as such at all, merely in combination as a signature algorithm (SHA1withRSA).
Thus, the IExternalSignature defined in sign as
IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
will work because here SHA1withRSA will be used, but the ProviderDigest defined there as
ProviderDigest digest = new ProviderDigest(provider);
will fail because it attempts to use the message digest algorithm SHA1.
As an aside
You use SHA1. As this message digest algorithm is less and less trusted in the context of signature creation, that is not a good idea. I would advise switching to an algorithm of the SHA2 famaily.
I have to extract signature fields from PDF signed document to create a printed signature version. Until now I've been able to recover signer certificate, reason, signing date and other fields using this iText code:
PdfReader reader = new PdfReader(signedPdf);
AcroFields af = reader.getAcroFields();
ArrayList<String> names = af.getSignatureNames();
SimpleDateFormat sdf = new SimpleDateFormat(
"dd/MM/yyyy 'a las' HH:mm:ss");
for (int i = 0; i < names.size(); ++i) {
StringBuilder sb = new StringBuilder();
String name = names.get(i);
PdfPKCS7 pk = af.verifySignature(name);
String firmante = CertificateInfo.getSubjectFields(
pk.getSigningCertificate()).getField("CN");
sb.append("Nombre del firmante: " + firmante + "\n");
Date signDate = pk.getSignDate().getTime();
String sdate = sdf.format(signDate);
sb.append("Fecha y hora de la firma: " + sdate + "\n");
String razon = pk.getReason();
sb.append("proposito: " + razon + "\n");
}
As far as I know, the PDF signature was made with iText PdfPkcs7 class using setExternalDigest method to add a PKCS1 byte array created in an external application. The file looks correctly signed and validated by external tools.
// Create the signature
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA1", "BC", null, false);
//pkcs1Bytes is a byte array with the signed document hash
sgn.setExternalDigest(pkcs1Bytes, null, "RSA");
However, one of the required fields for printed version is a "signature digital stamp" which is a base 64 string of signed document hash or PKCS1.
It's possible to extract that PKCS1 bytes from the signed PDF document?
EDITED: I forgot to mention that when I use PdfPKCS7.getEncodedPKCS1() method after verifying signature it throws ExceptionConverter: java.security.SignatureException: object not initialized for signing
I have reviewed the code and seems the class PdfPKCS7 does not allow to access the digest. But, the content is stored in a private member PdfPKCS7.digest. So using reflection will allow you to extract it.
I have found a similar example here and here (is basically the same)
PdfPKCS7 pdfPkcs7 = acroFields.verifySignature(name);
pdfPkcs7.verify();
Field digestField = PdfPKCS7.class.getDeclaredField("digest");
digestField.setAccessible(true);
byte[] digest = (byte[]) digestField.get(pdfPkcs7);
I think the variable you need is digest because the value is assigned in getEncodedPKCS1 when performing the signature
public byte[] getEncodedPKCS1() {
try {
if (externalDigest != null)
digest = externalDigest;
else
digest = sig.sign();
//skipped content
And is used in verify() in the following way verifyResult = sig.verify(digest);
Note that digest is a private variable, so the name or content could depend on the version. Review the code of your specific version.
Considering your code I assume you are using a 5.x iText version, not a 7.x.
You can either use reflection (cf. this older answer or pedrofb's answer here) or you can simply extract the CMS signature container using iText and then analyze that container using BouncyCastle; a version of BC usually already is present anyways if you use signature related functionality of iText.
As the OP has already observed, PdfPKCS7.getEncodedPKCS7() fails with "ExceptionConverter: java.security.SignatureException: object not initialized for signing". The reason is that this method is meant for retrieving a signature container newly constructed by the PdfPKCS7 instance.
To extract the CMS signature container using iText you can use this code instead:
AcroFields fields = reader.getAcroFields();
PdfDictionary sigDict = fields.getSignatureDictionary(name);
PdfString contents = sigDict.getAsString(PdfName.CONTENTS);
byte[] contentBytes = contents.getOriginalBytes();
contentBytes now contains the encoded CMS container (plus some trailing bytes, usually null bytes, as the Contents value usually is larger than required for the signature container).
Analyzing this container using BouncyCastle is not difficult but the details may depend on the exact BouncyCastle version you use.
I am try to add a digital signature to a pdf using java api, and signature was read by epass2003 token. so,here i done this job(add digital signature to pdf),
and its working fine, but when i open this pdf document in another system it shows
the "Atleast one signature has problem", bu in my system validate sign correctly please help me.I have attached my code below please find it.
public class Test {
public static void main(String args[]) throws IOException, GeneralSecurityException, DocumentException, CertificateVerificationException{
// Create instance of SunPKCS11 provider
String userFile = "C:/results/test.pdf";
String userFile_signed = "C:/results/test_signed.pdf";
String pkcs11Config = "name=eToken\nlibrary=C:\\Windows\\System32\\eps2003csp11.dll";
java.io.ByteArrayInputStream pkcs11ConfigStream = new java.io.ByteArrayInputStream(pkcs11Config.getBytes());
sun.security.pkcs11.SunPKCS11 providerPKCS11 = new sun.security.pkcs11.SunPKCS11(pkcs11ConfigStream);
java.security.Security.addProvider(providerPKCS11);
// Get provider KeyStore and login with PIN
String pin = "12345678";
java.security.KeyStore keyStore = java.security.KeyStore.getInstance("PKCS11", providerPKCS11);
keyStore.load(null, pin.toCharArray());
// Enumerate items (certificates and private keys) in the KeyStore
java.util.Enumeration<String> aliases = keyStore.aliases();
String alias = null;
while (aliases.hasMoreElements()) {
alias = aliases.nextElement();
System.out.println(alias);
}
PrivateKey pk = (PrivateKey)keyStore.getKey(alias, "12345678".toCharArray());
Certificate[] chain = keyStore.getCertificateChain(alias);
OcspClient ocspClient = new OcspClientBouncyCastle();
TSAClient tsaClient = null;
for (int i = 0; i < chain.length; i++) {
X509Certificate cert = (X509Certificate)chain[i];
String tsaUrl = CertificateUtil.getTSAURL(cert);
if (tsaUrl != null) {
tsaClient = new TSAClientBouncyCastle(tsaUrl);
break;
}
}
List<CrlClient> crlList = new ArrayList<CrlClient>();
crlList.add(new CrlClientOnline(chain));
Test t = new Test();
t.sign(userFile, userFile_signed, chain, pk, DigestAlgorithms.SHA256, providerPKCS11.getName(),
CryptoStandard.CMS, "Test", "Signature", crlList, ocspClient, tsaClient, 0);
}
public void sign(String src, String dest,
Certificate[] chain, PrivateKey pk,
String digestAlgorithm, String provider, CryptoStandard subfilter,
String reason, String location,
Collection<CrlClient> crlList,
OcspClient ocspClient,
TSAClient tsaClient,
int estimatedSize)
throws GeneralSecurityException, IOException, DocumentException {
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
// Creating the appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(reason);
appearance.setLocation(location);
appearance.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, "sig");
// Creating the signature
ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
}
}
so above is my code please help me.
Looking at the signature properties one sees:
This dialog states the problem:
The signer's identity is unknown because it has not been included in your list of trusted certificates and none of its parent certificates are trusted certificates.
Furthermore a look at the signer's certificate shows:
Thus, your code only embeds the signer certificate itself, not its certificate path (otherwise they would have shown in the certificate viewer window). Unfortunately the issuer certificate (SafeScrypt sub-CA for RCAI Class 2 2014) is not immediately trusted, neither is that certificate's issuer (SafeScrypt CA 2014), but that certificate's issuer (CCA India 2014) in turn is.
Most likely on your computer either the whole certificate chain is known or at least up to a certificate which is explicitly trusted.
To get the same effect on other computers which only know the root certificate, simply add the certificates for "SafeScrypt sub-CA for RCAI Class 2 2014" and "SafeScrypt CA 2014" to your Certificate[] chain.
I'm sign document, using Itext. I have that method:
public static void sign(String src, String dest, Certificate[] chain,PrivateKey pk, String digestAlgorithm, String provider,CryptoStandard subfilter, TSAClient tsa )
{
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
// Creating the appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(new Rectangle(10, 20, 100, 200), 1, "sig");
// Creating the signature
ExternalDigest digest = new BouncyCastleDigest();
ExternalSignature signature = new PrivateKeySignature(pk,
digestAlgorithm, provider);
MakeSignature.signDetached(appearance, digest, signature, chain, null,null, tsa, 0, subfilter);
// ALREADY SIGNED. ADD LTB NOW.
CrlClient crlClient = new CrlClientOnline("http://crl.mycrl.com/mycrl.crl");
OcspClient ocspClient=new OcspClientBouncyCastle();
addLtv(DEST, DEST2, ocspClient, crlClient, tsa);
}
and I sign:
sign(SRC, String.format(DEST, 1), chain, pk, DigestAlgorithms.SHA256, provider.getName(), CryptoStandard.CMS, "For Testing", " location", tsa);
everything works. PDF is signed well.
but then, I can't add ltv. I use code from itext documentation:
public static void addLtv(String src, String dest, OcspClient ocsp, CrlClient crl,
TSAClient tsa) throws IOException, DocumentException,
GeneralSecurityException {
PdfReader r = new PdfReader(src);
FileOutputStream fos = new FileOutputStream(dest);
PdfStamper stp = PdfStamper.createSignature(r, fos, '\0', null, true);
LtvVerification v = stp.getLtvVerification();
AcroFields fields = stp.getAcroFields();
List<String> names = fields.getSignatureNames();
String sigName = names.get(names.size() - 1);
PdfPKCS7 pkcs7 = fields.verifySignature(sigName);
if (pkcs7.isTsp()) {
v.addVerification(sigName, ocsp, crl,
LtvVerification.CertificateOption.SIGNING_CERTIFICATE,
LtvVerification.Level.OCSP_CRL,
LtvVerification.CertificateInclusion.NO);
}
else {
for (String name : names) {
v.addVerification(name, ocsp, crl,
LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL,
LtvVerification.CertificateInclusion.NO);
}
}
PdfSignatureAppearance sap = stp.getSignatureAppearance();
LtvTimestamp.timestamp(sap, tsa, null);
}
EDITED:
everything works, but at line
LtvTimestamp.timestamp(sap, tsa, null);
I have that error:
Exception in thread "main" java.io.IOException: Not enough space
that's my pdf:
https://www.dropbox.com/s/o05rw6ubiuslm4j/DOC_SIGNED.pdf
Exception in thread "main" java.io.IOException: Not enough space
at com.itextpdf.text.pdf.security.LtvTimestamp.timestamp(LtvTimestamp.java:103)
at ge.digital.signature.DocumentSigner.DigitalSignature.addLtv(MySignature.java:132)
at ge.digital.signature.DocumentSigner.DigitalSignature.main(MySignature.java:163)
That IOException occurs when the space reserved in the PDF for the integration of the time stamp does not suffice. Thus, you have to change the method getTokenSizeEstimate of the TSAClient instance tsa you call your sign method with to return a larger estimate of the time stamp size.
In case of the TSAClientBouncyCastle implementation of TSAClient, e.g., you can make it return arbitrary estimates instead of the default 4096 if you use the constructor with four arguments:
public TSAClientBouncyCastle(String url, String username, String password, int tokSzEstimate)
Some background: When integrating a signature or a document time stamp into a PDF, you first prepare the PDF with a gap of a given size, then calculate the hash of everything but that gap, then sign or time stamp that hash, and finally integrate the generated signature or time stamp into that gap, e.g.
(That /ByteRange entry is part of the signed content. Thus, one cannot enlarge the gap afterwards.)
Therefore, you have to use some estimate of the size of the generated signature or time stamp before its generation.
In case of document time stamps, this estimate is provided by the TSAClient method getTokenSizeEstimate.
PS: For still more details cf. this answer, the Adobe document Digital Signatures in a PDF (from which I copied the figure above), and the PDF specification ISO 32000-1:2008 made available here by Adobe.
If you are not using TSAClientBouncyCastle and you created your own TSAClient you must set the preferred size of signature, creating a org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions class, then set something like signatureOptions.setPreferredSignatureSize(8192*2)