summaryrefslogtreecommitdiff
path: root/xmlsecurity
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.co.uk>2016-11-23 11:27:32 +0100
committerMiklos Vajna <vmiklos@collabora.co.uk>2016-11-29 08:08:50 +0000
commitd02c3ba8c5e723561edb694e7ed8b2f2c33604af (patch)
treeb7ada52cc1c914f7d94028f8ddc5a0bac5ead847 /xmlsecurity
parentb992da28c595f40a75ddf3237edec10d73e56d91 (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.mk1
-rw-r--r--xmlsecurity/inc/pdfio/pdfdocument.hxx66
-rw-r--r--xmlsecurity/inc/sigstruct.hxx3
-rw-r--r--xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx28
-rw-r--r--xmlsecurity/qa/unit/signing/data/certificate.crt27
-rw-r--r--xmlsecurity/qa/unit/signing/data/key3.dbbin0 -> 16384 bytes
-rw-r--r--xmlsecurity/qa/unit/signing/signing.cxx94
-rw-r--r--xmlsecurity/source/pdfio/pdfdocument.cxx74
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
new file mode 100644
index 000000000000..8ab32c28d584
--- /dev/null
+++ b/xmlsecurity/qa/unit/signing/data/key3.db
Binary files differ
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");