Index: examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java
===================================================================
--- examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java (revision 1558346)
+++ examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java (working copy)
@@ -33,6 +33,7 @@
import java.security.cert.CertificateException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Enumeration;
@@ -44,232 +45,338 @@
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.TSACreator;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.cms.Attribute;
+import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.asn1.cms.Attributes;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSSignedGenerator;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.OperatorCreationException;
/**
- *
This is an example for singing a pdf with bouncy castle.
- * A keystore can be created with the java keytool
- * (e.g. keytool -genkeypair -storepass 123456 -storetype pkcs12 -alias test -validity 365 -v -keyalg RSA -keystore keystore.p12 )
+ *
+ * This is an example for singing a pdf with bouncy castle.
*
+ *
+ * A keystore can be created with the java keytool (e.g. keytool -genkeypair -storepass 123456 -storetype pkcs12 -alias
+ * test -validity 365 -v -keyalg RSA -keystore keystore.p12 )
+ *
*
- * @author Thomas Chojecki
+ * @author Thomas Chojecki and Vakhtang Koroghlishvili (Gogebashvili)
*
*/
public class CreateSignature implements SignatureInterface
{
- private static BouncyCastleProvider provider = new BouncyCastleProvider();
+ private static BouncyCastleProvider provider = new BouncyCastleProvider();
- private PrivateKey privKey;
+ private PrivateKey privKey;
- private Certificate[] cert;
+ private Certificate[] cert;
- private SignatureOptions options;
-
- /**
- * Initialize the signature creator with a keystore (pkcs12) and pin that
- * should be used for the signature.
- *
- * @param keystore
- * is a pkcs12 keystore.
- * @param pin
- * is the pin for the keystore / private key
- */
- public CreateSignature(KeyStore keystore, char[] pin)
- {
- try
+ private SignatureOptions options;
+
+ private TSACreator tsaCreator;
+
+ public static final String SHA_256_IDENTIFIER = "2.16.840.1.101.3.4.2.1";
+
+ /**
+ * Initialize the signature creator with a keystore (pkcs12) and pin that should be used for the signature.
+ *
+ * @param keystore
+ * is a pkcs12 keystore.
+ * @param pin
+ * is the pin for the keystore / private key
+ */
+ public CreateSignature(KeyStore keystore, char[] pin)
{
- /*
- * grabs the first alias from the keystore and get the private key. An
- * alternative method or constructor could be used for setting a specific
- * alias that should be used.
- */
- Enumeration aliases = keystore.aliases();
- String alias = null;
- if (aliases.hasMoreElements())
- {
- alias = aliases.nextElement();
- }
- else
- {
- throw new RuntimeException("Could not find alias");
- }
- privKey = (PrivateKey) keystore.getKey(alias, pin);
- cert = keystore.getCertificateChain(alias);
+ try
+ {
+ /*
+ * grabs the first alias from the keystore and get the private key. An alternative method or constructor
+ * could be used for setting a specific alias that should be used.
+ */
+ Enumeration aliases = keystore.aliases();
+ String alias = null;
+ if (aliases.hasMoreElements())
+ {
+ alias = aliases.nextElement();
+ }
+ else
+ {
+ throw new RuntimeException("Could not find alias");
+ }
+ privKey = (PrivateKey) keystore.getKey(alias, pin);
+ cert = keystore.getCertificateChain(alias);
+ }
+ catch (KeyStoreException e)
+ {
+ e.printStackTrace();
+ }
+ catch (UnrecoverableKeyException e)
+ {
+ System.err.println("Could not extract private key.");
+ e.printStackTrace();
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ System.err.println("Unknown algorithm.");
+ e.printStackTrace();
+ }
}
- catch (KeyStoreException e)
+
+ /**
+ * Signs the given pdf file.
+ *
+ * @param document
+ * is the pdf document
+ * @param tsaCreator
+ * is for tsa time
+ * @return the signed pdf document
+ * @throws IOException
+ * @throws COSVisitorException
+ * @throws SignatureException
+ */
+ public File signPDF(File document, TSACreator tsaCreator) throws IOException, COSVisitorException,
+ SignatureException
{
- e.printStackTrace();
+
+ if (tsaCreator != null)
+ {
+ this.tsaCreator = tsaCreator;
+ }
+ byte[] buffer = new byte[8 * 1024];
+ if (document == null || !document.exists())
+ {
+ new RuntimeException("Document for signing does not exist");
+ }
+
+ // creating output document and prepare the IO streams.
+ String name = document.getName();
+ String substring = name.substring(0, name.lastIndexOf("."));
+
+ File outputDocument = new File(document.getParent(), substring + "_signed.pdf");
+ FileInputStream fis = new FileInputStream(document);
+ FileOutputStream fos = new FileOutputStream(outputDocument);
+
+ int c;
+ while ((c = fis.read(buffer)) != -1)
+ {
+ fos.write(buffer, 0, c);
+ }
+ fis.close();
+ fis = new FileInputStream(outputDocument);
+
+ // load document
+ PDDocument doc = PDDocument.load(document);
+
+ // create signature dictionary
+ PDSignature signature = new PDSignature();
+ signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); // default filter
+ // subfilter for basic and PAdES Part 2 signatures
+ signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
+ signature.setName("signer name");
+ signature.setLocation("signer location");
+ signature.setReason("reason for signature");
+
+ // the signing date, needed for valid signature
+ signature.setSignDate(Calendar.getInstance());
+
+ // register signature dictionary and sign interface
+ if (options == null)
+ {
+ doc.addSignature(signature, this);
+ }
+ else
+ {
+ doc.addSignature(signature, this, options);
+ }
+
+ // write incremental (only for signing purpose)
+ doc.saveIncremental(fis, fos);
+
+ return outputDocument;
}
- catch (UnrecoverableKeyException e)
+
+ /**
+ *
+ * SignatureInterface implementation.
+ *
+ *
+ *
+ * This method will be called from inside of the pdfbox and create the pkcs7 signature. The given InputStream
+ * contains the bytes that are providen by the byte range.
+ *
+ *
+ *
+ * This method is for internal use only.
+ *
+ *
+ *
+ * Here the user should use his favorite cryptographic library and implement a pkcs7 signature creation.
+ *
+ */
+ public byte[] sign(InputStream content) throws SignatureException, IOException
{
- System.err.println("Could not extract private key.");
- e.printStackTrace();
+ CMSProcessableInputStream input = new CMSProcessableInputStream(content);
+ CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
+ // CertificateChain
+ List certList = Arrays.asList(cert);
+
+ CertStore certStore = null;
+ try
+ {
+ certStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList), provider);
+ gen.addSigner(privKey, (X509Certificate) certList.get(0), CMSSignedGenerator.DIGEST_SHA256);
+ gen.addCertificatesAndCRLs(certStore);
+ CMSSignedData signedData = gen.generate(input, false, provider);
+
+ CMSSignedData data = signedData;
+
+ if (tsaCreator != null)
+ {
+ data = extendSignedData(signedData, content);
+ }
+
+ return data.getEncoded();
+
+ }
+ catch (Exception e)
+ {
+ // should be handled
+ System.err.println("Error while creating pkcs7 signature.");
+ e.printStackTrace();
+ }
+ throw new RuntimeException("Problem while preparing signature");
}
- catch (NoSuchAlgorithmException e)
- {
- System.err.println("Unknown algorithm.");
- e.printStackTrace();
- }
- }
-
-
- /**
- * Signs the given pdf file.
- *
- * @param document is the pdf document
- * @return the signed pdf document
- * @throws IOException
- * @throws COSVisitorException
- * @throws SignatureException
- */
- public File signPDF(File document) throws IOException, COSVisitorException,
- SignatureException
- {
- byte[] buffer = new byte[8 * 1024];
- if (document == null || !document.exists())
+ /**
+ * We just extend CMS signed Data
+ *
+ * @param signedData -Generated CMS signed data
+ * @param content - Content of document
+ * @return CMSSignedData - Extended CMS signed data
+ * @throws NoSuchAlgorithmException - When algorithm is not supported
+ * @author vakhtang koroghlishvili
+ */
+ private CMSSignedData extendSignedData(CMSSignedData signedData, InputStream content)
+ throws NoSuchAlgorithmException
{
- new RuntimeException("Document for signing does not exist");
- }
- // creating output document and prepare the IO streams.
- String name = document.getName();
- String substring = name.substring(0, name.lastIndexOf("."));
-
- File outputDocument = new File(document.getParent(), substring+"_signed.pdf");
- FileInputStream fis = new FileInputStream(document);
- FileOutputStream fos = new FileOutputStream(outputDocument);
+ SignerInformationStore signerInformationStore = signedData.getSignerInfos();
+ ArrayList signerInformationArray = new ArrayList();
- int c;
- while ((c = fis.read(buffer)) != -1)
- {
- fos.write(buffer, 0, c);
- }
- fis.close();
- fis = new FileInputStream(outputDocument);
+ Object[] informations = signerInformationStore.getSigners().toArray();
- // load document
- PDDocument doc = PDDocument.load(document);
+ for (Object info : informations)
+ {
+ SignerInformation signerInformation = (SignerInformation) info;
- // create signature dictionary
- PDSignature signature = new PDSignature();
- signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); // default filter
- // subfilter for basic and PAdES Part 2 signatures
- signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
- signature.setName("signer name");
- signature.setLocation("signer location");
- signature.setReason("reason for signature");
+ SignerInformation inf = reNewUnsignedAttributes(signedData, signerInformation, content);
- // the signing date, needed for valid signature
- signature.setSignDate(Calendar.getInstance());
+ if (inf == null)
+ {
+ signerInformationArray.add(signerInformation);
+ }
+ else
+ {
+ signerInformationArray.add(inf);
+ }
- // register signature dictionary and sign interface
- if (options==null)
- {
- doc.addSignature(signature, this);
- }
- else
- {
- doc.addSignature(signature, this, options);
+ }
+
+ SignerInformationStore newSignerStore = new SignerInformationStore(signerInformationArray);
+ return CMSSignedData.replaceSigners(signedData, newSignerStore);
+
}
-
- // write incremental (only for signing purpose)
- doc.saveIncremental(fis, fos);
- return outputDocument;
- }
+ /**
+ * We are extending CMS Signature
+ *
+ * @param signedData - CMS signed data
+ * @param signerInformation - information about signer
+ * @param content - content of document
+ * @return Information about SignerInformation
+ *
+ * @author vakhtang koroghlishvili
+ */
+ protected SignerInformation reNewUnsignedAttributes(CMSSignedData signedData, SignerInformation signerInformation,
+ InputStream content)
+ {
- /**
- *
- * SignatureInterface implementation.
- *
- *
- *
- * This method will be called from inside of the pdfbox and create the pkcs7
- * signature. The given InputStream contains the bytes that are providen by
- * the byte range.
- *
- *
- *
- * This method is for internal use only.
- *
- *
- *
- * Here the user should use his favorite cryptographic library and implement a
- * pkcs7 signature creation.
- *
- */
- public byte[] sign(InputStream content) throws SignatureException,
- IOException
- {
- CMSProcessableInputStream input = new CMSProcessableInputStream(content);
- CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
- // CertificateChain
- List certList = Arrays.asList(cert);
+ AttributeTable unsignedTable = signerInformation.getUnsignedAttributes();
- CertStore certStore = null;
- try
- {
- certStore = CertStore.getInstance("Collection",
- new CollectionCertStoreParameters(certList), provider);
- gen.addSigner(privKey, (X509Certificate) certList.get(0),
- CMSSignedGenerator.DIGEST_SHA256);
- gen.addCertificatesAndCRLs(certStore);
- CMSSignedData signedData = gen.generate(input, false, provider);
- return signedData.getEncoded();
+ ASN1EncodableVector asn1EncodableVector = new ASN1EncodableVector();
+ if (unsignedTable != null)
+ {
+ asn1EncodableVector = signerInformation.getUnsignedAttributes().toASN1EncodableVector();
+ }
+
+ Attribute signatureTimeStamp;
+ try
+ {
+ signatureTimeStamp = this.tsaCreator.getTimeStampAttribute(
+ PKCSObjectIdentifiers.id_aa_signatureTimeStampToken, signerInformation.getSignature(), content);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ return null;
+ }
+ asn1EncodableVector.add(signatureTimeStamp);
+ Attributes atrs = new Attributes(asn1EncodableVector);
+ SignerInformation newSignerInformation = SignerInformation.replaceUnsignedAttributes(signerInformation,
+ new AttributeTable(atrs));
+ return newSignerInformation;
}
- catch (Exception e)
- {
- // should be handled
- System.err.println("Error while creating pkcs7 signature.");
- e.printStackTrace();
- }
- throw new RuntimeException("Problem while preparing signature");
- }
- public static void main(String[] args) throws KeyStoreException,
- NoSuchAlgorithmException, CertificateException, FileNotFoundException,
- IOException, COSVisitorException, SignatureException
- {
- if (args.length != 3)
+ public static void main(String[] args) throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
+ FileNotFoundException, IOException, COSVisitorException, SignatureException, OperatorCreationException
{
- usage();
- System.exit(1);
- }
- else
- {
- File ksFile = new File(args[0]);
- KeyStore keystore = KeyStore.getInstance("PKCS12", provider);
- char[] pin = args[1].toCharArray();
- keystore.load(new FileInputStream(ksFile), pin);
+ if (args.length != 3)
+ {
+ usage();
+ System.exit(1);
+ }
+ else
+ {
+ File ksFile = new File(args[0]);
+ KeyStore keystore = KeyStore.getInstance("PKCS12", provider);
+ char[] pin = args[1].toCharArray();
+ keystore.load(new FileInputStream(ksFile), pin);
- File document = new File(args[2]);
+ File document = new File(args[2]);
- CreateSignature signing = new CreateSignature(keystore, pin.clone());
- signing.signPDF(document);
+ CreateSignature signing = new CreateSignature(keystore, pin.clone());
+
+ String tsaUrl = "http://tsa.safecreative.org";
+ TSACreator tsaCreator = new TSACreator(tsaUrl, null, null, SHA_256_IDENTIFIER);
+
+ signing.signPDF(document, tsaCreator);
+ }
+
}
- }
-
- /**
- * This will print the usage for this program.
- */
- private static void usage()
- {
- System.err.println("Usage: java " + CreateSignature.class.getName()
- + " ");
- }
+ /**
+ * This will print the usage for this program.
+ */
+ private static void usage()
+ {
+ System.err.println("Usage: java " + CreateSignature.class.getName()
+ + " ");
+ }
}
/**
- * Wrap a InputStream into a CMSProcessable object for bouncy castle. It's an
- * alternative to the CMSProcessableByteArray.
+ * Wrap a InputStream into a CMSProcessable object for bouncy castle. It's an alternative to the
+ * CMSProcessableByteArray.
*
* @author Thomas Chojecki
*
@@ -277,27 +384,27 @@
class CMSProcessableInputStream implements CMSProcessable
{
- InputStream in;
+ InputStream in;
- public CMSProcessableInputStream(InputStream is)
- {
- in = is;
- }
+ public CMSProcessableInputStream(InputStream is)
+ {
+ in = is;
+ }
- public Object getContent()
- {
- return null;
- }
+ public Object getContent()
+ {
+ return null;
+ }
- public void write(OutputStream out) throws IOException, CMSException
- {
- // read the content only one time
- byte[] buffer = new byte[8 * 1024];
- int read;
- while ((read = in.read(buffer)) != -1)
+ public void write(OutputStream out) throws IOException, CMSException
{
- out.write(buffer, 0, read);
+ // read the content only one time
+ byte[] buffer = new byte[8 * 1024];
+ int read;
+ while ((read = in.read(buffer)) != -1)
+ {
+ out.write(buffer, 0, read);
+ }
+ in.close();
}
- in.close();
- }
}
Index: pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/TSACreator.java
===================================================================
--- pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/TSACreator.java (revision 0)
+++ pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/TSACreator.java (working copy)
@@ -0,0 +1,269 @@
+package org.apache.pdfbox.pdmodel.interactive.digitalsignature;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.pdfbox.io.IOUtils;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.cms.Attribute;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.tsp.TSPException;
+import org.bouncycastle.tsp.TimeStampRequest;
+import org.bouncycastle.tsp.TimeStampRequestGenerator;
+import org.bouncycastle.tsp.TimeStampResponse;
+import org.bouncycastle.tsp.TimeStampToken;
+
+/**
+ *
+ * @author Vakhtang koroghlishvili
+ *
+ */
+public class TSACreator
+{
+
+ private String hashIdentifier;
+ private URL url;
+ private String userName;
+ private String password;
+ private DigestCalculatorProvider digestCalculatorProvider;
+ private static final Logger LOG = Logger.getLogger(TSACreator.class.getName());
+ /**
+ *
+ * @param url- the address of TSA
+ * @param tsaUsername- user name of TSA
+ * @param tsaPassword- password of TSA
+ * @param hashIdentifier - algorithm identifier
+ * @throws OperatorCreationException - when we can't build DigestCalcProvBuilder
+ * @throws MalformedURLException - when URL have some problem
+ */
+ public TSACreator(String url, String tsaUsername, String tsaPassword, String hashIdentifier)
+ throws OperatorCreationException, MalformedURLException
+ {
+
+ LOG.info("Initializing varaibles in TSA client constructor");
+ this.setHashIdentifier(hashIdentifier);
+ URL tsaURL = new URL(url);
+ this.setUrl(tsaURL);
+ this.setUserName(tsaUsername);
+ this.setPassword(tsaPassword);
+ JcaDigestCalculatorProviderBuilder digestCalcProvBuilder = new JcaDigestCalculatorProviderBuilder()
+ .setProvider("BC");
+ this.setDigestCalculatorProvider(digestCalcProvBuilder.build());
+ }
+
+ /**
+ *
+ * @param oid- identifier of ASN1Object
+ * @param messageImprint- message imprint of document
+ * @param content- stream of content
+ * @return we return Attribute value
+ * @throws NoSuchAlgorithmException- when we can't
+ * work with your algorithm
+ */
+ public Attribute getTimeStampAttribute(ASN1ObjectIdentifier objectIdentifier, byte[] messageImprint,
+ InputStream content) throws NoSuchAlgorithmException
+ {
+
+ LOG.info("We are getting the time stamp attribute");
+ try
+ {
+
+ MessageDigest msgDig = MessageDigest.getInstance(getHashIdentifier(), new BouncyCastleProvider());
+ byte[] digesttoTimeStamp = msgDig.digest(messageImprint);
+
+ byte[] tokenBytes = null;
+ try
+ {
+ tokenBytes = getTimeStampToken(digesttoTimeStamp);
+ }
+ catch (TSPException e)
+ {
+ LOG.log(Level.SEVERE, "can't get Time Stump Token", e);
+ throw new RuntimeException("can't get Time Stump Token");
+ }
+
+ Attribute signatureTimeStamp = new Attribute(objectIdentifier, new DERSet(
+ TSAUtils.byteToASN1Objec(tokenBytes)));
+
+ return signatureTimeStamp;
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ *
+ * @param imprint is the the array of bytes for the
+ * resulting hash value.
+ * @return encoded is time stamp Token
+ * @throws IOException- if we can't get encoded request,
+ * or can't get TSA Request, or when we can't create
+ * TimeStumpResoponse
+ * @throws TSPException- When we can't create Time
+ * StampResponse or when we can't validate response
+ * - this happens when the request can not match
+ * this response.
+ */
+ public byte[] getTimeStampToken(byte[] imprint) throws IOException, TSPException
+ {
+ LOG.log(Level.INFO, "imprint value is:"+Arrays.toString(imprint));
+
+ LOG.log(Level.INFO, "we are getting time stamp token using imprint");
+ byte[] tsaResponse = null;
+
+ TimeStampRequestGenerator tsaGenerator = new TimeStampRequestGenerator();
+ tsaGenerator.setCertReq(true);
+
+ LOG.log(Level.INFO, "we are generating nonce value for more security");
+ BigInteger nonce = TSAUtils.generateNonce(0, 97359710);
+ LOG.log(Level.INFO, "nonce is"+nonce);
+
+ LOG.log(Level.INFO, "generate TSA request");
+ TimeStampRequest request = tsaGenerator.generate(new ASN1ObjectIdentifier(getHashIdentifier()), imprint, nonce);
+
+ byte[] requestData = request.getEncoded();
+ tsaResponse = getTSAResponse(requestData);
+
+ TimeStampResponse response = new TimeStampResponse(tsaResponse);
+ response.validate(request);
+
+ LOG.log(Level.INFO, "Successful validating");
+
+ TimeStampToken tsToken = response.getTimeStampToken();
+ if (tsToken != null)
+ {
+ LOG.log(Level.INFO, "We have already Time Stamp token! \n \nnonce is:" +tsToken.getTimeStampInfo().getNonce()
+ + "\nAlgorithm identifier: "+tsToken.getTimeStampInfo().getHashAlgorithm().getAlgorithm().getId()
+ + "\nGenerated Date:"+tsToken.getTimeStampInfo().getGenTime()
+ + "\nimprint value:"+Arrays.toString(imprint) + "\n");
+
+ }
+ else
+ {
+ LOG.log(Level.SEVERE, "OUR TSA token is NULL");
+ throw new RuntimeException("ERROR: OUR TSA token is NULL!");
+ }
+
+ byte[] encoded = tsToken.getEncoded();
+ return encoded;
+ }
+
+
+
+ /**
+ *
+ * @param request - encoded TimeStampRequest data
+ * @return response bytes - just response data
+ * @throws IOException - when input stream of TSA have problems
+ */
+ private byte[] getTSAResponse(byte[] request) throws IOException
+ {
+
+ URLConnection tsaConnection = prepareTSAConnection();
+ OutputStream outputStreamData = tsaConnection.getOutputStream();
+ outputStreamData.write(request);
+ outputStreamData.close();
+ InputStream is = tsaConnection.getInputStream();
+ byte[] respBytes = IOUtils.toByteArray(is);
+
+ return respBytes;
+ }
+
+ /**
+ * Prepare TSAConnection - sets request properties
+ *
+ * @return URLConnection - We return TSA URL connection
+ * @throws IOException- when we can't open TSA URL Connection
+ */
+ private URLConnection prepareTSAConnection() throws IOException
+ {
+
+ URLConnection tsaConn;
+ tsaConn = (URLConnection) getUrl().openConnection();
+ tsaConn.setUseCaches(true);
+ tsaConn.setAllowUserInteraction(true);
+ tsaConn.setDoOutput(true);
+ tsaConn.setDoInput(true);
+ tsaConn.setUseCaches(true);
+ tsaConn.setRequestProperty("Content-Transfer-Encoding", "binary");
+ tsaConn.setRequestProperty("Content-Type", "application/timestamp-query");
+
+ if (userName != null && password != null)
+ {
+ if (userName.length() != 0 && password.length() != 0)
+ {
+ tsaConn.setRequestProperty(userName, password);
+ }
+
+ }
+ return tsaConn;
+ }
+
+ public URL getUrl()
+ {
+ return url;
+ }
+
+ public void setUrl(URL url)
+ {
+ this.url = url;
+ }
+
+ public String getUserName()
+ {
+ return userName;
+ }
+
+ public void setUserName(String userName)
+ {
+ this.userName = userName;
+ }
+
+ public String getPassword()
+ {
+ return password;
+ }
+
+ public void setPassword(String password)
+ {
+ this.password = password;
+ }
+
+ public DigestCalculatorProvider getDigestCalculatorProvider()
+ {
+ return digestCalculatorProvider;
+ }
+
+ public void setDigestCalculatorProvider(DigestCalculatorProvider digestCalculatorProvider)
+ {
+ this.digestCalculatorProvider = digestCalculatorProvider;
+ }
+
+ public String getHashIdentifier()
+ {
+ return hashIdentifier;
+ }
+
+ public void setHashIdentifier(String hashIdentifier)
+ {
+ this.hashIdentifier = hashIdentifier;
+ }
+
+}
Index: pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/TSAUtils.java
===================================================================
--- pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/TSAUtils.java (revision 0)
+++ pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/TSAUtils.java (working copy)
@@ -0,0 +1,67 @@
+package org.apache.pdfbox.pdmodel.interactive.digitalsignature;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.Random;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1Primitive;
+
+/**
+ *
+ * @author vakhtang koroghlishvili
+ *
+ */
+public class TSAUtils
+{
+
+ /**
+ *
+ * @param data - time stamp token byte
+ * @return ASN1Object which is created by the
+ * ASN1InputStream of the time stamp token
+ * @throws IOException - if we can't cast
+ * ASN1Primitive to ASN1Object
+ */
+ public static ASN1Object byteToASN1Objec(byte[] data) throws IOException
+ {
+ ASN1InputStream in = null;
+ try
+ {
+ in = new ASN1InputStream(data);
+ ASN1Primitive asn1Primitive = in.readObject();
+ return (ASN1Object) asn1Primitive;
+ }
+ catch (ClassCastException e)
+ {
+ throw new IOException("can't cast to the ASN1Object object");
+ }
+
+ }
+
+ /**
+ * Generates salt for more security. Is does not only get number
+ * from the specified range- from minimum value to maximum- there
+ * we use current time and etc.
+ *
+ * @param min - minimum value is the additional value
+ * @param max - maximum value is the additional value
+ * @return
+ */
+ public static BigInteger generateNonce(int min, int max)
+ {
+
+ Random rand = new Random();
+
+ Integer randomNum = rand.nextInt((max - min) + 1) + min;
+
+ BigInteger nonce = new BigInteger(randomNum.toString());
+
+ Long timeLong = System.currentTimeMillis();
+ Integer timeInt = timeLong != null ? timeLong.intValue() : 761820123;
+
+ return nonce.multiply((new BigInteger(timeInt.toString())));
+ }
+
+}