Estou gerando um p7s com CryptSignMessage e etoken (safenet), mas após inseri-lo no PDF, o Adobe indica que a assinatura não pode ser validada porque o documento foi modificado ou corrompido.
Esta é minha função C:
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;
}
o p7s gerado é: firma.p7s teste pdf assinado: pdf
Testei vários PDFs diferentes. Também verifiquei os hashes SHA256 antes e depois da assinatura. Analisei a estrutura do PDF com diferentes ferramentas, como o QPDF. Portanto, acredito que o problema esteja no PKCS7, em algum atributo ou na própria assinatura que está impedindo a Adobe de validá-la. Também verifiquei com o decodificador ANS.1 e, embora tenha corrigido algumas coisas, o erro continua o mesmo.
Não conheço os detalhes da criação de assinaturas com
CryptSignMessage
, então só pude analisar o seu PDF assinado. Algumas observações:O hash assinado está incorreto, o hash na assinatura no SignerInfo do contêiner de assinatura está
enquanto o hash dos intervalos de bytes assinados é
Os OIDs dos algoritmos de resumo no contêiner de assinatura estão errados, eles são o OID do SHA256withRSA, mas deveriam ser o OID do SHA256.
A estrutura do contêiner de assinatura é primitiva e não utiliza atributos assinados. Embora isso não seja um erro em si, políticas de validação avançadas definem o perfil da estrutura do contêiner de assinatura para conter necessariamente determinados atributos assinados.
O PDF, estritamente falando, é inválido: seu trailer final tem uma entrada de tamanho de 1013 e as seções da tabela de referência cruzada contêm apenas entradas para os números de objeto 0 a 8 e 1008 a 1012. Mas, de acordo com a especificação do PDF:
Portanto, também deve haver entradas para os números de objeto 9 a 1007, mesmo que sejam apenas f entradas.
Esses erros de referência cruzada também podem resultar em erros de validação.
Espero que essas observações ajudem você a corrigir os problemas no seu código.