summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Lillqvist <tml@collabora.com>2014-12-05 16:06:08 +0200
committerTor Lillqvist <tml@collabora.com>2014-12-05 16:16:59 +0200
commit7e3c931786c3cbe83ee170b8b0746d141b520ce6 (patch)
treebb7693f6b863621c8a4ee8d5423574dbdea269e8
parentc4cc31b5b55019aabad4045c8174b45e1b27073a (diff)
fdo#87030: PDF signing using Windows API, work in progress
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. Change-Id: I0941995ad6e22c5487c6e6fe0084d3df0b2341c7
-rw-r--r--vcl/Library_vcl.mk4
-rw-r--r--vcl/source/gdi/pdfwriter_impl.cxx212
2 files changed, 212 insertions, 4 deletions
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 1adccabb7d64..7c8ccb5c9e0b 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 \
))
@@ -674,6 +675,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 f8cc4a201a8c..b459a8d28a7a 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
@@ -5971,6 +5978,8 @@ bool PDFWriterImpl::emitSignature()
return true;
}
+#if !defined(ANDROID) && !defined(IOS) && !defined(_WIN32)
+
char *PDFSigningPKCS7PasswordCallback(PK11SlotInfo * /*slot*/, PRBool /*retry*/, void *arg)
{
return (char *)arg;
@@ -5987,6 +5996,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()
{
@@ -6021,7 +6063,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);
@@ -6170,6 +6212,170 @@ bool PDFWriterImpl::finalizeSignature()
CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) );
return true;
+
+#else
+
+ 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;
+ }
+
+#if SAL_LOG_INFO
+ DWORD nProperty(0);
+ bool first(true);
+ while ((nProperty = CertEnumCertificateContextProperties(pCertContext, nProperty)) != 0)
+ {
+ if (first)
+ SAL_INFO("vcl.pdfwriter", "Certificate context properties:");
+ first = false;
+#if 0
+ DWORD nSize(0);
+ if (!CertGetCertificateContextProperty(pCertContext, nProperty, NULL, &nSize))
+ SAL_INFO("vcl.pdfwriter", " " << "(missing?) " << std::hex << nProperty);
+ else
+ {
+ boost::scoped_array<char> aData(new char[nSize]);
+ if (!CertGetCertificateContextProperty(pCertContext, nProperty, aData.get(), &nSize))
+ SAL_INFO("vcl.pdfwriter", " " << "(missing?) " << std::hex:: << nProperty);
+ else
+ SAL_INFO("vcl.pdfwriter", " " << CertificatePropertyNameAndData(nProperty, aData, nSize));
+ }
+#else
+ SAL_INFO("vcl.pdfwriter", " " << std::hex << nProperty);
+#endif
+ }
+#endif
+
+ // Prepare buffer and calculate PDF file digest
+ CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, 0)) );
+
+ HCRYPTPROV hCryptProvider;
+ if (!CryptAcquireContext(&hCryptProvider, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+ {
+ SAL_WARN("vcl.pdfwriter", "CryptAcquireContext failed: " << WindowsError(GetLastError()));
+ CertFreeCertificateContext(pCertContext);
+ return false;
+ }
+
+ HCRYPTHASH hHash;
+ if (!CryptCreateHash(hCryptProvider, CALG_SHA1, 0, 0, &hHash))
+ {
+ SAL_WARN("vcl.pdfwriter", "CryptCreateHash failed: " << WindowsError(GetLastError()));
+ CryptReleaseContext(hCryptProvider, 0);
+ CertFreeCertificateContext(pCertContext);
+ return false;
+ }
+
+ DWORD nHashSize;
+ DWORD nHashSizeLen(sizeof(DWORD));
+ if (!CryptGetHashParam(hHash, HP_HASHSIZE, reinterpret_cast<BYTE *>(&nHashSize), &nHashSizeLen, 0))
+ {
+ SAL_WARN("vcl.pdfwriter", "CryptGetHashParam failed: " << WindowsError(GetLastError()));
+ CryptDestroyHash(hHash);
+ CryptReleaseContext(hCryptProvider, 0);
+ CertFreeCertificateContext(pCertContext);
+ return false;
+ }
+
+ assert(nHashSizeLen == sizeof(DWORD));
+ assert(nHashSize == 20);
+
+ boost::scoped_array<char> buffer(new char[m_nSignatureContentOffset + 1]);
+ sal_uInt64 bytesRead;
+
+ //FIXME: Check if SHA1 is calculated from the correct byterange
+ CHECK_RETURN( (osl::File::E_None == m_aFile.read(buffer.get(), m_nSignatureContentOffset - 1 , bytesRead)) );
+ if (bytesRead != (sal_uInt64)m_nSignatureContentOffset - 1)
+ SAL_WARN("vcl.pdfwriter", "PDF Signing: First buffer read failed!");
+
+ if (!CryptHashData(hHash, reinterpret_cast<const BYTE *>(buffer.get()), bytesRead, 0))
+ {
+ SAL_WARN("vcl.pdfwriter", "CryptHashData failed: " << WindowsError(GetLastError()));
+ CryptDestroyHash(hHash);
+ CryptReleaseContext(hCryptProvider, 0);
+ CertFreeCertificateContext(pCertContext);
+ return false;
+ }
+
+ CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1)) );
+ buffer.reset(new char[nLastByteRangeNo + 1]);
+ CHECK_RETURN( (osl::File::E_None == m_aFile.read(buffer.get(), nLastByteRangeNo, bytesRead)) );
+ if (bytesRead != (sal_uInt64) nLastByteRangeNo)
+ SAL_WARN("vcl.pdfwriter", "PDF Signing: Second buffer read failed!");
+
+ if (!CryptHashData(hHash, reinterpret_cast<const BYTE *>(buffer.get()), bytesRead, 0))
+ {
+ SAL_WARN("vcl.pdfwriter", "CryptHashData failed: " << WindowsError(GetLastError()));
+ CryptDestroyHash(hHash);
+ CryptReleaseContext(hCryptProvider, 0);
+ CertFreeCertificateContext(pCertContext);
+ return false;
+ }
+
+#if 0 // We don't actualy need the hash bytes
+ unsigned char aHash[20];
+ if (!CryptGetHashParam(hHash, HP_HASHVAL, aHash, &nHashSize, 0))
+ {
+ SAL_WARN("vcl.pdfwriter", "CryptGetHashParam failed: " << WindowsError(GetLastError()));
+ CryptDestroyHash(hHash);
+ CryptReleaseContext(hCryptProvider, 0);
+ CertFreeCertificateContext(pCertContext);
+ return false;
+ }
+
+ if (nHashSize != 20)
+ {
+ SAL_WARN("vcl.pdfwriter", "CryptGetHashParam returned unexpected size hash value: " << nHashSize);
+ CryptDestroyHash(hHash);
+ CryptReleaseContext(hCryptProvider, 0);
+ CertFreeCertificateContext(pCertContext);
+ return false;
+ }
+#endif
+
+ OString pass = OUStringToOString( m_aContext.SignPassword, RTL_TEXTENCODING_UTF8 );
+
+ DWORD nSigLen(0);
+ if (!CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, NULL, &nSigLen))
+ {
+ SAL_WARN("vcl.pdfwriter", "CryptSignHash failed: " << WindowsError(GetLastError()));
+ CryptDestroyHash(hHash);
+ CryptReleaseContext(hCryptProvider, 0);
+ CertFreeCertificateContext(pCertContext);
+ return false;
+ }
+
+ boost::scoped_array<BYTE> pSig(new BYTE[nSigLen]);
+ if (!CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, pSig.get(), &nSigLen))
+ {
+ SAL_WARN("vcl.pdfwriter", "CryptSignHash failed: " << WindowsError(GetLastError()));
+ CryptDestroyHash(hHash);
+ CryptReleaseContext(hCryptProvider, 0);
+ CertFreeCertificateContext(pCertContext);
+ return false;
+ }
+
+ // Release resources
+ CryptDestroyHash(hHash);
+ CryptReleaseContext(hCryptProvider, 0);
+ 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