summaryrefslogtreecommitdiff
path: root/vcl/source/gdi/pdfwriter_impl.cxx
diff options
context:
space:
mode:
authorTor Lillqvist <tml@collabora.com>2014-12-03 14:00:38 +0200
committerAndras Timar <andras.timar@collabora.com>2014-12-12 06:33:52 -0800
commit4ffc81782072148f8ffeb9793c4bf4421866eefe (patch)
tree64e2d4ea6a837f07878361ac8d799ecb7a330b0d /vcl/source/gdi/pdfwriter_impl.cxx
parent975c0abd7c019f24b9a4887b78874dadb2e44287 (diff)
PDF signing feature backported from 4.4
Diffstat (limited to 'vcl/source/gdi/pdfwriter_impl.cxx')
-rw-r--r--vcl/source/gdi/pdfwriter_impl.cxx202
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 );