Description
While trying to digitally sign document using filter as FILTER_ADOBE_PPKLITE and subfilter as SUBFILTER_ETSI_CADES_DETACHED. For ETSI_CADES_Detached, a signing attribute is needs to be added. I am fetching signed hash and certificates from CSC. But after adding signing attribute, it is making the document corrupt. Below is the screenshot for the reference . Seems like hash is getting changed.
Code snippet for reference:
PDDocument document = PDDocument.load(inputStream); outFile = File.createTempFile("signedFIle", ".pdf"); Certificate[] certificateChain = //retrieve certificate chain from CSC integration setCertificateChain(certificateChain); // sign FileOutputStream output = new FileOutputStream(outFile); IOUtils.copy(inputStream, output); // create signature dictionary PDSignature signature = new PDSignature(); int accessPermissions = SigUtils.getMDPPermission(document); if (accessPermissions == 1) { throw new IllegalStateException("No changes to the document are permitted due to DocMDP transform parameters dictionary"); } signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); signature.setSubFilter(PDSignature.SUBFILTER_ETSI_CADES_DETACHED); signature.setName("Test Name"); signature.setLocation("Bucharest, RO"); signature.setReason("PDFBox Signing"); signature.setSignDate(Calendar.getInstance()); Rectangle2D humanRect = new Rectangle2D.Float(location.getLeft(), location.getBottom(), location.getRight(), location.getTop()); PDRectangle rect = createSignatureRectangle(document, humanRect); SignatureOptions signatureOptions = new SignatureOptions(); signatureOptions.setVisualSignature(createVisualSignatureTemplate(document, 0, rect, signature)); signatureOptions.setPage(0); document.addSignature(signature, signatureOptions); ExternalSigningSupport externalSigning = document.saveIncrementalForExternalSigning(output); InputStream content = externalSigning.getContent(); CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); X509Certificate cert = (X509Certificate) certificateChain[0]; gen.addCertificates(new JcaCertStore(Arrays.asList(certificateChain))); MessageDigest digest = MessageDigest.getInstance("SHA-256"); // Use a buffer to read the input stream in chunks byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = content.read(buffer)) != -1) { digest.update(buffer, 0, bytesRead); } byte[] hashBytes = digest.digest(); ESSCertIDv2 certid = new ESSCertIDv2( new AlgorithmIdentifier(new ASN1ObjectIdentifier("*****")), MessageDigest.getInstance("SHA-256").digest(cert.getEncoded()) ); SigningCertificateV2 sigcert = new SigningCertificateV2(certid); final DERSet attrValues = new DERSet(sigcert); Attribute attr = new Attribute(PKCSObjectIdentifiers.id_aa_signingCertificateV2, attrValues); ASN1EncodableVector v = new ASN1EncodableVector(); v.add(attr); AttributeTable atttributeTable = new AttributeTable(v); //Create a standard attribute table from the passed in parameters - certhash CMSAttributeTableGenerator attrGen = new DefaultSignedAttributeTableGenerator(atttributeTable); final byte[] signedHash = // Retrieve signed hash from CSC. ContentSigner nonSigner = new ContentSigner() { @Override public byte[] getSignature() { return signedHash; } @Override public OutputStream getOutputStream() { return new ByteArrayOutputStream(); } @Override public AlgorithmIdentifier getAlgorithmIdentifier() { return new DefaultSignatureAlgorithmIdentifierFinder().find( "SHA256WithRSA" ); } }; org.bouncycastle.asn1.x509.Certificate cert2 = org.bouncycastle.asn1.x509.Certificate.getInstance(ASN1Primitive.fromByteArray(cert.getEncoded())); JcaSignerInfoGeneratorBuilder sigb = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()); sigb.setSignedAttributeGenerator(attrGen); gen.addSignerInfoGenerator(sigb.build(nonSigner, new X509CertificateHolder(cert2))); CMSTypedData msg = new CMSProcessableInputStream( inputStream); CMSSignedData signedData = gen.generate((CMSTypedData)msg, false); byte[] cmsSignature = signedData.getEncoded(); inputStream.close(); externalSigning.setSignature(cmsSignature); IOUtils.closeQuietly(signatureOptions); return new FileInputStream(outFile);
If I use subfilter as SUBFILTER_ADBE_PKCS7_DETACHED and don’t add addtibutesTable, then it works fine. But for SUBFILTER_ETSI_CADES_DETACHED, attributes needs to be added.