diff options
author | Gökçen Eraslan <gokcen.eraslan@gmail.com> | 2012-08-11 23:09:14 +0300 |
---|---|---|
committer | Gökçen Eraslan <gokcen.eraslan@gmail.com> | 2012-08-11 23:13:59 +0300 |
commit | 500261a6b6e9dbeacce7f693b01a1c32a112f4fb (patch) | |
tree | a066867d4b96aae13c84a0d937d1f27f1601bb89 | |
parent | 607e387412606c92a2ebd6fcc1d41743f33a4a72 (diff) |
Use NSS to create a PKCS7 object and write it to PDF file.
After calculating the SHA1 sum of the PDF file, a PKCS7 object is
created using the SEC_PKCS7CreateSignedData function of NSS
library. Then, created PKCS7 object is encoded and written to PDF
file.
Change-Id: Ied747f2e31ede1bc44109e5d4223a18f0af59633
-rw-r--r-- | RepositoryExternal.mk | 1 | ||||
-rw-r--r-- | vcl/Library_vcl.mk | 32 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.cxx | 129 |
3 files changed, 152 insertions, 10 deletions
diff --git a/RepositoryExternal.mk b/RepositoryExternal.mk index 4216bf240350..342f5fb0698d 100644 --- a/RepositoryExternal.mk +++ b/RepositoryExternal.mk @@ -1900,6 +1900,7 @@ else $(eval $(call gb_Helper_register_libraries,PLAINLIBS_OOO,\ nspr4 \ + smime3 \ nss3 \ plc4 \ )) diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index daa51764dd68..ebd1f56696ee 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -83,6 +83,38 @@ $(eval $(call gb_Library_use_libraries,vcl,\ $(gb_STDLIBS) \ )) +ifeq ($(SYSTEM_NSS),YES) + +$(eval $(call gb_Library_set_include,vcl,\ + $$(INCLUDE) \ + $(NSS_CFLAGS) \ +)) + +$(eval $(call gb_Library_add_defs,vcl,\ + -DSYSTEM_MOZILLA \ +)) + +$(eval $(call gb_Library_add_libs,vcl,\ + $(NSS_LIBS) \ +)) + +else + +$(eval $(call gb_Library_set_include,vcl,\ + $$(INCLUDE) \ + -I$(OUTDIR)/inc/mozilla/nspr \ + -I$(OUTDIR)/inc/mozilla/nss \ +)) + +$(eval $(call gb_Library_use_libraries,vcl,\ + nspr4 \ + nss3 \ + smime3 \ +)) + +endif # ifeq ($(SYSTEM_NSS),YES) + + ifneq ($(OS),IOS) $(eval $(call gb_Library_use_libraries,vcl,\ jvmaccess \ diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index aafea0564701..6104d5593c66 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -82,6 +82,14 @@ #include "cppuhelper/implbase1.hxx" +// NSS header files for PDF signing support +#include "nss.h" +#include "cert.h" +#include "hasht.h" +#include "sechash.h" +#include "pkcs7t.h" +#include "secpkcs7.h" + using namespace vcl; using ::rtl::OUString; @@ -6098,7 +6106,7 @@ bool PDFWriterImpl::emitSignature() OStringBuffer aContentFiller( MAX_SIGNATURE_CONTENT_LENGTH ); comphelper::string::padToLength(aContentFiller, MAX_SIGNATURE_CONTENT_LENGTH, '0'); aLine.append( aContentFiller.makeStringAndClear() ); - aLine.append( ">\n/Type/Sig/SubFilter/adbe.pkcs7.sha1"); + aLine.append( ">\n/Type/Sig/SubFilter/adbe.pkcs7.detached"); if( m_aContext.DocumentInfo.Author.Len() ) { @@ -6152,6 +6160,23 @@ bool PDFWriterImpl::emitSignature() return true; } +void PDFSigningPKCS7Callback(void *arg, const char *buf, unsigned long len) +{ + OStringBuffer outbuffer; + + for (unsigned int i = 0; i < len ; i++) + appendHex(buf[i], outbuffer); + + sal_uInt64 nWritten = 0; + + osl_writeFile((oslFileHandle)arg, outbuffer.getStr(), outbuffer.getLength(), &nWritten); +} + +void *PDFSigningPKCS7PasswordCallback(void *arg, void * /*handle*/) +{ + return arg; +} + bool PDFWriterImpl::finalizeSignature() { @@ -6162,7 +6187,7 @@ bool PDFWriterImpl::finalizeSignature() sal_uInt64 nOffset = ~0U; CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nOffset ) ) ); - sal_Int64 nLastByteRangeNo = nOffset - (m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH) - 1; + sal_Int64 nLastByteRangeNo = nOffset - (m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1); // 2- overwrite the value to the m_nSignatureLastByteRangeNoOffset position sal_uInt64 nWritten = 0; @@ -6185,18 +6210,102 @@ bool PDFWriterImpl::finalizeSignature() if (!derEncoded.hasElements()) return false; -#if 0 - // FIXME TODO sal_Int8* n_derArray = derEncoded.getArray(); sal_Int32 n_derLength = derEncoded.getLength(); -#endif - // 4- overwrite the PKCS7 content to the m_nSignatureContentOffset - CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, m_nSignatureContentOffset ) ) ); - // osl_writeFile() + NSS_NoDB_Init("."); + + /* An alternate method for certificate reconstruction + SECItem certitem; + certitem.data = reinterpret_cast<unsigned char *>(n_derArray); + certitem.len = n_derLength; + certitem.type = siDERCertBuffer; + CERTCertificate *cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &certitem, NULL, PR_FALSE, PR_TRUE); + */ + + CERTCertificate *cert = CERT_DecodeCertFromPackage(reinterpret_cast<char *>(n_derArray), n_derLength); + + if (!cert) + { + SAL_WARN("vcl.gdi", "PDF Signing: Error occured, 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) ) ); + + HASHContext *hc = HASH_Create(HASH_AlgSHA1); + + if (!hc) + { + SAL_WARN("vcl.gdi", "PDF Signing: SHA1 HASH_Create failed!"); + return false; + } + + HASH_Begin(hc); + + 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 == osl_readFile( m_aFile, buffer, m_nSignatureContentOffset, &bytesRead ) ) ); + if (bytesRead != (sal_uInt64)m_nSignatureContentOffset) + SAL_WARN("vcl.gdi", "PDF Signing: First buffer read failed!"); + + HASH_Update(hc, reinterpret_cast<const unsigned char*>(buffer), m_nSignatureContentOffset); + delete[] buffer; + + buffer = new char[nLastByteRangeNo + 1]; + CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1) ) ); + CHECK_RETURN( (osl_File_E_None == osl_readFile( m_aFile, buffer, nLastByteRangeNo, &bytesRead ) ) ); + if (bytesRead != (sal_uInt64) nLastByteRangeNo) + SAL_WARN("vcl.gdi", "PDF Signing: Second buffer read failed!"); + + HASH_Update(hc, reinterpret_cast<const unsigned char*>(buffer), nLastByteRangeNo); + delete[] buffer; + + SECItem digest; + unsigned char hash[SHA1_LENGTH]; + digest.data = hash; + HASH_End(hc, digest.data, &digest.len, SHA1_LENGTH); + HASH_Destroy(hc); + + const char *pass = OUStringToOString( m_aContext.SignPassword, RTL_TEXTENCODING_UTF8 ).getStr(); + + //FIXME: Check if password is passed correctly to SEC_PKCS7CreateSignedData function + //TODO: Create PKCS7 object even if the certificate is invalid + SEC_PKCS7ContentInfo *ci = SEC_PKCS7CreateSignedData(cert, certUsageEmailSigner, NULL, SEC_OID_SHA1, &digest, (SECKEYGetPasswordKey)::PDFSigningPKCS7PasswordCallback, (void *)pass); + + if (!ci) + { + SAL_WARN("vcl.gdi", "PDF Signing: PKCS7 Creation failed: " << PORT_GetError()); + return false; + } + + SAL_WARN("vcl.gdi","PKCS7 Object created successfully!"); + + //SEC_PKCS7AddSigningTime(ci); //FIXME: Requires PDF 1.6 ? + + // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object + // Be careful! This is an asynchronous method, so signature may not be ready when the method returns back + CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, m_nSignatureContentOffset) ) ); + SECStatus srv = SEC_PKCS7Encode(ci, (SEC_PKCS7EncoderOutputCallback)::PDFSigningPKCS7Callback, (void *)m_aFile, NULL, (SECKEYGetPasswordKey)::PDFSigningPKCS7PasswordCallback, (void *)pass); + + if (srv != SECSuccess) + { + SAL_WARN("vcl.gdi", "PKCS encoding failed!"); + return false; + } + + SAL_WARN("vcl.gdi","PKCS7 object encoded successfully!"); + + SEC_PKCS7DestroyContentInfo(ci); - // revert the file position back - CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, nOffset ) ) ); + //CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, nOffset ) ) ); return true; } |