I have the following ASN1 ASN.1 dump
SET (4 elem)
SEQUENCE (2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.9.3 contentType (PKCS #9)
SET (1 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.7.1 data (PKCS #7)
SEQUENCE (2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.9.5 signingTime (PKCS #9)
SET (1 elem)
UTCTime 2021-05-26 19:03:42 UTC
SEQUENCE (2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.9.52 cmsAlgorithmProtection (RFC 6211)
SET (1 elem)
SEQUENCE (2 elem)
SEQUENCE (2 elem)
OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1 sha-256 (NIST Algorithm)
NULL
[1] (2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.1.11 sha256WithRSAEncryption (PKCS #1)
NULL
SEQUENCE (2 elem)
OBJECT IDENTIFIER 1.2.840.113549.1.9.4 messageDigest (PKCS #9)
SET (1 elem)
OCTET STRING (32 byte) E2BB4AD28C95B99E9EDEF70662AFE825AF477680F4867B59833AA05313D8F4C0
and I understand that the OCTET STRING is the messageDigest(hash sha-256) of what I am trying to sign. Which in this case is a PDF document using PDFBOX the code I'm using to sign is the following
public byte[] signPKCS7(InputStream content) throws IOException,SignedBytesException {
try {
if (SigUtils.checkCertificateUsage((X509Certificate) certificateChain[0])) {
CMSSignedDataGenerator signGenerator = new CMSSignedDataGenerator();
X509Certificate userCert = (X509Certificate) this.certificateChain[0];
ContentSigner mySigner = new CustomSigner(invoke,String.valueOf(userCert.getSerialNumber()),sad);
signGenerator.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build())
.build(mySigner, userCert));
signGenerator.addCertificates(new JcaCertStore(Arrays.asList(certificateChain)));
CMSProcessableInputStream msg = new CMSProcessableInputStream(content);
CMSSignedData signedData = signGenerator.generate(msg, false);
return signedData.getEncoded();
} else {
throw new Exception("Unable to sign pdf. Certificate usage not appropiate for request");
}
} catch (GeneralSecurityException | CMSException | OperatorCreationException e) {
logger.error(e.getMessage());
throw new RuntimeException("unable to sign pdf!", e);
}
}
I have also calculated the sha-256 of the document I am trying to sign and the result is the following
0622971147486E1900037EFF229D921D14F5B51AAC7171729B2B66F81CDF6585
So my question is, is the message digest from the ANS1 the same as the one I calculated? And if so how do I reach that result as when I'm going through the ASN1 structure with the following code I have not been able to get the same result
private byte[] getMessageDigest(byte[] signatures) throws IOException {
ASN1InputStream input = new ASN1InputStream(signatures);
byte[] bytesToSign = null;
ASN1Primitive p;
while ((p = input.readObject()) != null) {
if (p instanceof ASN1Set) {
ASN1Set set = ASN1Set.getInstance(p);
ASN1Sequence asn1 = ASN1Sequence.getInstance(set.getObjectAt(3));
ASN1Set setOcter = ASN1Set.getInstance(asn1.getObjectAt(1));
ASN1OctetString octstr = ASN1OctetString.getInstance(setOcter.getObjectAt(0));
bytesToSign = octstr.getOctets();
}
}
return bytesToSign;
}
and the using the following code to convert the bytes to hex
private String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
I get the following result
E2BB4AD28C95B99E9EDEF70662AFE825AF477680F4867B59833AA05313D8F4C0
Which is the OCTET STRING of the ASN1 dump but its not the hash of the document. And that Octet String is always changing so I can assume its actually not a regular message digest. So what exactly is it and am I able to get the sha-256 of the content I'm sending to sign
The document hash is not calculated from the original PDF you want to sign. That PDF first is prepared for signing by applying certain changes, and then the hash is calculated from this prepared PDF except a placeholder gap in it prepared to later house the signature container.
To create an integrated PDF signature, certain changes have to be applied to the PDF:
With these additions the PDF is stored. Thereafter the position of the Contents value in the file is fixed and the blank value of the ByteRange value is patched to an array of four integers, the start offset and size of the file segment before the Contents value and the start offset and size of the file segment thereafter.
Then the bytes of these segments of the file are hashed and a CMS signature container signing this document hash is generated which in turn is injected into the Contents value.
In your case the hash you find in the to-be-signed attributes,
E2BB4AD28C95B99E9EDEF70662AFE825AF477680F4867B59833AA05313D8F4C0
is the hash over those two segments of the prepared file which almost always will differ from the hash over the original PDF, like in your case where that is
0622971147486E1900037EFF229D921D14F5B51AAC7171729B2B66F81CDF6585