I'm generating a p7s with CryptSignMessage and etoken (safenet), but after inserting it into the PDF, Adobe indicates that the signature cannot be validated because the document was modified or corrupted.
This is my C function:
int GeneratePKCS7Signature(const BYTE * pbData, DWORD cbData,
const CERTIFICATES_INFO * certInfo, BYTE ** ppbPkcs7, DWORD * pcbPkcs7) {
if (!pbData || cbData == 0 || !certInfo || !certInfo -> signerCert || !ppbPkcs7 || !pcbPkcs7) {
printf("Invalid parameters.\n");
return 1;
}
printf("Start GeneratePKCS7Signature\n");
// Verify that the signing certificate has its private key associated (using SafeNet etoken)
NCRYPT_KEY_HANDLE hKey = 0;
BOOL freeKey = FALSE;
if (!CryptAcquireCertificatePrivateKey(certInfo -> signerCert,
CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG,
NULL,
(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE * ) & hKey,
NULL, &
freeKey)) {
printf("Error searching for the private key of the signing certificate: %lu\n", GetLastError());
return 1;
}
if (freeKey && hKey)
NCryptFreeObject(hKey);
// Build the array of certificates to be included in thePKCS#7:
// The signing certificate is included, then the intermediate certificates and finally the root certificates
size_t totalCertCount = 1 + certInfo -> numIntermediates + certInfo -> numRoots;
PCCERT_CONTEXT * rgpCerts = (PCCERT_CONTEXT * ) malloc(totalCertCount * sizeof(PCCERT_CONTEXT));
if (!rgpCerts) {
printf("Error allocating memory for the certificate array.\n");
return 1;
}
size_t idx = 0;
rgpCerts[idx++] = certInfo -> signerCert;
for (size_t i = 0; i < certInfo -> numIntermediates; i++) {
rgpCerts[idx++] = certInfo -> intermediates[i];
}
for (size_t i = 0; i < certInfo -> numRoots; i++) {
rgpCerts[idx++] = certInfo -> roots[i];
}
// Configure the structure CRYPT_SIGN_MESSAGE_PARA.
CRYPT_SIGN_MESSAGE_PARA signPara;
memset( & signPara, 0, sizeof(signPara));
signPara.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
signPara.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
signPara.pSigningCert = certInfo -> signerCert;
signPara.HashAlgorithm.pszObjId = OID_RSA_SHA256; // SHA256
signPara.cMsgCert = (DWORD) totalCertCount;
signPara.rgpMsgCert = rgpCerts;
signPara.cAuthAttr = 0;
signPara.dwFlags = 0;
signPara.dwInnerContentType = 0;
// Prepare the parameters for the function CryptSignMessage.
const BYTE * rgpbToBeSigned[1] = {
pbData
};
DWORD rgcbToBeSigned[1] = {
cbData
};
// First call to get the required size of the PKCS#7.
DWORD cbPkcs7 = 0;
if (!CryptSignMessage( & signPara,
TRUE, // Sign detached
1,
rgpbToBeSigned,
rgcbToBeSigned,
NULL, &
cbPkcs7)) {
printf("Error calculating the size of the PKCS#7: 0x%x\n", GetLastError());
free(rgpCerts);
return 1;
}
BYTE * pbPkcs7 = (BYTE * ) HeapAlloc(GetProcessHeap(), 0, cbPkcs7);
if (!pbPkcs7) {
printf("Error allocating memory for the PKCS#7.\n");
free(rgpCerts);
return 1;
}
// Second call to generate the PKCS#7.
if (!CryptSignMessage( & signPara,
TRUE, // Sign detached
1,
rgpbToBeSigned,
rgcbToBeSigned,
pbPkcs7, &
cbPkcs7)) {
printf("Error generating the PKCS#7: 0x%x\n", GetLastError());
HeapFree(GetProcessHeap(), 0, pbPkcs7);
free(rgpCerts);
return 1;
}
* ppbPkcs7 = pbPkcs7;
* pcbPkcs7 = cbPkcs7;
free(rgpCerts);
return 0;
}
the p7s generated is: firma.p7s test pdf signed: pdf
I've tried several different PDFs. I've also verified the SHA256 hashes before and after signing. I've analyzed the PDF structure with different tools, such as QPDF. Therefore, I think the problem is in PKCS7, some attribute, or the signature itself that's preventing Adobe from validating it. I've also checked with the ANS.1 decoder, and even though I've corrected some things, the error remains the same.
I don't know the details of creating signatures with CryptSignMessage
, so I could only analyze your signed PDF. Some observations:
The signed hash is incorrect, the hash in the signature in the SignerInfo of the signature container is
71AC5A10FA2A842885C3D988B154F024B93E0F7858D0823B3DB35F6D30BA8F5B
while the hash of the signed byte ranges is
37F0FAEC2048A45E9F621794922B606C622666CD0D74122A23FE564049F960EA
The digest algorithms OIDs in the signature container are wrong, they are the OID of SHA256withRSA but they should be the OID of SHA256.
The structure of the signature container is primitive, it doesn't make use of signed attributes. While this is not an error per se, advanced validation policies profile the signature container structure to necessarily contain certain signed attributes.
The PDF strictly speaking is invalid: Its final trailer has a Size entry of 1013 and the cross reference table sections only contain entries for the object numbers 0..8 and 1008..1012. But according to the PDF spec:
The cross-reference table (comprising the original cross-reference section and all update sections) shall contain one entry for each object number from 0 to the maximum object number defined in the file, even if one or more of the object numbers in this range do not actually occur in the file.
Thus, there also should be entries for the object numbers 9..1007, even if they are only f entries.
Such cross reference errors also can result in validation errors.
I hope these observations help you to fix the issues in your code.