diff options
author | Miklos Vajna <vmiklos@collabora.co.uk> | 2016-11-23 11:27:32 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.co.uk> | 2016-11-29 08:08:50 +0000 |
commit | d02c3ba8c5e723561edb694e7ed8b2f2c33604af (patch) | |
tree | b7ada52cc1c914f7d94028f8ddc5a0bac5ead847 /xmlsecurity | |
parent | b992da28c595f40a75ddf3237edec10d73e56d91 (diff) |
vcl mscrypto PDF sign: bring it up to date with NSS, part 1
This is a combination of 6 commits:
1) vcl mscrypto PDF sign: add initial 'signing-certificate' signed attribute
Equivalent of the earlier NSS commit, payload is just an empty sequence
at the moment.
(cherry picked from commit cb851cbb09adc637bb6e8095050292f7a8c6a7b1)
2) vcl mscrypto PDF sign: write ESSCertIDv2
With this, the value of signing-certificate conforms to the RFC on
Windows as well.
(cherry picked from commit b12410f212658996fdb5fb291a06038e9ac39b2e)
3) xmlsecurity mscrypto PDF sign: conditionally add back CAdES SubFilter
We can now write that on Windows as well when requested, after the
signing-certificate attribute is implemented using mscrypto.
With this, the PAdES validator at
<http://signatures-conformance-checker.etsi.org/protected/upload.php?sigtype=padesconf>
finds our Windows signature valid.
(cherry picked from commit 8a279d7de4cf94c99f655f6edd0da0c24ab4003c)
4) CppunitTest_xmlsecurity_signing: don't assume we always have a signing cert
This makes this suite in sync with CppunitTest_xmlsecurity_pdfsigning. A
signing certificate is available on 64bit NSS platforms, as there we
provide a pre-created NSS db, but on other platforms by default there is
just no signing certificate. The certificate.crt I added earlier is not
enough, that's just the certificate, but it doesn't provide a private
key.
(cherry picked from commit 748f778d0f42f2cbb78a7ca7e013bfbd77cdf2b7)
5) CppunitTest_xmlsecurity_signing: add XAdES testcase
Assert the two user-visible changes: SHA-256 hashes and the digest of
the signing certificate.
(cherry picked from commit 426495cb441e6a83cd0d1f74b0ddf656322815b5)
6) CppunitTest_xmlsecurity_pdfsigning: add PAdES testcase
Assert the two user-visible changes: SHA-256 hashes and the SubFilter of the
signature.
(cherry picked from commit 5cb580144c286117db485e605c79ce1139cb94fb)
Change-Id: I12a2355e2ddfc368bed4430a7b5ad244b5778afe
Reviewed-on: https://gerrit.libreoffice.org/31316
Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk>
Tested-by: Miklos Vajna <vmiklos@collabora.co.uk>
Diffstat (limited to 'xmlsecurity')
-rw-r--r-- | xmlsecurity/CppunitTest_xmlsecurity_signing.mk | 1 | ||||
-rw-r--r-- | xmlsecurity/inc/pdfio/pdfdocument.hxx | 66 | ||||
-rw-r--r-- | xmlsecurity/inc/sigstruct.hxx | 3 | ||||
-rw-r--r-- | xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx | 28 | ||||
-rw-r--r-- | xmlsecurity/qa/unit/signing/data/certificate.crt | 27 | ||||
-rw-r--r-- | xmlsecurity/qa/unit/signing/data/key3.db | bin | 0 -> 16384 bytes | |||
-rw-r--r-- | xmlsecurity/qa/unit/signing/signing.cxx | 94 | ||||
-rw-r--r-- | xmlsecurity/source/pdfio/pdfdocument.cxx | 74 |
8 files changed, 169 insertions, 124 deletions
diff --git a/xmlsecurity/CppunitTest_xmlsecurity_signing.mk b/xmlsecurity/CppunitTest_xmlsecurity_signing.mk index 24713cbe8e24..f7bbb0e83753 100644 --- a/xmlsecurity/CppunitTest_xmlsecurity_signing.mk +++ b/xmlsecurity/CppunitTest_xmlsecurity_signing.mk @@ -32,6 +32,7 @@ $(eval $(call gb_CppunitTest_use_libraries,xmlsecurity_signing, \ $(eval $(call gb_CppunitTest_use_externals,xmlsecurity_signing,\ boost_headers \ + libxml2 \ )) $(eval $(call gb_CppunitTest_set_include,xmlsecurity_signing,\ diff --git a/xmlsecurity/inc/pdfio/pdfdocument.hxx b/xmlsecurity/inc/pdfio/pdfdocument.hxx index 31a0546deb38..e2f2913e863c 100644 --- a/xmlsecurity/inc/pdfio/pdfdocument.hxx +++ b/xmlsecurity/inc/pdfio/pdfdocument.hxx @@ -27,9 +27,12 @@ namespace pdfio { class PDFTrailerElement; -class PDFObjectElement; class PDFHexStringElement; class PDFReferenceElement; +class PDFDocument; +class PDFDictionaryElement; +class PDFArrayElement; +class PDFStreamElement; /// A byte range in a PDF file. class PDFElement @@ -39,6 +42,67 @@ public: virtual ~PDFElement() { } }; +/// Indirect object: something with a unique ID. +class XMLSECURITY_DLLPUBLIC PDFObjectElement : public PDFElement +{ + PDFDocument& m_rDoc; + double m_fObjectValue; + double m_fGenerationValue; + std::map<OString, PDFElement*> m_aDictionary; + /// Position after the '<<' token. + sal_uInt64 m_nDictionaryOffset; + /// Length of the dictionary buffer till (before) the '<<' token. + sal_uInt64 m_nDictionaryLength; + PDFDictionaryElement* m_pDictionaryElement; + /// The contained direct array, if any. + PDFArrayElement* m_pArrayElement; + /// The stream of this object, used when this is an object stream. + PDFStreamElement* m_pStreamElement; + /// Objects of an object stream. + std::vector< std::unique_ptr<PDFObjectElement> > m_aStoredElements; + /// Elements of an object in an object stream. + std::vector< std::unique_ptr<PDFElement> > m_aElements; + /// Uncompressed buffer of an object in an object stream. + std::unique_ptr<SvMemoryStream> m_pStreamBuffer; + +public: + PDFObjectElement(PDFDocument& rDoc, double fObjectValue, double fGenerationValue); + bool Read(SvStream& rStream) override; + PDFElement* Lookup(const OString& rDictionaryKey); + PDFObjectElement* LookupObject(const OString& rDictionaryKey); + double GetObjectValue() const; + void SetDictionaryOffset(sal_uInt64 nDictionaryOffset); + sal_uInt64 GetDictionaryOffset(); + void SetDictionaryLength(sal_uInt64 nDictionaryLength); + sal_uInt64 GetDictionaryLength(); + PDFDictionaryElement* GetDictionary() const; + void SetDictionary(PDFDictionaryElement* pDictionaryElement); + void SetArray(PDFArrayElement* pArrayElement); + void SetStream(PDFStreamElement* pStreamElement); + PDFArrayElement* GetArray() const; + /// Parse objects stored in this object stream. + void ParseStoredObjects(); + std::vector< std::unique_ptr<PDFElement> >& GetStoredElements(); + SvMemoryStream* GetStreamBuffer() const; + void SetStreamBuffer(std::unique_ptr<SvMemoryStream>& pStreamBuffer); +}; + +/// Name object: a key string. +class XMLSECURITY_DLLPUBLIC PDFNameElement : public PDFElement +{ + OString m_aValue; + /// Offset after the '/' token. + sal_uInt64 m_nLocation; + /// Length till the next token start. + sal_uInt64 m_nLength; +public: + PDFNameElement(); + bool Read(SvStream& rStream) override; + const OString& GetValue() const; + sal_uInt64 GetLocation() const; + sal_uInt64 GetLength() const; +}; + enum class TokenizeMode { /// Full file. diff --git a/xmlsecurity/inc/sigstruct.hxx b/xmlsecurity/inc/sigstruct.hxx index 6dd4f7f206e2..ab455d555953 100644 --- a/xmlsecurity/inc/sigstruct.hxx +++ b/xmlsecurity/inc/sigstruct.hxx @@ -102,11 +102,14 @@ struct SignatureInformation OUString ouCertDigest; /// A full OOXML signguature for unchanged roundtrip, empty for ODF. css::uno::Sequence<sal_Int8> aSignatureBytes; + /// For PDF: digest format, from css::xml::crypto::DigestID + sal_Int32 nDigestID; SignatureInformation( sal_Int32 nId ) { nSecurityId = nId; nStatus = css::xml::crypto::SecurityOperationStatus_UNKNOWN; + nDigestID = 0; } }; diff --git a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx index 99e176b03b07..4d0ce52c3f7f 100644 --- a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx +++ b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx @@ -40,7 +40,7 @@ class PDFSigningTest : public test::BootstrapFixture * Read a pdf and make sure that it has the expected number of valid * signatures. */ - std::vector<SignatureInformation> verify(const OUString& rURL, size_t nCount); + std::vector<SignatureInformation> verify(const OUString& rURL, size_t nCount, const OString& rExpectedSubFilter); public: PDFSigningTest(); @@ -98,7 +98,7 @@ void PDFSigningTest::setUp() #endif } -std::vector<SignatureInformation> PDFSigningTest::verify(const OUString& rURL, size_t nCount) +std::vector<SignatureInformation> PDFSigningTest::verify(const OUString& rURL, size_t nCount, const OString& rExpectedSubFilter) { uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(mxComponentContext); uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString()); @@ -115,6 +115,15 @@ std::vector<SignatureInformation> PDFSigningTest::verify(const OUString& rURL, s bool bLast = i == aSignatures.size() - 1; CPPUNIT_ASSERT(xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[i], aInfo, bLast)); aRet.push_back(aInfo); + + if (!rExpectedSubFilter.isEmpty()) + { + xmlsecurity::pdfio::PDFObjectElement* pValue = aSignatures[i]->LookupObject("V"); + CPPUNIT_ASSERT(pValue); + auto pSubFilter = dynamic_cast<xmlsecurity::pdfio::PDFNameElement*>(pValue->Lookup("SubFilter")); + CPPUNIT_ASSERT(pSubFilter); + CPPUNIT_ASSERT_EQUAL(rExpectedSubFilter, pSubFilter->GetValue()); + } } return aRet; @@ -148,7 +157,7 @@ bool PDFSigningTest::sign(const OUString& rInURL, const OUString& rOutURL, size_ } // This was nOriginalSignatureCount when PDFDocument::Sign() silently returned success, without doing anything. - verify(rOutURL, nOriginalSignatureCount + 1); + verify(rOutURL, nOriginalSignatureCount + 1, /*rExpectedSubFilter=*/OString()); return true; } @@ -163,11 +172,14 @@ void PDFSigningTest::testPDFAdd() if (bHadCertificates) { + // Assert that the SubFilter is not adbe.pkcs7.detached in the bAdES case. + std::vector<SignatureInformation> aInfos = verify(aOutURL, 1, "ETSI.CAdES.detached"); // Make sure the timestamp is correct. - std::vector<SignatureInformation> aInfos = verify(aOutURL, 1); DateTime aDateTime(DateTime::SYSTEM); // This was 0 (on Windows), as neither the /M key nor the PKCS#7 blob contained a timestamp. CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(aDateTime.GetYear()), aInfos[0].stDateTime.Year); + // Assert that the digest algorithm is not SHA-1 in the bAdES case. + CPPUNIT_ASSERT_EQUAL(xml::crypto::DigestID::SHA256, aInfos[0].nDigestID); } } @@ -218,7 +230,7 @@ void PDFSigningTest::testPDFRemove() // Read back the pdf and make sure that it no longer has signatures. // This failed when PDFDocument::RemoveSignature() silently returned // success, without doing anything. - verify(aOutURL, 0); + verify(aOutURL, 0, /*rExpectedSubFilter=*/OString()); } void PDFSigningTest::testPDFRemoveAll() @@ -259,7 +271,7 @@ void PDFSigningTest::testPDF14Adobe() // Two signatures, first is SHA1, the second is SHA256. // This was 0, as we failed to find the Annots key's value when it was a // reference-to-array, not an array. - std::vector<SignatureInformation> aInfos = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf14adobe.pdf", 2); + std::vector<SignatureInformation> aInfos = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf14adobe.pdf", 2, /*rExpectedSubFilter=*/OString()); // This was 0, out-of-PKCS#7 signature date wasn't read. CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(2016), aInfos[1].stDateTime.Year); } @@ -270,7 +282,7 @@ void PDFSigningTest::testPDF16Adobe() // stream with a predictor. And a valid signature. // Found signatures was 0, as parsing failed due to lack of support for // these features. - verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf16adobe.pdf", 1); + verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf16adobe.pdf", 1, /*rExpectedSubFilter=*/OString()); } void PDFSigningTest::testPDF16Add() @@ -299,7 +311,7 @@ void PDFSigningTest::testPDF14LOWin() // algorithm when it meant SEC_OID_SHA1, make sure we tolerate that on all // platforms. // This failed, as NSS HASH_Create() didn't handle the sign algorithm. - verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf14lowin.pdf", 1); + verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf14lowin.pdf", 1, /*rExpectedSubFilter=*/OString()); } CPPUNIT_TEST_SUITE_REGISTRATION(PDFSigningTest); diff --git a/xmlsecurity/qa/unit/signing/data/certificate.crt b/xmlsecurity/qa/unit/signing/data/certificate.crt deleted file mode 100644 index f3f34b7c65ab..000000000000 --- a/xmlsecurity/qa/unit/signing/data/certificate.crt +++ /dev/null @@ -1,27 +0,0 @@ -MIIE7jCCAtagAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwVzELMAkGA1UEBhMCVUsx -EDAOBgNVBAgMB0VuZ2xhbmQxEjAQBgNVBAoMCVRTQ1AgVGVzdDEiMCAGA1UEAwwZ -VFNDUCBJbnRlcm1lZGlhdGUgUm9vdCBDQTAeFw0xNTEyMTgwNzU4MTlaFw0xNjEy -MjcwNzU4MTlaMFUxCzAJBgNVBAYTAlVLMRAwDgYDVQQIDAdFbmdsYW5kMRIwEAYD -VQQKDAlUU0NQIFRlc3QxIDAeBgNVBAMMF1RTQ1AgVGVzdCBleGFtcGxlIEFsaWNl -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3m2YNdX+nc1LkhlrNrcI -PI3yCWnv0/0k9zDKpKiwjMH4vjWM46M6ptAiupxVpAMW5ojnhEyxaNHvZNsCwddY -E6778hut2SJvz0szSBuHUuedcALI2EhVwdM0yLqfGo6WGeOIBDId49TemdNCMhk2 -zOpb1BqYhKls0LfdbxT/an3JaDmmLhPjvgYMJNYVX86L199OQFLJ1zLqQ0YirkKq -XL9cSPmyYBKjgnqQ4Z5YfPL63EP0TsEfa5oQmy/0gS5FB2Wz9CqIptB130v0GR4X -ObTpOkhPFfC5RDBFTMZoi4NCK10wn2NCbr7qZ3aMrOlfeKbsNIifwu0KYFHXyxL5 -AwIDAQABo4HFMIHCMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgWgMDMGCWCG -SAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBDbGllbnQgQ2VydGlmaWNhdGUw -HQYDVR0OBBYEFCL6DzsuAbni8475Z+HkX5tv8iiWMB8GA1UdIwQYMBaAFMuejS1r -WjUf3x1+2QbPSVpuXFl+MA4GA1UdDwEB/wQEAwIF4DAdBgNVHSUEFjAUBggrBgEF -BQcDAgYIKwYBBQUHAwQwDQYJKoZIhvcNAQELBQADggIBAFs0DeCDjttHQ0UHsYcn -hfBCWRdOFdIr3F/IEbN2BL/grScGXoXRaYMIQJv/s5dKgZIuH7xMCVKazoftPVqU -4bOEduAv0IJ6hQF/wEMBueA0UjvQQVYZgsOALi7TD3gYpFqYcH2Wfx5/5Ln6dllL -8UsHoP+6gSLaYwjJd7FQ+IlNTzR65dRMLoJhoKqqyuM6cf/PM8sbK2NH2r8toypj -fPixvD/w3wP7xn4oo/IGXcRK4DTHBF/rSMqeR6ePwXm5tVHrQBfnxN3dsGsXkQgq -zBvvbPY0raraO4CPR7mZp4GVFHOsUNh5TI1SlfxWZ49HU3F5jWeiI9jPuw1RmuAy -ZdFEt403Wi67v6revXe1By6UqIZjq3b2pJGBKZH+60P1cJScawzrN8pi1qQFV8Ji -iJM6/MSciqplTT5F7SG0XZx1CjnBz5rMdYNhI9NNtF3oy9Xy9RvgYehFaC43ZlBB -UMDmZFj5a78hOOkkq1UnrHUdeXyWhiEFzv5d8My2i0kWGq8r0HuC25BmOa17lHVx -Q2o7Rdu9jDFP9oNizC7kQfA5QVRTfBFcWH7jml69RmVgfM+X+wdQgen9hJAILhBz -mDfeteJ5ZEaoEYtw3isOGkpSyg7odjgYq7I+bOiN1toDg07vzfIkvF9KxlkDeRLX -bmcFIvQsqFeF6cUwlZQYLOHA diff --git a/xmlsecurity/qa/unit/signing/data/key3.db b/xmlsecurity/qa/unit/signing/data/key3.db Binary files differnew file mode 100644 index 000000000000..8ab32c28d584 --- /dev/null +++ b/xmlsecurity/qa/unit/signing/data/key3.db diff --git a/xmlsecurity/qa/unit/signing/signing.cxx b/xmlsecurity/qa/unit/signing/signing.cxx index 51e536614ad6..5bcf0c26f441 100644 --- a/xmlsecurity/qa/unit/signing/signing.cxx +++ b/xmlsecurity/qa/unit/signing/signing.cxx @@ -15,6 +15,7 @@ #include <test/bootstrapfixture.hxx> #include <unotest/macros_test.hxx> +#include <test/xmltesttools.hxx> #include <com/sun/star/document/XStorageBasedDocument.hpp> #include <com/sun/star/embed/XStorage.hpp> @@ -52,7 +53,7 @@ const char* DATA_DIRECTORY = "/xmlsecurity/qa/unit/signing/data/"; } /// Testsuite for the document signing feature. -class SigningTest : public test::BootstrapFixture, public unotest::MacrosTest +class SigningTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools { uno::Reference<uno::XComponentContext> mxComponentContext; uno::Reference<lang::XComponent> mxComponent; @@ -61,6 +62,7 @@ public: SigningTest(); virtual void setUp() override; virtual void tearDown() override; + void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override; void testDescription(); /// Test a typical ODF where all streams are signed. @@ -88,6 +90,7 @@ public: #endif void test96097Calc(); void test96097Doc(); + void testXAdES(); CPPUNIT_TEST_SUITE(SigningTest); CPPUNIT_TEST(testDescription); @@ -107,6 +110,7 @@ public: #endif CPPUNIT_TEST(test96097Calc); CPPUNIT_TEST(test96097Doc); + CPPUNIT_TEST(testXAdES); CPPUNIT_TEST_SUITE_END(); private: @@ -132,6 +136,7 @@ void SigningTest::setUp() OUString aTargetDir = m_directories.getURLFromWorkdir( "/CppunitTest/xmlsecurity_signing.test.user/"); osl::File::copy(aSourceDir + "cert8.db", aTargetDir + "cert8.db"); + osl::File::copy(aSourceDir + "key3.db", aTargetDir + "key3.db"); OUString aTargetPath; osl::FileBase::getSystemPathFromFileURL(aTargetDir, aTargetPath); setenv("MOZILLA_CERTIFICATE_FOLDER", aTargetPath.toUtf8().getStr(), 1); @@ -168,20 +173,14 @@ void SigningTest::createCalc(const OUString& rURL) uno::Reference<security::XCertificate> SigningTest::getCertificate(DocumentSignatureManager& rSignatureManager) { + uno::Reference<security::XCertificate> xCertificate; + uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = rSignatureManager.getSecurityEnvironment(); - OUString aCertificate; - { - SvFileStream aStream(m_directories.getURLFromSrc(DATA_DIRECTORY) + "certificate.crt", StreamMode::READ); - OString aLine; - bool bMore = aStream.ReadLine(aLine); - while (bMore) - { - aCertificate += OUString::fromUtf8(aLine); - aCertificate += "\n"; - bMore = aStream.ReadLine(aLine); - } - } - return xSecurityEnvironment->createCertificateFromAscii(aCertificate); + uno::Sequence<uno::Reference<security::XCertificate>> aCertificates = xSecurityEnvironment->getPersonalCertificates(); + if (!aCertificates.hasElements()) + return xCertificate; + + return aCertificates[0]; } void SigningTest::testDescription() @@ -205,7 +204,8 @@ void SigningTest::testDescription() // Then add a signature document. uno::Reference<security::XCertificate> xCertificate = getCertificate(aManager); - CPPUNIT_ASSERT(xCertificate.is()); + if (!xCertificate.is()) + return; OUString aDescription("SigningTest::testDescription"); sal_Int32 nSecurityId; aManager.add(xCertificate, aDescription, nSecurityId, false); @@ -238,7 +238,8 @@ void SigningTest::testOOXMLDescription() // Then add a document signature. uno::Reference<security::XCertificate> xCertificate = getCertificate(aManager); - CPPUNIT_ASSERT(xCertificate.is()); + if (!xCertificate.is()) + return; OUString aDescription("SigningTest::testDescription"); sal_Int32 nSecurityId; aManager.add(xCertificate, aDescription, nSecurityId, false); @@ -271,7 +272,8 @@ void SigningTest::testOOXMLAppend() // Then add a second document signature. uno::Reference<security::XCertificate> xCertificate = getCertificate(aManager); - CPPUNIT_ASSERT(xCertificate.is()); + if (!xCertificate.is()) + return; sal_Int32 nSecurityId; aManager.add(xCertificate, OUString(), nSecurityId, false); @@ -297,7 +299,8 @@ void SigningTest::testOOXMLRemove() // Then remove the last added signature. uno::Reference<security::XCertificate> xCertificate = getCertificate(aManager); - CPPUNIT_ASSERT(xCertificate.is()); + if (!xCertificate.is()) + return; aManager.remove(0); // Read back the signatures and make sure that only purpose1 is left. @@ -327,7 +330,8 @@ void SigningTest::testOOXMLRemoveAll() // Then remove the only signature in the document. uno::Reference<security::XCertificate> xCertificate = getCertificate(aManager); - CPPUNIT_ASSERT(xCertificate.is()); + if (!xCertificate.is()) + return; aManager.remove(0); aManager.read(/*bUseTempStream=*/true); aManager.write(/*bXAdESCompliantIfODF=*/false); @@ -542,6 +546,58 @@ void SigningTest::test96097Doc() } } +void SigningTest::testXAdES() +{ + // Create an empty document, store it to a tempfile and load it as a storage. + createDoc(OUString()); + + utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("writer8"); + xStorable->storeAsURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList()); + + DocumentSignatureManager aManager(mxComponentContext, DocumentSignatureMode::Content); + CPPUNIT_ASSERT(aManager.init()); + uno::Reference <embed::XStorage> xStorage = comphelper::OStorageHelper::GetStorageOfFormatFromURL(ZIP_STORAGE_FORMAT_STRING, aTempFile.GetURL(), embed::ElementModes::READWRITE); + CPPUNIT_ASSERT(xStorage.is()); + aManager.mxStore = xStorage; + aManager.maSignatureHelper.SetStorage(xStorage, "1.2"); + + // Create a signature. + uno::Reference<security::XCertificate> xCertificate = getCertificate(aManager); + if (!xCertificate.is()) + return; + sal_Int32 nSecurityId; + aManager.add(xCertificate, /*rDescription=*/OUString(), nSecurityId, /*bAdESCompliant=*/true); + + // Write to storage. + aManager.read(/*bUseTempStream=*/true); + aManager.write(/*bXAdESCompliantIfODF=*/true); + uno::Reference<embed::XTransactedObject> xTransactedObject(xStorage, uno::UNO_QUERY); + xTransactedObject->commit(); + + // Parse the resulting XML. + uno::Reference<embed::XStorage> xMetaInf = xStorage->openStorageElement("META-INF", embed::ElementModes::READ); + uno::Reference<io::XInputStream> xInputStream(xMetaInf->openStreamElement("documentsignatures.xml", embed::ElementModes::READ), uno::UNO_QUERY); + std::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true)); + xmlDocPtr pXmlDoc = parseXmlStream(pStream.get()); + + // Assert that the digest algorithm is SHA-256 in the bAdESCompliant case, not SHA-1. + assertXPath(pXmlDoc, "/odfds:document-signatures/dsig:Signature/dsig:SignedInfo/dsig:Reference[@URI='content.xml']/dsig:DigestMethod", "Algorithm", ALGO_XMLDSIGSHA256); + + // Assert that the digest of the signing certificate is included. + assertXPath(pXmlDoc, "//xd:CertDigest", 1); +} + +void SigningTest::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) +{ + xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("odfds"), BAD_CAST("urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0")); + xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("dsig"), BAD_CAST("http://www.w3.org/2000/09/xmldsig#")); + xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("xd"), BAD_CAST("http://uri.etsi.org/01903/v1.3.2#")); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SigningTest); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx index ef9900c13f3b..29b4a026cf2b 100644 --- a/xmlsecurity/source/pdfio/pdfdocument.cxx +++ b/xmlsecurity/source/pdfio/pdfdocument.cxx @@ -54,7 +54,6 @@ namespace pdfio const int MAX_SIGNATURE_CONTENT_LENGTH = 50000; class PDFTrailerElement; -class PDFObjectElement; /// A one-liner comment. class PDFCommentElement : public PDFElement @@ -85,54 +84,6 @@ public: }; class PDFReferenceElement; -class PDFDictionaryElement; -class PDFArrayElement; -class PDFStreamElement; - -/// Indirect object: something with a unique ID. -class PDFObjectElement : public PDFElement -{ - PDFDocument& m_rDoc; - double m_fObjectValue; - double m_fGenerationValue; - std::map<OString, PDFElement*> m_aDictionary; - /// Position after the '<<' token. - sal_uInt64 m_nDictionaryOffset; - /// Length of the dictionary buffer till (before) the '<<' token. - sal_uInt64 m_nDictionaryLength; - PDFDictionaryElement* m_pDictionaryElement; - /// The contained direct array, if any. - PDFArrayElement* m_pArrayElement; - /// The stream of this object, used when this is an object stream. - PDFStreamElement* m_pStreamElement; - /// Objects of an object stream. - std::vector< std::unique_ptr<PDFObjectElement> > m_aStoredElements; - /// Elements of an object in an object stream. - std::vector< std::unique_ptr<PDFElement> > m_aElements; - /// Uncompressed buffer of an object in an object stream. - std::unique_ptr<SvMemoryStream> m_pStreamBuffer; - -public: - PDFObjectElement(PDFDocument& rDoc, double fObjectValue, double fGenerationValue); - bool Read(SvStream& rStream) override; - PDFElement* Lookup(const OString& rDictionaryKey); - PDFObjectElement* LookupObject(const OString& rDictionaryKey); - double GetObjectValue() const; - void SetDictionaryOffset(sal_uInt64 nDictionaryOffset); - sal_uInt64 GetDictionaryOffset(); - void SetDictionaryLength(sal_uInt64 nDictionaryLength); - sal_uInt64 GetDictionaryLength(); - PDFDictionaryElement* GetDictionary() const; - void SetDictionary(PDFDictionaryElement* pDictionaryElement); - void SetArray(PDFArrayElement* pArrayElement); - void SetStream(PDFStreamElement* pStreamElement); - PDFArrayElement* GetArray() const; - /// Parse objects stored in this object stream. - void ParseStoredObjects(); - std::vector< std::unique_ptr<PDFElement> >& GetStoredElements(); - SvMemoryStream* GetStreamBuffer() const; - void SetStreamBuffer(std::unique_ptr<SvMemoryStream>& pStreamBuffer); -}; /// Dictionary object: a set key-value pairs. class PDFDictionaryElement : public PDFElement @@ -170,22 +121,6 @@ public: sal_uInt64 GetLocation() const; }; -/// Name object: a key string. -class PDFNameElement : public PDFElement -{ - OString m_aValue; - /// Offset after the '/' token. - sal_uInt64 m_nLocation; - /// Length till the next token start. - sal_uInt64 m_nLength; -public: - PDFNameElement(); - bool Read(SvStream& rStream) override; - const OString& GetValue() const; - sal_uInt64 GetLocation() const; - sal_uInt64 GetLength() const; -}; - /// Reference object: something with a unique ID. class PDFReferenceElement : public PDFElement { @@ -375,13 +310,9 @@ sal_Int32 PDFDocument::WriteSignatureObject(const OUString& rDescription, bool b comphelper::string::padToLength(aContentFiller, MAX_SIGNATURE_CONTENT_LENGTH, '0'); aSigBuffer.append(aContentFiller.makeStringAndClear()); aSigBuffer.append(">\n/Type/Sig/SubFilter"); -#ifdef XMLSEC_CRYPTO_NSS if (bAdES) aSigBuffer.append("/ETSI.CAdES.detached"); else -#else - (void)bAdES; -#endif aSigBuffer.append("/adbe.pkcs7.detached"); // Time of signing. @@ -2243,9 +2174,14 @@ bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignat { case SEC_OID_SHA1: nMaxResultLen = msfilter::SHA1_HASH_LENGTH; + rInformation.nDigestID = xml::crypto::DigestID::SHA1; break; case SEC_OID_SHA256: nMaxResultLen = msfilter::SHA256_HASH_LENGTH; + rInformation.nDigestID = xml::crypto::DigestID::SHA256; + break; + case SEC_OID_SHA512: + nMaxResultLen = msfilter::SHA512_HASH_LENGTH; break; default: SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ValidateSignature: unrecognized algorithm"); |