filter/source/pdf/impdialog.cxx | 18 +-- filter/source/pdf/impdialog.hrc | 4 filter/source/pdf/impdialog.src | 10 + filter/source/pdf/pdfexport.cxx | 2 include/vcl/pdfwriter.hxx | 5 vcl/Library_vcl.mk | 4 vcl/source/gdi/pdfwriter_impl.cxx | 202 ++++++++++++++++++++++++++++++++------ 7 files changed, 203 insertions(+), 42 deletions(-)
New commits: commit 4ffc81782072148f8ffeb9793c4bf4421866eefe Author: Tor Lillqvist <t...@collabora.com> Date: Wed Dec 3 14:00:38 2014 +0200 PDF signing feature backported from 4.4 Cherry picked from: 840f75065918c4584fa9159fdc90242b5374ab37 1fe9ee73a758603ee0e1465931352c41ef8bd999 d8a8ff8177df628636d80c4dc1d8f597f6677dfc bfa01d8a8248f7e44675de5ed1f85a1d17105022 bbb18f679f5a50e9c709520d6c3260d3d9db5aa9 c4cc31b5b55019aabad4045c8174b45e1b27073a 7e3c931786c3cbe83ee170b8b0746d141b520ce6 070c93af73df9aa4eb333265c81060d123b530b9 6e91763769a562b88882a4c2a94b1367c6ed4866 Change-Id: Ie8ac5e1e067f8e3b15e2f11389bd0517d348f1e3 diff --git a/filter/source/pdf/impdialog.cxx b/filter/source/pdf/impdialog.cxx index 0825ea0..7cffddd 100644 --- a/filter/source/pdf/impdialog.cxx +++ b/filter/source/pdf/impdialog.cxx @@ -251,11 +251,6 @@ ImpPDFTabDialog::ImpPDFTabDialog(Window* pParent, Sequence< PropertyValue >& rFi mnInterfacePageId = AddTabPage("userinterface", ImpPDFTabViewerPage::Create, 0); mnViewPageId = AddTabPage("initialview", ImpPDFTabOpnFtrPage::Create, 0); -//remove tabpage if experimentalmode is not set - SvtMiscOptions aMiscOptions; - if (!aMiscOptions.IsExperimentalMode()) - RemoveTabPage(mnSigningPageId); - //last queued is the first to be displayed (or so it seems..) mnGeneralPageId = AddTabPage("general", ImpPDFTabGeneralPage::Create, 0 ); @@ -322,11 +317,7 @@ ImpPDFTabDialog::~ImpPDFTabDialog() RemoveTabPage(mnViewPageId); RemoveTabPage(mnLinksPage); RemoveTabPage(mnSecurityPageId); - -//remove tabpage if experimentalmode is set - SvtMiscOptions aMiscOptions; - if (aMiscOptions.IsExperimentalMode()) - RemoveTabPage(mnSigningPageId); + RemoveTabPage(mnSigningPageId); } @@ -1495,6 +1486,13 @@ ImplErrorDialog::ImplErrorDialog(const std::set< vcl::PDFWriter::ErrorCode >& rE m_pErrors->SetEntryData( nPos, new OUString( PDFFilterResId( STR_WARN_TRANSP_CONVERTED ) ) ); } break; + case vcl::PDFWriter::Error_Signature_Failed: + { + sal_uInt16 nPos = m_pErrors->InsertEntry( OUString( PDFFilterResId( STR_ERR_SIGNATURE_FAILED ) ), + aErrImg ); + m_pErrors->SetEntryData( nPos, new OUString( PDFFilterResId( STR_ERR_PDF_EXPORT_ABORTED ) ) ); + } + break; default: break; } diff --git a/filter/source/pdf/impdialog.hrc b/filter/source/pdf/impdialog.hrc index 0927cb0..3b03503 100644 --- a/filter/source/pdf/impdialog.hrc +++ b/filter/source/pdf/impdialog.hrc @@ -19,6 +19,9 @@ #include <filter.hrc> +#define STR_ERR_SIGNATURE_FAILED (RID_PDF_DIALOG_START + 0) +#define STR_ERR_PDF_EXPORT_ABORTED (RID_PDF_DIALOG_START + 1) + #define RID_PDF_WARNPDFAPASSWORD (RID_PDF_DIALOG_START + 6) //strings for PDF security, user password management @@ -39,5 +42,6 @@ #define IMG_ERR (RID_PDF_DIALOG_START + 18) //ATTENTION: maximum allowed value is (RID_PDF_DIALOG_START + 19) +//(see filter/inc/filter.hrc) /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/pdf/impdialog.src b/filter/source/pdf/impdialog.src index 32d81a4..1aee2df 100644 --- a/filter/source/pdf/impdialog.src +++ b/filter/source/pdf/impdialog.src @@ -79,6 +79,16 @@ String STR_WARN_TRANSP_CONVERTED_SHORT Text [en-US] = "Transparencies removed"; }; +String STR_ERR_SIGNATURE_FAILED +{ + Text [en-US] = "Signature generation failed"; +}; + +String STR_ERR_PDF_EXPORT_ABORTED +{ + Text [en-US] = "PDF export aborted"; +}; + Bitmap IMG_WARN { File = "ballgreen_7.png"; diff --git a/filter/source/pdf/pdfexport.cxx b/filter/source/pdf/pdfexport.cxx index 8aceae6..164eff9 100644 --- a/filter/source/pdf/pdfexport.cxx +++ b/filter/source/pdf/pdfexport.cxx @@ -915,7 +915,7 @@ bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >& if( bRet ) { pPDFExtOutDevData->PlayGlobalActions( *pPDFWriter ); - pPDFWriter->Emit(); + bRet = pPDFWriter->Emit(); aErrors = pPDFWriter->GetErrors(); } pOut->SetExtOutDevData( NULL ); diff --git a/include/vcl/pdfwriter.hxx b/include/vcl/pdfwriter.hxx index d4b6e90..9357c91 100644 --- a/include/vcl/pdfwriter.hxx +++ b/include/vcl/pdfwriter.hxx @@ -215,7 +215,10 @@ public: // transparent objects were converted to a bitmap in order // to removetransparencies from the output - Warning_Transparency_Converted + Warning_Transparency_Converted, + + // signature generation failed + Error_Signature_Failed, }; struct VCL_DLLPUBLIC AnyWidget diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index 4e53256..aed7a60 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -55,7 +55,8 @@ $(eval $(call gb_Library_use_custom_headers,vcl,\ $(eval $(call gb_Library_use_externals,vcl,\ jpeg \ - nss3 \ + $(if $(filter-out WNT,$(OS)), \ + nss3) \ libeot \ )) @@ -625,6 +626,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ $(eval $(call gb_Library_use_system_win32_libs,vcl,\ advapi32 \ + crypt32 \ gdi32 \ gdiplus \ imm32 \ diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index 5548629..7d7b779 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 ); _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits