[Libreoffice-commits] core.git: Branch 'libreoffice-4-4' - vcl/Library_vcl.mk vcl/source

Tor Lillqvist tml at collabora.com
Thu Dec 11 03:23:46 PST 2014


 vcl/Library_vcl.mk                |    4 
 vcl/source/gdi/pdfwriter_impl.cxx |  154 +++++++++++++++++++++++++++++++++++---
 2 files changed, 146 insertions(+), 12 deletions(-)

New commits:
commit be417961eecf6b096bd64fd55dccd4214c48836f
Author: Tor Lillqvist <tml at collabora.com>
Date:   Fri Dec 5 11:50:30 2014 +0200

    fdo#87030: Make PDF signing using Windows API work
    
    This is a squash of four commits that incrementally add Windows-specific code
    for PDF signing and make it work properly.
    
    1)
    
    Remove noise SAL_WARNs that were not warnings, just informative messages
    
    SAL_DEBUG is what should be used for stuff like that (and not committed).
    
    2)
    
    Started writing a Windows version of PDFWriterImpl::finalizeSignature(). The
    certificate we get from the selection dialog (in xmlsecurity) is from the
    Windows built-in store anyway, so it is pointless to try to use it with
    NSS. (See bug for longer discussion.)
    
    So far it was pretty straightforward. The WinCrypt API seems clean and easy to
    use. But for some reason the CryptSignHash() call fails with "Keyset does not
    exist" (NTE_BAD_KEYSET). What am I missing?
    
    Anyway, comitting this now as it does compile and doesn't make things worse.
    
    3)
    
    Prevent PDF signing using Windows API from failing
    
    There was one details that I had missed in my initial coding:
    CryptAcquireContext() doesn't give you a HCRYPTPROV key container that
    would contain the private key of a public key certificate. For that
    you need to use CryptAcquireCertificatePrivateKey(). When the hash has
    been created using such a HCRYPTPROV, the CryptSignHash() call
    succeeds.
    
    The certificate in DER encoding that is passed in from the caller,
    obtained in the certificate chooser (in xmlsecurity), is possibly not
    good enough to be used for the other things. So look the same (?)
    certificate up in the user's key store instead. At least more
    properties are present in the certificate when looked up like that.
    
    Add more SAL_INFO logging, with cleartext dumping of certificate
    context property names and list of algorithms supported by the CSP.
    
    Unfortunately, even if all the WinCrypt API calls now succeed, the
    signatures we produce still are not good enough for Adobe Reader... A
    lot of information must be missing, they are quite short, just 256
    bytes.
    
    4)
    
    Generate a proper PKCS#7 signature
    
    The signature should be in DER-encoded PKCS#7 format and what CryptSignHash()
    produces is nothing like that. Luckily CryptSignMessage() is actually almost
    easier to use and is capable of doing what we need. This also means that we
    won't need any HCRYPTPROV or HCRYPTHASH after all so all the code related to
    that can be removed. CryptSignMessage() handles both calculating the hash and
    signing it.
    
    One less than ideal issue with CryptSignMessage() is that it needs all the
    data to be hashed and signed at the same time, so we need to keep both buffers
    around for signing.
    
    It also turns out that we don't need to look up the certificate anew from the
    user's certificate store after all.
    
    Now Adobe Reader doesn't complain any longer about the signature's format and
    contents.
    
    Change-Id: I4674805cca2fa9342860e635cb07cbdfddfdb08c
    Reviewed-on: https://gerrit.libreoffice.org/13434
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>
    Tested-by: Jan Holesovsky <kendy at collabora.com>

diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index ccf3ddb..9361310 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -60,7 +60,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 \
 ))
 
@@ -673,6 +674,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
 
 $(eval $(call gb_Library_use_system_win32_libs,vcl,\
 	advapi32 \
+	crypt32 \
 	gdi32 \
 	gdiplus \
     glu32 \
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index ffd9661..93efd94 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -68,8 +68,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"
@@ -78,6 +78,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
@@ -5953,6 +5960,8 @@ bool PDFWriterImpl::emitSignature()
     return true;
 }
 
+#if !defined(ANDROID) && !defined(IOS) && !defined(_WIN32)
+
 char *PDFSigningPKCS7PasswordCallback(PK11SlotInfo * /*slot*/, PRBool /*retry*/, void *arg)
 {
     return (char *)arg;
@@ -5969,6 +5978,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()
 {
 
@@ -6003,7 +6045,7 @@ 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);
 
@@ -6013,8 +6055,6 @@ bool PDFWriterImpl::finalizeSignature()
         return false;
     }
 
-    SAL_WARN("vcl.pdfwriter", "PDF Signing: Certificate Subject: " <<  cert->subjectName << "\n\tCertificate Issuer: " << cert->issuerName);
-
     // Prepare buffer and calculate PDF file digest
     CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, 0)) );
 
@@ -6119,8 +6159,6 @@ bool PDFWriterImpl::finalizeSignature()
         return false;
     }
 
-    SAL_WARN("vcl.pdfwriter","PKCS7 Object created successfully!");
-
     SECItem cms_output;
     cms_output.data = 0;
     cms_output.len = 0;
@@ -6135,22 +6173,18 @@ bool PDFWriterImpl::finalizeSignature()
         SAL_WARN("vcl.pdfwriter", "PDF Signing: can't start DER encoder.");
         return false;
     }
-    SAL_WARN("vcl.pdfwriter", "PDF Signing: Started DER encoding.");
 
     if (NSS_CMSEncoder_Finish(cms_ecx) != SECSuccess)
     {
         SAL_WARN("vcl.pdfwriter", "PDF Signing: can't finish DER encoder.");
         return false;
     }
-    SAL_WARN("vcl.pdfwriter", "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.pdfwriter","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 == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset)) );
@@ -6160,6 +6194,104 @@ bool PDFWriterImpl::finalizeSignature()
 
     CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) );
     return true;
+
+#else
+
+    // Prepare buffer and calculate PDF file digest
+    CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(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 != m_aFile.read(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 != m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1) ||
+        osl::File::E_None != m_aFile.read(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 == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset)) );
+    m_aFile.write(cms_hexbuffer.getStr(), cms_hexbuffer.getLength(), nWritten);
+
+    CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) );
+
+    return true;
+#endif
 }
 
 #endif


More information about the Libreoffice-commits mailing list