I am working on a solution that allows signing using p12 certificate on a remote server.
First, I have the digest of the document which is calculated on a server and then I send it for signature on another server.
Here are the PDF files, you will find two PDF versions. The "CURRENT_SIGNATURE.pdf" file is the result I got using the code below. And the "TARGER_SIGNATUREPDF.pdf" is the target I want to have. As you can see the difference the TARGET file says "certificate revocation list embedded in signature." on the other hand the CURRENT indicates that "the list of revocation of the certificates incorporated in the document." In addition, the TARGET file there is only one signature and no revision added : https://www.grosfichiers.com/i4fmqCz43is
Result vérification :
My goal now is to add a LTV verification, knowing that I am signing on the server part using: PadesCMSSignedDataBuilder
********************* ON SERVER A **********************
public class ServerA {
private static PAdESSignatureParameters signatureParameters;
private static DSSDocument documentToSign;
public static ExternalCMSPAdESService service;
private static final String TSA_URL = "http://dss.nowina.lu/pki-factory/tsa/good-tsa";
public static void main(String[] args) throws Exception {
documentToSign = new FileDocument(new File("Doc 2.pdf"));
signatureParameters = new PAdESSignatureParameters();
signatureParameters.setSignatureLevel(SignatureLevel.PAdES_BASELINE_B);
signatureParameters.setLocation("Luxembourg");
signatureParameters.setReason("DSS testing");
signatureParameters.setContactInfo("Jira");
signatureParameters.setGenerateTBSWithoutCertificate(true);
CommonCertificateVerifier commonCertificateVerifier = new CommonCertificateVerifier();
commonCertificateVerifier.setCrlSource(new OnlineCRLSource());
commonCertificateVerifier.setOcspSource(new OnlineOCSPSource());
commonCertificateVerifier.setCheckRevocationForUntrustedChains(true);
service = new ExternalCMSPAdESService(commonCertificateVerifier);
byte[] documentDigest = computeDocumentDigest(documentToSign, signatureParameters);
// Embedded CAdES is generated by a third party
byte[] cmsSignedData = ServerB.getSignedCMSignedData(documentDigest);
service.setCmsSignedData(cmsSignedData);
DSSDocument finalDoc = service.signDocument(documentToSign, signatureParameters, null);
PAdESService service = new PAdESService(commonCertificateVerifier);
TimestampDataLoader timestampDataLoader = new TimestampDataLoader();// uses the specific content-type
OnlineTSPSource tsa1 = new OnlineTSPSource("http://dss.nowina.lu/pki-factory/tsa/ee-good-tsa");
tsa1.setDataLoader(timestampDataLoader);
service.setTspSource(tsa1);
PAdESSignatureParameters extensionParameters = new PAdESSignatureParameters();
extensionParameters.setSignatureLevel(SignatureLevel.PAdES_BASELINE_LT);
DSSDocument extendedDocument = service.extendDocument(finalDoc, extensionParameters);
save(finalDoc);
save2(extendedDocument);
}
private static void save(DSSDocument signedDocument) {
try (FileOutputStream fos = new FileOutputStream("DSS.pdf")) {
Utils.copy(signedDocument.openStream(), fos);
} catch (Exception e) {
Alert alert = new Alert(Alert.AlertType.ERROR, "Unable to save file : " + e.getMessage(), ButtonType.CLOSE);
alert.showAndWait();
return;
}
}
private static void save2(DSSDocument signedDocument) {
try (FileOutputStream fos = new FileOutputStream("DSS-2.pdf")) {
Utils.copy(signedDocument.openStream(), fos);
} catch (Exception e) {
Alert alert = new Alert(Alert.AlertType.ERROR, "Unable to save file : " + e.getMessage(), ButtonType.CLOSE);
alert.showAndWait();
return;
}
}
public static CertificateVerifier getOfflineCertificateVerifier() {
CertificateVerifier cv = new CommonCertificateVerifier();
cv.setDataLoader(new IgnoreDataLoader());
return cv;
}
protected static byte[] computeDocumentDigest(final DSSDocument toSignDocument, final PAdESSignatureParameters parameters) {
IPdfObjFactory pdfObjFactory = new ServiceLoaderPdfObjFactory();
final PDFSignatureService pdfSignatureService = pdfObjFactory.newPAdESSignatureService();
return pdfSignatureService.digest(toSignDocument, parameters);
}
private static class ExternalCMSPAdESService extends PAdESService {
private static final long serialVersionUID = -2003453716888412577L;
private byte[] cmsSignedData;
public ExternalCMSPAdESService(CertificateVerifier certificateVerifier) {
super(certificateVerifier);
}
@Override
protected byte[] generateCMSSignedData(final DSSDocument toSignDocument, final PAdESSignatureParameters parameters,
final SignatureValue signatureValue) {
if (this.cmsSignedData == null) {
throw new NullPointerException("A CMS signed data must be provided");
}
return this.cmsSignedData;
}
public void setCmsSignedData(final byte[] cmsSignedData) {
this.cmsSignedData = cmsSignedData;
}
}
}
And to be able to sign the calculated hash :
********************* ON SERVER B **********************
public class ServerB {
private static PAdESSignatureParameters signatureParameters;
private static DSSDocument documentToSign;
public static ExternalCMSPAdESService service;
/**
* Computes a CAdES with specific things for PAdES
*/
public static byte[] getSignedCMSignedData(byte[] documentDigest) throws Exception {
signatureParameters = new PAdESSignatureParameters();
signatureParameters.setSigningCertificate(getSigningCert());
signatureParameters.setCertificateChain(getCertificateChain());
signatureParameters.setSignatureLevel(SignatureLevel.PAdES_BASELINE_B);
signatureParameters.setLocation("Luxembourg");
signatureParameters.setReason("DSS testing");
signatureParameters.setContactInfo("Jira");
CMSProcessableByteArray content = new CMSProcessableByteArray(documentDigest);
PadesCMSSignedDataBuilder padesCMSSignedDataBuilder = new PadesCMSSignedDataBuilder(getOfflineCertificateVerifier());
SignatureAlgorithm signatureAlgorithm = signatureParameters.getSignatureAlgorithm();
CustomContentSigner customContentSigner = new CustomContentSigner(signatureAlgorithm.getJCEId());
SignerInfoGeneratorBuilder signerInfoGeneratorBuilder = padesCMSSignedDataBuilder.getSignerInfoGeneratorBuilder(signatureParameters, documentDigest);
CMSSignedDataGenerator generator = padesCMSSignedDataBuilder.createCMSSignedDataGenerator(signatureParameters, customContentSigner,
signerInfoGeneratorBuilder, null);
CMSUtils.generateDetachedCMSSignedData(generator, content);
SignatureTokenConnection signingToken = new Pkcs12SignatureToken("certificate.p12",
new KeyStore.PasswordProtection("123456".toCharArray()));
DSSPrivateKeyEntry privateKey = getKey("certificate.p12","123456");
SignatureValue signatureValue = signingToken.sign(new ToBeSigned(customContentSigner.getOutputStream().toByteArray()),
signatureParameters.getDigestAlgorithm(), privateKey);
customContentSigner = new CustomContentSigner(signatureAlgorithm.getJCEId(), signatureValue.getValue());
generator = padesCMSSignedDataBuilder.createCMSSignedDataGenerator(signatureParameters, customContentSigner, signerInfoGeneratorBuilder, null);
CMSSignedData cmsSignedData = CMSUtils.generateDetachedCMSSignedData(generator, content);
return DSSASN1Utils.getDEREncoded(cmsSignedData);
}
public static CertificateVerifier getOfflineCertificateVerifier() {
CertificateVerifier cv = new CommonCertificateVerifier();
cv.setDataLoader(new IgnoreDataLoader());
return cv;
}
public static List<CertificateToken> getCertificateChain() throws Exception {
List<CertificateToken> list = new ArrayList<>();
CertificateToken[] l = getKey("certificate.p12","123456").getCertificateChain();
for (int i = 0; i < l.length; i++) {
list.add(l[i]);
}
return list;
}
public static CertificateToken getSigningCert() throws Exception {
return getKey("certificate.p12","123456").getCertificate();
}
public static DSSPrivateKeyEntry getKey(String certificate, String pin) throws Exception {
try (Pkcs12SignatureToken signatureToken = new Pkcs12SignatureToken("certificate.p12",
new KeyStore.PasswordProtection("123456".toCharArray()))) {
List<DSSPrivateKeyEntry> keys = signatureToken.getKeys();
KSPrivateKeyEntry dssPrivateKeyEntry = (KSPrivateKeyEntry) keys.get(0);
DSSPrivateKeyEntry entry = signatureToken.getKey(dssPrivateKeyEntry.getAlias(),
new KeyStore.PasswordProtection("123456".toCharArray()));
return entry;
}
}
private static class ExternalCMSPAdESService extends PAdESService {
private static final long serialVersionUID = -2003453716888412577L;
private byte[] cmsSignedData;
public ExternalCMSPAdESService(CertificateVerifier certificateVerifier) {
super(certificateVerifier);
}
@Override
protected byte[] generateCMSSignedData(final DSSDocument toSignDocument, final PAdESSignatureParameters parameters,
final SignatureValue signatureValue) {
if (this.cmsSignedData == null) {
throw new NullPointerException("A CMS signed data must be provided");
}
return this.cmsSignedData;
}
public void setCmsSignedData(final byte[] cmsSignedData) {
this.cmsSignedData = cmsSignedData;
}
}
}