Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions src/main/GetOcspResp.java
Original file line number Diff line number Diff line change
@@ -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;

}
}
111 changes: 69 additions & 42 deletions src/main/SignAndTimeStamp.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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<CRL> crlList = new ArrayList<CRL>();

//Fill certificate byte and CRLS
for(int i =0;i<certificateChain.length;i++){
certs[i] = certificateChain[i].getEncoded();
if(i==certificateChain.length-1) {break;}
crlList.addAll(new DssHelper().readCRLsFromCert((X509Certificate) certificateChain[i]));
}

//Loop getting All CRLS
byte[][] crls = new byte[crlList.size()][];
for (int i = 0 ; i < crlList.size();i++) {
crls[i] = ( (X509CRL) crlList.get(i)).getEncoded();
}

Iterable<byte[]> 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);

Expand All @@ -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<CRL> crlList = new DssHelper().readCRLsFromCert((X509Certificate) certificate);
CertificateList[] certRevList = new CertificateList[crlList.size()];

for(int i=0; i<crlList.size(); i++) {
X509CRL crl = (X509CRL) crlList.get(0);
X509CRLHolder crlHolder = new X509CRLHolder(crl.getEncoded());
certRevList[i] = crlHolder.toASN1Structure();
}

List<OCSPResponse> ocspList = new ArrayList<OCSPResponse>();
for (int i=0; i<certificateChain.length; i++) {
X509Certificate certTemp = (X509Certificate) certificateChain[i];
if (!certTemp.getIssuerDN().equals(certTemp.getSubjectDN())) {

X509Certificate issuerCert = (X509Certificate) certificateChain[i+1];
if(issuerCert == null) {
issuerCert = new GetOcspResp().getIssuerCert(certTemp);
}
OCSPResp ocspResp = new GetOcspResp().getOcspResp(certTemp, issuerCert);
if (ocspResp != null) {
ocspList.add(OCSPResponse.getInstance(ocspResp.getEncoded()));
}
}
}

OCSPResponse[] ocsps = new OCSPResponse[ocspList.size()];
for(int i=0; i<ocspList.size(); i++) {
ocsps[i] = ocspList.get(i);
}


RevocationValues revValues = new RevocationValues(certRevList, ocsps, null);

signedAttributes.add(new Attribute(new ASN1ObjectIdentifier("1.2.840.113583.1.1.8"), new DERSet(revValues)));

// =========================== For LTV Enable ===========================

AttributeTable signedAttributesTable = new AttributeTable(signedAttributes);
signedAttributesTable.toASN1EncodableVector();
DefaultSignedAttributeTableGenerator signedAttributeGenerator = new DefaultSignedAttributeTableGenerator(
signedAttributesTable);

SignerInfoGeneratorBuilder signerInfoBuilder = new SignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().build());
signerInfoBuilder.setSignedAttributeGenerator(signedAttributeGenerator);

SignerInfoGenerator signerInfoGen = signerInfoBuilder.build(sha512Signer, new X509CertificateHolder(cert));

CMSSignedDataGenerator gen = new CMSSignedDataGenerator();

gen.addSignerInfoGenerator(signerInfoGen);

gen.addCertificates(certStore);

CMSProcessableInputStream msg = new CMSProcessableInputStream(is);
CMSSignedData signedData = gen.generate(msg,false);

Expand Down
Loading