diff options
author | Tor Lillqvist <tml@collabora.com> | 2014-12-03 14:00:38 +0200 |
---|---|---|
committer | Andras Timar <andras.timar@collabora.com> | 2014-12-12 06:33:52 -0800 |
commit | 4ffc81782072148f8ffeb9793c4bf4421866eefe (patch) | |
tree | 64e2d4ea6a837f07878361ac8d799ecb7a330b0d /vcl/source/gdi/pdfwriter_impl.cxx | |
parent | 975c0abd7c019f24b9a4887b78874dadb2e44287 (diff) |
PDF signing feature backported from 4.4
Cherry picked from:
840f75065918c4584fa9159fdc90242b5374ab37
1fe9ee73a758603ee0e1465931352c41ef8bd999
d8a8ff8177df628636d80c4dc1d8f597f6677dfc
bfa01d8a8248f7e44675de5ed1f85a1d17105022
bbb18f679f5a50e9c709520d6c3260d3d9db5aa9
c4cc31b5b55019aabad4045c8174b45e1b27073a
7e3c931786c3cbe83ee170b8b0746d141b520ce6
070c93af73df9aa4eb333265c81060d123b530b9
6e91763769a562b88882a4c2a94b1367c6ed4866
Change-Id: Ie8ac5e1e067f8e3b15e2f11389bd0517d348f1e3
Diffstat (limited to 'vcl/source/gdi/pdfwriter_impl.cxx')
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.cxx | 202 |
1 files changed, 173 insertions, 29 deletions
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index 55486298cd62..7d7b7798eb4d 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -66,8 +66,8 @@ #include "pdfwriter_impl.hxx" -#if !defined(ANDROID) && !defined(IOS) -// NSS header files for PDF signing support +#if !defined(ANDROID) && !defined(IOS) && !defined(_WIN32) +// NSS headers for PDF signing #include "nss.h" #include "cert.h" #include "hasht.h" @@ -76,6 +76,13 @@ #include "cmst.h" #endif +#ifdef _WIN32 +// WinCrypt headers for PDF signing +#include <prewin.h> +#include <wincrypt.h> +#include <postwin.h> +#endif + #include <config_eot.h> #if ENABLE_EOT @@ -5940,6 +5947,8 @@ bool PDFWriterImpl::emitSignature() return true; } +#if !defined(ANDROID) && !defined(IOS) && !defined(_WIN32) + char *PDFSigningPKCS7PasswordCallback(PK11SlotInfo * /*slot*/, PRBool /*retry*/, void *arg) { return (char *)arg; @@ -5956,6 +5965,39 @@ namespace { }; } +#endif + +#ifdef _WIN32 + +namespace { + +OUString WindowsError(DWORD nErrorCode) +{ + LPWSTR pMsgBuf; + + if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + nErrorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&pMsgBuf, + 0, + NULL) == 0) + return OUString::number(nErrorCode, 16); + + if (pMsgBuf[wcslen(pMsgBuf)-1] == '\n') + pMsgBuf[wcslen(pMsgBuf)-1] = '\0'; + + OUString result(pMsgBuf); + + LocalFree(pMsgBuf); + + return result; +} + +} + +#endif + bool PDFWriterImpl::finalizeSignature() { @@ -5990,25 +6032,23 @@ bool PDFWriterImpl::finalizeSignature() sal_Int8* n_derArray = derEncoded.getArray(); sal_Int32 n_derLength = derEncoded.getLength(); - NSS_NoDB_Init("."); +#ifndef _WIN32 CERTCertificate *cert = CERT_DecodeCertFromPackage(reinterpret_cast<char *>(n_derArray), n_derLength); if (!cert) { - SAL_WARN("vcl.gdi", "PDF Signing: Error occurred, certificate cannot be reconstructed."); + SAL_WARN("vcl.pdfwriter", "PDF Signing: Error occurred, certificate cannot be reconstructed."); return false; } - SAL_WARN("vcl.gdi", "PDF Signing: Certificate Subject: " << cert->subjectName << "\n\tCertificate Issuer: " << cert->issuerName); - // Prepare buffer and calculate PDF file digest CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, 0) ) ); HashContextScope hc(HASH_Create(HASH_AlgSHA1)); if (!hc.get()) { - SAL_WARN("vcl.gdi", "PDF Signing: SHA1 HASH_Create failed!"); + SAL_WARN("vcl.pdfwriter", "PDF Signing: SHA1 HASH_Create failed!"); return false; } @@ -6021,7 +6061,7 @@ bool PDFWriterImpl::finalizeSignature() CHECK_RETURN( (osl_File_E_None == osl_readFile( m_aFile, buffer.get(), m_nSignatureContentOffset - 1 , &bytesRead ) ) ); if (bytesRead != (sal_uInt64)m_nSignatureContentOffset - 1) - SAL_WARN("vcl.gdi", "PDF Signing: First buffer read failed!"); + SAL_WARN("vcl.pdfwriter", "PDF Signing: First buffer read failed!"); HASH_Update(hc.get(), reinterpret_cast<const unsigned char*>(buffer.get()), bytesRead); @@ -6029,7 +6069,7 @@ bool PDFWriterImpl::finalizeSignature() buffer.reset(new char[nLastByteRangeNo + 1]); CHECK_RETURN( (osl_File_E_None == osl_readFile( m_aFile, buffer.get(), nLastByteRangeNo, &bytesRead ) ) ); if (bytesRead != (sal_uInt64) nLastByteRangeNo) - SAL_WARN("vcl.gdi", "PDF Signing: Second buffer read failed!"); + SAL_WARN("vcl.pdfwriter", "PDF Signing: Second buffer read failed!"); HASH_Update(hc.get(), reinterpret_cast<const unsigned char*>(buffer.get()), bytesRead); @@ -6044,21 +6084,21 @@ bool PDFWriterImpl::finalizeSignature() NSSCMSMessage *cms_msg = NSS_CMSMessage_Create(NULL); if (!cms_msg) { - SAL_WARN("vcl.gdi", "PDF signing: can't create new CMS message."); + SAL_WARN("vcl.pdfwriter", "PDF signing: can't create new CMS message."); return false; } NSSCMSSignedData *cms_sd = NSS_CMSSignedData_Create(cms_msg); if (!cms_sd) { - SAL_WARN("vcl.gdi", "PDF signing: can't create CMS SignedData."); + SAL_WARN("vcl.pdfwriter", "PDF signing: can't create CMS SignedData."); return false; } NSSCMSContentInfo *cms_cinfo = NSS_CMSMessage_GetContentInfo(cms_msg); if (NSS_CMSContentInfo_SetContent_SignedData(cms_msg, cms_cinfo, cms_sd) != SECSuccess) { - SAL_WARN("vcl.gdi", "PDF signing: Can't set CMS content signed data."); + SAL_WARN("vcl.pdfwriter", "PDF signing: Can't set CMS content signed data."); return false; } @@ -6066,49 +6106,47 @@ bool PDFWriterImpl::finalizeSignature() //attach NULL data as detached data if (NSS_CMSContentInfo_SetContent_Data(cms_msg, cms_cinfo, NULL, PR_TRUE) != SECSuccess) { - SAL_WARN("vcl.gdi", "PDF signing: Can't set CMS content data."); + SAL_WARN("vcl.pdfwriter", "PDF signing: Can't set CMS content data."); return false; } NSSCMSSignerInfo *cms_signer = NSS_CMSSignerInfo_Create(cms_msg, cert, SEC_OID_SHA1); if (!cms_signer) { - SAL_WARN("vcl.gdi", "PDF signing: can't create CMS SignerInfo."); + SAL_WARN("vcl.pdfwriter", "PDF signing: can't create CMS SignerInfo."); return false; } if (NSS_CMSSignerInfo_IncludeCerts(cms_signer, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess) { - SAL_WARN("vcl.gdi", "PDF signing: can't include cert chain."); + SAL_WARN("vcl.pdfwriter", "PDF signing: can't include cert chain."); return false; } if (NSS_CMSSignerInfo_AddSigningTime(cms_signer, PR_Now()) != SECSuccess) { - SAL_WARN("vcl.gdi", "PDF signing: can't add signing time."); + SAL_WARN("vcl.pdfwriter", "PDF signing: can't add signing time."); return false; } if (NSS_CMSSignedData_AddCertificate(cms_sd, cert) != SECSuccess) { - SAL_WARN("vcl.gdi", "PDF signing: can't add signer certificate."); + SAL_WARN("vcl.pdfwriter", "PDF signing: can't add signer certificate."); return false; } if (NSS_CMSSignedData_AddSignerInfo(cms_sd, cms_signer) != SECSuccess) { - SAL_WARN("vcl.gdi", "PDF signing: can't add signer info."); + SAL_WARN("vcl.pdfwriter", "PDF signing: can't add signer info."); return false; } if (NSS_CMSSignedData_SetDigestValue(cms_sd, SEC_OID_SHA1, &digest) != SECSuccess) { - SAL_WARN("vcl.gdi", "PDF signing: can't set PDF digest value."); + SAL_WARN("vcl.pdfwriter", "PDF signing: can't set PDF digest value."); return false; } - SAL_WARN("vcl.gdi","PKCS7 Object created successfully!"); - SECItem cms_output; cms_output.data = 0; cms_output.len = 0; @@ -6120,25 +6158,21 @@ bool PDFWriterImpl::finalizeSignature() if (!cms_ecx) { - SAL_WARN("vcl.gdi", "PDF Signing: can't start DER encoder."); + SAL_WARN("vcl.pdfwriter", "PDF Signing: can't start DER encoder."); return false; } - SAL_WARN("vcl.gdi", "PDF Signing: Started DER encoding."); if (NSS_CMSEncoder_Finish(cms_ecx) != SECSuccess) { - SAL_WARN("vcl.gdi", "PDF Signing: can't finish DER encoder."); + SAL_WARN("vcl.pdfwriter", "PDF Signing: can't finish DER encoder."); return false; } - SAL_WARN("vcl.gdi", "PDF Signing: Finished DER encoding."); OStringBuffer cms_hexbuffer; for (unsigned int i = 0; i < cms_output.len ; i++) appendHex(cms_output.data[i], cms_hexbuffer); - SAL_WARN("vcl.gdi","PKCS7 object encoded successfully!"); - // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object nWritten = 0; CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, m_nSignatureContentOffset) ) ); @@ -6148,6 +6182,104 @@ bool PDFWriterImpl::finalizeSignature() CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, nOffset ) ) ); return true; + +#else + + // Prepare buffer and calculate PDF file digest + CHECK_RETURN( (osl_File_E_None == osl_setFilePos(m_aFile, osl_Pos_Absolut, 0))); + + PCCERT_CONTEXT pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, reinterpret_cast<const BYTE*>(n_derArray), n_derLength); + if (pCertContext == NULL) + { + SAL_WARN("vcl.pdfwriter", "CertCreateCertificateContext failed: " << WindowsError(GetLastError())); + return false; + } + + boost::scoped_array<char> buffer1(new char[m_nSignatureContentOffset - 1]); + sal_uInt64 bytesRead1; + + if (osl_File_E_None != osl_readFile(m_aFile, buffer1.get(), m_nSignatureContentOffset - 1 , &bytesRead1) || + bytesRead1 != (sal_uInt64)m_nSignatureContentOffset - 1) + { + SAL_WARN("vcl.pdfwriter", "PDF Signing: First buffer read failed!"); + CertFreeCertificateContext(pCertContext); + return false; + } + + boost::scoped_array<char> buffer2(new char[nLastByteRangeNo]); + sal_uInt64 bytesRead2; + + if (osl_File_E_None != osl_setFilePos(m_aFile, osl_Pos_Absolut, m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1) || + osl_File_E_None != osl_readFile(m_aFile, buffer2.get(), nLastByteRangeNo, &bytesRead2) || + bytesRead2 != (sal_uInt64) nLastByteRangeNo) + { + SAL_WARN("vcl.pdfwriter", "PDF Signing: Second buffer read failed!"); + CertFreeCertificateContext(pCertContext); + return false; + } + + OString pass = OUStringToOString( m_aContext.SignPassword, RTL_TEXTENCODING_UTF8 ); + + CRYPT_SIGN_MESSAGE_PARA aPara; + + memset(&aPara, 0, sizeof(aPara)); + aPara.cbSize = sizeof(aPara); + aPara.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING; + aPara.pSigningCert = pCertContext; + aPara.HashAlgorithm.pszObjId = szOID_RSA_SHA1RSA; + aPara.HashAlgorithm.Parameters.cbData = 0; + aPara.cMsgCert = 1; + aPara.rgpMsgCert = &pCertContext; + + const BYTE *aBuffers[] = + { reinterpret_cast<BYTE*>(buffer1.get()), reinterpret_cast<BYTE*>(buffer2.get()) }; + DWORD aBufferLens[] = + { bytesRead1, bytesRead2 }; + assert(SAL_N_ELEMENTS(aBuffers) == SAL_N_ELEMENTS(aBufferLens)); + + DWORD nSigLen(0); + + if (!CryptSignMessage(&aPara, TRUE, SAL_N_ELEMENTS(aBuffers), aBuffers, aBufferLens, NULL, &nSigLen)) + { + SAL_WARN("vcl.pdfwriter", "CryptSignMessage failed: " << WindowsError(GetLastError())); + CertFreeCertificateContext(pCertContext); + return false; + } + + if (nSigLen*2 > MAX_SIGNATURE_CONTENT_LENGTH) + { + SAL_WARN("vcl.pdfwriter", "Signature requires more space (" << nSigLen*2 << ") than we reserved (" << MAX_SIGNATURE_CONTENT_LENGTH << ")"); + CertFreeCertificateContext(pCertContext); + return false; + } + + SAL_INFO("vcl.pdfwriter", "Signature size is " << nSigLen << " bytes"); + + boost::scoped_array<BYTE> pSig(new BYTE[nSigLen]); + if (!CryptSignMessage(&aPara, TRUE, SAL_N_ELEMENTS(aBuffers), aBuffers, aBufferLens, pSig.get(), &nSigLen)) + { + SAL_WARN("vcl.pdfwriter", "CryptSignMessage failed: " << WindowsError(GetLastError())); + CertFreeCertificateContext(pCertContext); + return false; + } + + // Release resources + CertFreeCertificateContext(pCertContext); + + OStringBuffer cms_hexbuffer; + + for (unsigned int i = 0; i < nSigLen ; i++) + appendHex(pSig[i], cms_hexbuffer); + + // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object + nWritten = 0; + CHECK_RETURN( (osl_File_E_None == osl_setFilePos(m_aFile, osl_Pos_Absolut, m_nSignatureContentOffset)) ); + osl_writeFile(m_aFile, cms_hexbuffer.getStr(), cms_hexbuffer.getLength(), &nWritten); + + CHECK_RETURN( (osl_File_E_None == osl_setFilePos(m_aFile, osl_Pos_Absolut, nOffset)) ); + + return true; +#endif } #endif @@ -6951,7 +7083,13 @@ bool PDFWriterImpl::emit() #if !defined(ANDROID) && !defined(IOS) if (m_nSignatureObject != -1) // if document is signed, emit sigdict - CHECK_RETURN( emitSignature() ); + { + if( !emitSignature() ) + { + m_aErrors.insert( PDFWriter::Error_Signature_Failed ); + return false; + } + } #endif // emit trailer @@ -6959,7 +7097,13 @@ bool PDFWriterImpl::emit() #if !defined(ANDROID) && !defined(IOS) if (m_nSignatureObject != -1) // finalize the signature - CHECK_RETURN( finalizeSignature() ); + { + if( !finalizeSignature() ) + { + m_aErrors.insert( PDFWriter::Error_Signature_Failed ); + return false; + } + } #endif osl_closeFile( m_aFile ); |