summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGökçen Eraslan <gokcen.eraslan@gmail.com>2012-06-19 17:18:52 +0300
committerGökçen Eraslan <gokcen.eraslan@gmail.com>2012-06-19 17:18:52 +0300
commit35373f0ef4b60f5d50b53949db083f33b78974de (patch)
tree0ca8fbbf1a819e2040afeef1a309872a97788b31
parentd633da43995f7457e487634016b2ca3bbc9245b2 (diff)
Add initial support for PDF signing.
PDF signing has still some missing parts like PKCS7 object creation and GUI stuff (like certificate selection). But this basically implements the skeleton of the Signature structure. Change-Id: Ic0c7e4a1b0f79791aec1cc682dfa85ed163330eb
-rw-r--r--vcl/inc/vcl/pdfwriter.hxx19
-rw-r--r--vcl/source/gdi/pdfwriter_impl.cxx165
-rw-r--r--vcl/source/gdi/pdfwriter_impl.hxx9
3 files changed, 185 insertions, 8 deletions
diff --git a/vcl/inc/vcl/pdfwriter.hxx b/vcl/inc/vcl/pdfwriter.hxx
index cb6d271dfe73..99f99d6f6ca2 100644
--- a/vcl/inc/vcl/pdfwriter.hxx
+++ b/vcl/inc/vcl/pdfwriter.hxx
@@ -197,7 +197,8 @@ public:
enum WidgetType
{
- PushButton, RadioButton, CheckBox, Edit, ListBox, ComboBox, Hierarchy
+ PushButton, RadioButton, CheckBox, Edit, ListBox, ComboBox, Hierarchy,
+ Signature
};
enum WidgetState
@@ -449,6 +450,22 @@ public:
}
};
+ struct SignatureWidget: public AnyWidget
+ {
+ rtl::OUString SigLocation;
+ rtl::OUString SigReason;
+ rtl::OUString SigContactInfo;
+
+ SignatureWidget()
+ : AnyWidget( vcl::PDFWriter::Signature )
+ {}
+
+ virtual AnyWidget* Clone() const
+ {
+ return new SignatureWidget( *this );
+ }
+ };
+
enum ExportDataFormat { HTML, XML, FDF, PDF };
// see 3.6.1 of PDF 1.4 ref for details, used for 8.1 PDF v 1.4 ref also
// These emuns are treated as integer while reading/writing to configuration
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index 72201bab2fa1..9e0dbf974702 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -74,6 +74,7 @@
#include <lcms2.h>
#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/util/URLTransformer.hpp>
@@ -97,6 +98,8 @@ using ::rtl::OUStringBuffer;
#define DEBUG_DISABLE_PDFCOMPRESSION // also do not compress streams
#endif
+#define MAX_SIGNATURE_CONTENT_LENGTH 0x4000
+
#ifdef DO_TEST_PDF
class PDFTestOutputStream : public PDFOutputStream
{
@@ -520,6 +523,13 @@ void doTestCode()
aWriter.CreateOutlineItem( nPage2OL, OUString( "Dest 1" ), nFirstDest );
aWriter.EndStructureElement(); // close document
+
+ // sign the document
+ PDFWriter::SignatureWidget aSignature;
+ aSignature.Name = OUString("Signature1");
+ aSignature.Location = Rectangle( Point( 0, 0 ), Size( 0, 0 ) );
+ aWriter.CreateControl( aSignature, 0);
+
aWriter.Emit();
}
#endif
@@ -1755,6 +1765,9 @@ void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal
m_eInheritedOrientation( PDFWriter::Portrait ),
m_nCurrentPage( -1 ),
m_nResourceDict( -1 ),
+ m_nSignatureObject( -1 ),
+ m_nSignatureContentOffset( 0 ),
+ m_nSignatureLastByteRangeNoOffset( 0 ),
m_nFontDictObject( -1 ),
m_pCodec( NULL ),
m_aDocDigest( rtl_digest_createMD5() ),
@@ -5452,15 +5465,29 @@ bool PDFWriterImpl::emitWidgetAnnotations()
// emit widget annotation only for terminal fields
if( rWidget.m_aKids.empty() )
{
- aLine.append( "/Type/Annot/Subtype/Widget/F 4\n"
- "/Rect[" );
- appendFixedInt( rWidget.m_aRect.Left()-1, aLine );
+ int iRectMargin;
+
+ aLine.append( "/Type/Annot/Subtype/Widget/F " );
+
+ if (rWidget.m_eType == PDFWriter::Signature)
+ {
+ aLine.append( "132\n" ); // Print & Locked
+ iRectMargin = 0;
+ }
+ else
+ {
+ aLine.append( "4\n" );
+ iRectMargin = 1;
+ }
+
+ aLine.append("/Rect[" );
+ appendFixedInt( rWidget.m_aRect.Left()-iRectMargin, aLine );
aLine.append( ' ' );
- appendFixedInt( rWidget.m_aRect.Top()+1, aLine );
+ appendFixedInt( rWidget.m_aRect.Top()+iRectMargin, aLine );
aLine.append( ' ' );
- appendFixedInt( rWidget.m_aRect.Right()+1, aLine );
+ appendFixedInt( rWidget.m_aRect.Right()+iRectMargin, aLine );
aLine.append( ' ' );
- appendFixedInt( rWidget.m_aRect.Bottom()-1, aLine );
+ appendFixedInt( rWidget.m_aRect.Bottom()-iRectMargin, aLine );
aLine.append( "]\n" );
}
aLine.append( "/FT/" );
@@ -5515,6 +5542,10 @@ bool PDFWriterImpl::emitWidgetAnnotations()
aLine.append( "Tx" );
appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
break;
+ case PDFWriter::Signature:
+ aLine.append( "Sig" );
+ aValue.append(OUStringToOString(rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US));
+ break;
case PDFWriter::Hierarchy: // make the compiler happy
break;
}
@@ -5877,6 +5908,14 @@ bool PDFWriterImpl::emitCatalog()
}
else
aInitPageRef.append( "0" );
+
+ if (m_nSignatureObject != -1) // Document will be signed
+ {
+ aLine.append("/Perms<</DocMDP ");
+ aLine.append(m_nSignatureObject);
+ aLine.append(" 0 R>>");
+ }
+
switch( m_aContext.PDFDocumentAction )
{
case PDFWriter::ActionDefault : //do nothing, this is the Acrobat default
@@ -6001,7 +6040,12 @@ bool PDFWriterImpl::emitCatalog()
aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " );
}
}
- aLine.append( "\n]/DR " );
+ aLine.append( "\n]" );
+
+ if (m_nSignatureObject != -1)
+ aLine.append( "/SigFlags 3");
+
+ aLine.append( "/DR " );
aLine.append( getResourceDictObj() );
aLine.append( " 0 R" );
if( m_bIsPDF_A1 )
@@ -6031,6 +6075,99 @@ bool PDFWriterImpl::emitCatalog()
return true;
}
+bool PDFWriterImpl::emitSignature()
+{
+ if( !updateObject( m_nSignatureObject ) )
+ return false;
+
+ OStringBuffer aLine( 0x5000 );
+ aLine.append( m_nSignatureObject );
+ aLine.append( " 0 obj\n" );
+ aLine.append("<</Reference[<</Data ");
+ aLine.append( m_nCatalogObject );
+ aLine.append(" 0 R/Type/SigRef/TransformParams<</Type/TransformParams"
+ "/V/1.2/P 1>>/DigestMethod/MD5/DigestLocation[0 0]"
+ "/DigestValue(aa)/TransformMethod/DocMDP>>]/Contents <" );
+
+ sal_uInt64 nOffset = ~0U;
+ oslFileError aError = osl_getFilePos( m_aFile, &nOffset );
+ DBG_ASSERT( aError == osl_File_E_None, "could not get file position" );
+
+ m_nSignatureContentOffset = nOffset + aLine.getLength();
+
+ // reserve some space for the PKCS#7 object
+ 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/Location()"
+ "/Name ");
+
+ if( m_aContext.DocumentInfo.Author.Len() )
+ appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, m_nSignatureObject, aLine );
+ else
+ aLine.append("()");
+
+ aLine.append( " /M ");
+ appendLiteralStringEncrypt( m_aCreationDateString, m_nSignatureObject, aLine );
+
+ aLine.append( " /ByteRange [ 0 ");
+ aLine.append( m_nSignatureContentOffset, 10 );
+ aLine.append( " " );
+ aLine.append( m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH, 10 );
+ aLine.append( " " );
+
+ m_nSignatureLastByteRangeNoOffset = nOffset + aLine.getLength();
+
+ // mark the last ByteRange no and add some space. Now, we don't know
+ // how many bytes we need for this ByteRange value
+ // The real value will be overwritten in the finalizeSignature method
+ OStringBuffer aByteRangeFiller( 100 );
+ comphelper::string::padToLength(aByteRangeFiller, 100, ' ');
+ aLine.append( aByteRangeFiller.makeStringAndClear() );
+ aLine.append(" /Filter/Adobe.PPKMS/Reason()>>"
+ "\nendobj\n\n" );
+
+ if (!writeBuffer( aLine.getStr(), aLine.getLength() ))
+ return false;
+
+ return true;
+}
+
+bool PDFWriterImpl::finalizeSignature()
+{
+ // 1- calculate last ByteRange value
+ sal_uInt64 nOffset = ~0U;
+ oslFileError aError = osl_getFilePos( m_aFile, &nOffset );
+
+ if ( aError != osl_File_E_None )
+ return false;
+
+ sal_Int64 nLastByteRangeNo = nOffset - (m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH);
+
+ // 2- overwrite the value to the m_nSignatureLastByteRangeNoOffset position
+ sal_uInt64 nWritten = 0;
+ osl_setFilePos( m_aFile, osl_Pos_Absolut, m_nSignatureLastByteRangeNoOffset );
+ OStringBuffer aByteRangeNo( 256 );
+ aByteRangeNo.append( nLastByteRangeNo, 10);
+ aByteRangeNo.append( " ]" );
+
+ if( osl_writeFile( m_aFile, aByteRangeNo.getStr(), aByteRangeNo.getLength(), &nWritten ) != osl_File_E_None )
+ {
+ osl_setFilePos( m_aFile, osl_Pos_Absolut, nOffset );
+ return false;
+ }
+
+ // 3- create the PKCS#7 object using NSS
+
+ // 4- overwrite the PKCS7 content to the m_nSignatureContentOffset
+ osl_setFilePos( m_aFile, osl_Pos_Absolut, m_nSignatureContentOffset );
+ // osl_writeFile()
+
+ // revert the file position back
+ osl_setFilePos( m_aFile, osl_Pos_Absolut, nOffset );
+ return true;
+}
+
sal_Int32 PDFWriterImpl::emitInfoDict( )
{
sal_Int32 nObject = createObject();
@@ -6818,9 +6955,15 @@ bool PDFWriterImpl::emit()
// emit catalog
CHECK_RETURN( emitCatalog() );
+ if (m_nSignatureObject != -1) // if document is signed, emit sigdict
+ CHECK_RETURN( emitSignature() );
+
// emit trailer
CHECK_RETURN( emitTrailer() );
+ if (m_nSignatureObject != -1) // finalize the signature
+ CHECK_RETURN( finalizeSignature() );
+
osl_closeFile( m_aFile );
m_bOpen = false;
@@ -11722,6 +11865,14 @@ sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sa
createDefaultEditAppearance( rNewWidget, rEdit );
}
+ else if( rControl.getType() == PDFWriter::Signature)
+ {
+ //const PDFWriter::SignatureWidget& rSig = static_cast<const PDFWriter::SignatureWidget&>(rControl);
+ m_nSignatureObject = createObject();
+ rNewWidget.m_aValue = OUString::valueOf( m_nSignatureObject );
+ rNewWidget.m_aValue += OUString(" 0 R");
+ //createDefaultSignatureAppearance( rNewWidget, rSig );
+ }
// convert to default user space now, since the mapmode may change
// note: create default appearances before m_aRect gets transformed
diff --git a/vcl/source/gdi/pdfwriter_impl.hxx b/vcl/source/gdi/pdfwriter_impl.hxx
index 8e1ba1d1d7d7..ac9427579c42 100644
--- a/vcl/source/gdi/pdfwriter_impl.hxx
+++ b/vcl/source/gdi/pdfwriter_impl.hxx
@@ -678,6 +678,10 @@ private:
sal_Int32 m_nCurrentPage;
sal_Int32 m_nCatalogObject;
+ // object number of the main signature dictionary
+ sal_Int32 m_nSignatureObject;
+ sal_Int64 m_nSignatureContentOffset;
+ sal_Int64 m_nSignatureLastByteRangeNoOffset;
sal_Int32 m_nResourceDict;
ResourceDict m_aGlobalResourceDict;
sal_Int32 m_nFontDictObject;
@@ -950,6 +954,11 @@ i12626
sal_Int32 emitStructParentTree( sal_Int32 nTreeObject );
// writes page tree and catalog
bool emitCatalog();
+ // writes signature dictionary object
+ bool emitSignature();
+ // creates a PKCS7 object using the ByteRange and overwrite /Contents
+ // of the signature dictionary
+ bool finalizeSignature();
// writes xref and trailer
bool emitTrailer();
// emit additional streams collected; also create there object numbers