diff --git a/src/main/GetOcspResp.java b/src/main/GetOcspResp.java new file mode 100644 index 0000000..24855bc --- /dev/null +++ b/src/main/GetOcspResp.java @@ -0,0 +1,178 @@ +package main; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.Principal; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; + +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers; +import org.bouncycastle.asn1.x509.AccessDescription; +import org.bouncycastle.asn1.x509.AuthorityInformationAccess; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.Extensions; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.ocsp.CertificateID; +import org.bouncycastle.cert.ocsp.OCSPException; +import org.bouncycastle.cert.ocsp.OCSPReq; +import org.bouncycastle.cert.ocsp.OCSPReqBuilder; +import org.bouncycastle.cert.ocsp.OCSPResp; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.bc.BcDigestCalculatorProvider; + +public class GetOcspResp { + public OCSPResp getOcspResp(X509Certificate certificate, X509Certificate issuer) + throws MalformedURLException, IOException, CertificateEncodingException, OperatorCreationException, + OCSPException { + + Principal subjectX500Principal = certificate.getSubjectX500Principal(); + + String ocspUrl = getOCSPUrl(certificate); + if (ocspUrl == null) { + System.out.println("OCSP URL for '" + subjectX500Principal + "' is empty"); + return null; + } + + // Generate OCSP request + OCSPReq ocspReq = generateOcspRequest(issuer, certificate.getSerialNumber()); + + // Get OCSP response from server + OCSPResp ocspResp = requestOCSPResponse(ocspUrl, ocspReq); + + return ocspResp; + } + + private OCSPReq generateOcspRequest(X509Certificate issuerCert, BigInteger serialNumber) + throws OCSPException, CertificateEncodingException, OperatorCreationException, IOException { + + BcDigestCalculatorProvider util = new BcDigestCalculatorProvider(); + + // Generate the id for the certificate we are looking for + CertificateID id = new CertificateID(util.get(CertificateID.HASH_SHA1), + new X509CertificateHolder(issuerCert.getEncoded()), serialNumber); + OCSPReqBuilder ocspGen = new OCSPReqBuilder(); + + ocspGen.addRequest(id); + + BigInteger nonce = BigInteger.valueOf(System.currentTimeMillis()); + Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, true, + new DEROctetString(nonce.toByteArray())); + ocspGen.setRequestExtensions(new Extensions(new Extension[] { ext })); + + return ocspGen.build(); + } + + public OCSPResp requestOCSPResponse(String url, OCSPReq ocspReq) throws IOException, MalformedURLException { + byte[] bytes = ocspReq.getEncoded(); + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setRequestProperty("Content-Type", "application/ocsp-request"); + connection.setRequestProperty("Accept", "application/ocsp-response"); + connection.setDoOutput(true); + DataOutputStream outputStream = new DataOutputStream(new BufferedOutputStream(connection.getOutputStream())); + outputStream.write(bytes); + outputStream.flush(); + outputStream.close(); + if (connection.getResponseCode() != 200) { + // this.log.error("OCSP request has been failed (HTTP {}) - {}", + // connection.getResponseCode(), + // connection.getResponseMessage()); + System.out.println("OCSP request has been failed (HTTP {}) - {}" + connection.getResponseCode() + + connection.getResponseMessage()); + } + try (InputStream in = (InputStream) connection.getContent()) { + return new OCSPResp(in); + } + } + + @SuppressWarnings("resource") + private String getOCSPUrl(X509Certificate certificate) throws IOException { + byte[] obj; + obj = certificate.getExtensionValue(Extension.authorityInfoAccess.getId()); + + if (obj == null) { + return null; + } + + AuthorityInformationAccess authorityInformationAccess; + DEROctetString oct = (DEROctetString) (new ASN1InputStream(new ByteArrayInputStream(obj)).readObject()); + authorityInformationAccess = AuthorityInformationAccess + .getInstance(new ASN1InputStream(oct.getOctets()).readObject()); + + AccessDescription[] accessDescriptions = authorityInformationAccess.getAccessDescriptions(); + for (AccessDescription accessDescription : accessDescriptions) { + boolean correctAccessMethod = accessDescription.getAccessMethod() + .equals(X509ObjectIdentifiers.ocspAccessMethod); + if (!correctAccessMethod) { + continue; + } + + GeneralName name = accessDescription.getAccessLocation(); + if (name.getTagNo() != GeneralName.uniformResourceIdentifier) { + continue; + } + + DERIA5String derStr = DERIA5String.getInstance((ASN1TaggedObject) name.toASN1Primitive(), false); + return derStr.getString(); + } + + return null; + + } + + @SuppressWarnings("resource") X509Certificate getIssuerCert(X509Certificate certificate) throws IOException { + byte[] obj; + obj = certificate.getExtensionValue(Extension.authorityInfoAccess.getId()); + + if (obj == null) { + return null; + } + + AuthorityInformationAccess authorityInformationAccess; + DEROctetString oct = (DEROctetString) (new ASN1InputStream(new ByteArrayInputStream(obj)).readObject()); + authorityInformationAccess = AuthorityInformationAccess + .getInstance(new ASN1InputStream(oct.getOctets()).readObject()); + + AccessDescription[] accessDescriptions = authorityInformationAccess.getAccessDescriptions(); + for (AccessDescription accessDescription : accessDescriptions) { + if (accessDescription.getAccessMethod().equals(X509ObjectIdentifiers.id_ad_caIssuers)) { + GeneralName location = accessDescription.getAccessLocation(); + if (location.getTagNo() == GeneralName.uniformResourceIdentifier) { + String issuerUrl = location.getName().toString(); + // http URL to issuer (test in your browser to see if it's a valid certificate) + // you can use java.net.URL.openStream() to create a InputStream and create + // the certificate with your CertificateFactory + URL url = new URL(issuerUrl); + CertificateFactory certificateFactory; + X509Certificate issuer = null; + try { + certificateFactory = CertificateFactory.getInstance("X.509"); + issuer = (X509Certificate) certificateFactory.generateCertificate(url.openStream()); + + } catch (CertificateException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return issuer; + } + } + } + + return null; + + } +} diff --git a/src/main/SignAndTimeStamp.java b/src/main/SignAndTimeStamp.java index 38708d4..7cfc97d 100644 --- a/src/main/SignAndTimeStamp.java +++ b/src/main/SignAndTimeStamp.java @@ -33,7 +33,6 @@ import java.util.List; import org.apache.pdfbox.cos.COSDictionary; -import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface; @@ -46,23 +45,29 @@ import org.bouncycastle.asn1.cms.Attribute; import org.bouncycastle.asn1.cms.AttributeTable; import org.bouncycastle.asn1.cms.Attributes; +import org.bouncycastle.asn1.cms.CMSAttributes; +import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; +import org.bouncycastle.asn1.ocsp.OCSPResponse; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.x509.CertificateList; import org.bouncycastle.cert.X509CRLHolder; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaCertStore; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.ocsp.OCSPResp; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.CMSSignedDataGenerator; +import org.bouncycastle.cms.DefaultSignedAttributeTableGenerator; +import org.bouncycastle.cms.SignerInfoGenerator; +import org.bouncycastle.cms.SignerInfoGeneratorBuilder; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.SignerInformationStore; -import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.tsp.TSPException; import org.bouncycastle.util.Store; -import main.util.X509Util; +import main.util.RevocationValues; /** * The SignAndTimeStamp class is used to sign PDF(.pdf) with TSA @@ -90,38 +95,8 @@ boolean signPdf(File pdfFile, File signedPdfFile) throws IOException { COSDictionary catalogDict = doc.getDocumentCatalog().getCOSObject(); catalogDict.setNeedToBeUpdated(true); - // =========================== For LTV Enable =========================== - - //Sorted Certificate 0 = E Entity , 1 = intermediate , 2 = root - Certificate[] sortedCertificateChain = X509Util.SortX509Chain(certificateChain,certificate); - certificateChain = sortedCertificateChain; - - //Assign byte array for storing certificate in DSS Store. - byte[][] certs = new byte[certificateChain.length][]; - - //Assign byte array for storing certificate in DSS Store. - List crlList = new ArrayList(); - - //Fill certificate byte and CRLS - for(int i =0;i certifiates = Arrays.asList(certs); - COSDictionary dss = new DssHelper().createDssDictionary(certifiates,Arrays.asList(crls) , null); - catalogDict.setItem(COSName.getPDFName("DSS"), dss); - - // =========================== For LTV Enable =========================== */ - - // For big certificate chain + // For add CRL, OCSP and timestamp token : SignatureOptions.DEFAULT_SIGNATURE_SIZE * 2 + // Add more for big chain certificate SignatureOptions signatureOptions = new SignatureOptions(); signatureOptions.setPreferredSignatureSize(SignatureOptions.DEFAULT_SIGNATURE_SIZE * 2); @@ -147,16 +122,68 @@ public byte[] sign(InputStream is) throws IOException { @SuppressWarnings("rawtypes") Store certStore = new JcaCertStore(certList); - CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); + ContentSigner sha512Signer = new JcaContentSignerBuilder("SHA256WithRSA").build(privateKey); org.bouncycastle.asn1.x509.Certificate cert = org.bouncycastle.asn1.x509.Certificate .getInstance(ASN1Primitive.fromByteArray(certificate.getEncoded())); - ContentSigner sha512Signer = new JcaContentSignerBuilder("SHA256WithRSA").build(privateKey); - gen.addSignerInfoGenerator( - new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()) - .build(sha512Signer, new X509CertificateHolder(cert))); + ASN1EncodableVector signedAttributes = new ASN1EncodableVector(); + signedAttributes.add(new Attribute(CMSAttributes.contentType, new DERSet(CMSObjectIdentifiers.data))); + + // =========================== For LTV Enable =========================== + List crlList = new DssHelper().readCRLsFromCert((X509Certificate) certificate); + CertificateList[] certRevList = new CertificateList[crlList.size()]; + + for(int i=0; i ocspList = new ArrayList(); + for (int i=0; i + * RevocationValues ::= SEQUENCE { + * crlVals [0] SEQUENCE OF CertificateList OPTIONAL, + * ocspVals [1] SEQUENCE OF BasicOCSPResponse OPTIONAL, + * otherRevVals [2] OtherRevVals OPTIONAL} + * + */ +public class RevocationValues + extends ASN1Object +{ + + private ASN1Sequence crlVals; + private ASN1Sequence ocspVals; + private OtherRevVals otherRevVals; + + public RevocationValues(CertificateList[] crlVals, + OCSPResponse[] ocspVals, OtherRevVals otherRevVals) + { + if (null != crlVals) + { + this.crlVals = new DERSequence(crlVals); + } + if (null != ocspVals) + { + this.ocspVals = new DERSequence(ocspVals); + } + this.otherRevVals = otherRevVals; + } + + public CertificateList[] getCrlVals() + { + if (null == this.crlVals) + { + return new CertificateList[0]; + } + CertificateList[] result = new CertificateList[this.crlVals.size()]; + for (int idx = 0; idx < result.length; idx++) + { + result[idx] = CertificateList.getInstance(this.crlVals + .getObjectAt(idx)); + } + return result; + } + + public OCSPResponse[] getOcspVals() + { + if (null == this.ocspVals) + { + return new OCSPResponse[0]; + } + OCSPResponse[] result = new OCSPResponse[this.ocspVals.size()]; + for (int idx = 0; idx < result.length; idx++) + { + result[idx] = OCSPResponse.getInstance(this.ocspVals + .getObjectAt(idx)); + } + return result; + } + + public OtherRevVals getOtherRevVals() + { + return this.otherRevVals; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + if (null != this.crlVals) + { + v.add(new DERTaggedObject(true, 0, this.crlVals)); + } + if (null != this.ocspVals) + { + v.add(new DERTaggedObject(true, 1, this.ocspVals)); + } + if (null != this.otherRevVals) + { + v.add(new DERTaggedObject(true, 2, this.otherRevVals.toASN1Primitive())); + } + return new DERSequence(v); + } +} diff --git a/src/sample/TestSignAndStamp.java b/src/sample/TestSignAndStamp.java index 40613f3..0e93cf8 100644 --- a/src/sample/TestSignAndStamp.java +++ b/src/sample/TestSignAndStamp.java @@ -11,26 +11,26 @@ public static void main(String[] args) throws SignatureException, IOException, G /**** Sample Input ****/ - /*String passwordP12 = "password"; - String inputFileP12 = "cert_sign_01.p12" || "PKCS11Config.txt"; + String passwordP12 = "123456"; + String inputFileP12 = "itsaya.p12"; String inputFileName = "pdfA3.pdf"; String outputFile = "tsa_signed.pdf"; String filePath = "resources/"; - String tsaUrl = "https://time-test.teda.th"; - String keystorePath = "KeystorePath"; - String keystorePassword = "KeystorePassword"; - String keystoreType = "PKCS12" || "PKCS11"; - */ + String tsaUrl = ""; + String keystorePath = ""; + String keystorePassword = ""; + String keystoreType = "PKCS12"; - String passwordP12 = args[0]; - String inputFileP12 = args[1]; - String inputFileName = args[2]; - String outputFile = args[3]; - String filePath = args[4]; - String tsaUrl = args[5]; - String keystorePath = args[6]; - String keystorePassword = args[7]; - String keystoreType = args[8]; + +// String passwordP12 = args[0]; +// String inputFileP12 = args[1]; +// String inputFileName = args[2]; +// String outputFile = args[3]; +// String filePath = args[4]; +// String tsaUrl = args[5]; +// String keystorePath = args[6]; +// String keystorePassword = args[7]; +// String keystoreType = args[8]; SignAndTimeStamp.signWithTSA(passwordP12, inputFileP12, inputFileName, outputFile,