1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <xmlsignaturehelper.hxx>
22 #include <documentsignaturehelper.hxx>
23 #include <xsecctl.hxx>
25 #include <xmlsignaturehelper2.hxx>
27 #include <tools/stream.hxx>
28 #include <tools/datetime.hxx>
30 #include <xmloff/attrlist.hxx>
32 #include <com/sun/star/io/XOutputStream.hpp>
33 #include <com/sun/star/io/XInputStream.hpp>
34 #include <com/sun/star/io/XActiveDataSource.hpp>
35 #include <com/sun/star/io/XTruncate.hpp>
36 #include <com/sun/star/lang/XComponent.hpp>
37 #include <com/sun/star/beans/XPropertySet.hpp>
38 #include <com/sun/star/beans/StringPair.hpp>
39 #include <com/sun/star/xml/sax/Parser.hpp>
40 #include <com/sun/star/xml/sax/Writer.hpp>
41 #include <com/sun/star/embed/ElementModes.hpp>
42 #include <com/sun/star/embed/XStorage.hpp>
43 #include <com/sun/star/embed/StorageFormats.hpp>
44 #include <com/sun/star/embed/XTransactedObject.hpp>
45 #include <com/sun/star/io/XSeekable.hpp>
47 #include <tools/date.hxx>
48 #include <tools/time.hxx>
49 #include <comphelper/ofopxmlhelper.hxx>
50 #include <comphelper/sequence.hxx>
52 #define NS_DOCUMENTSIGNATURES "http://openoffice.org/2004/documentsignatures"
53 #define NS_DOCUMENTSIGNATURES_ODF_1_2 "urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0"
54 #define OOXML_SIGNATURE_ORIGIN "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin"
55 #define OOXML_SIGNATURE_SIGNATURE "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature"
57 using namespace ::com::sun::star
;
58 using namespace ::com::sun::star::uno
;
60 XMLSignatureHelper::XMLSignatureHelper( const uno::Reference
< uno::XComponentContext
>& rxCtx
)
61 : mxCtx(rxCtx
), mbODFPre1_2(false)
63 mpXSecController
= new XSecController(rxCtx
);
67 XMLSignatureHelper::~XMLSignatureHelper()
71 void XMLSignatureHelper::SetStorage(
72 const Reference
< css::embed::XStorage
>& rxStorage
,
73 const OUString
& sODFVersion
)
75 SAL_WARN_IF( mxUriBinding
.is(), "xmlsecurity.helper", "SetStorage - UriBinding already set!" );
76 mxUriBinding
= new UriBindingHelper( rxStorage
);
77 SAL_WARN_IF(!rxStorage
.is(), "xmlsecurity.helper", "SetStorage - empty storage!");
78 mbODFPre1_2
= DocumentSignatureHelper::isODFPre_1_2(sODFVersion
);
82 void XMLSignatureHelper::SetStartVerifySignatureHdl( const Link
<LinkParamNone
*,bool>& rLink
)
84 maStartVerifySignatureHdl
= rLink
;
88 void XMLSignatureHelper::StartMission(const uno::Reference
<xml::crypto::XXMLSecurityContext
>& xSecurityContext
)
90 if ( !mxUriBinding
.is() )
91 mxUriBinding
= new UriBindingHelper();
93 mpXSecController
->startMission(mxUriBinding
, xSecurityContext
);
96 void XMLSignatureHelper::EndMission()
98 mpXSecController
->endMission();
101 sal_Int32
XMLSignatureHelper::GetNewSecurityId()
103 return mpXSecController
->getNewSecurityId();
106 void XMLSignatureHelper::SetX509Certificate(
107 sal_Int32 nSecurityId
,
108 const OUString
& ouX509IssuerName
,
109 const OUString
& ouX509SerialNumber
,
110 const OUString
& ouX509Cert
,
111 const OUString
& ouX509CertDigest
)
113 mpXSecController
->setX509Certificate(
121 void XMLSignatureHelper::AddEncapsulatedX509Certificate(const OUString
& ouEncapsulatedX509Certificate
)
123 mpXSecController
->addEncapsulatedX509Certificate(ouEncapsulatedX509Certificate
);
126 void XMLSignatureHelper::SetGpgCertificate(sal_Int32 nSecurityId
,
127 const OUString
& ouGpgCertDigest
,
128 const OUString
& ouGpgCert
,
129 const OUString
& ouGpgOwner
)
131 mpXSecController
->setGpgCertificate(
138 void XMLSignatureHelper::SetDateTime( sal_Int32 nSecurityId
, const ::Date
& rDate
, const tools::Time
& rTime
)
140 css::util::DateTime stDateTime
= ::DateTime(rDate
, rTime
).GetUNODateTime();
141 mpXSecController
->setDate( nSecurityId
, stDateTime
);
144 void XMLSignatureHelper::SetDescription(sal_Int32 nSecurityId
, const OUString
& rDescription
)
146 mpXSecController
->setDescription(nSecurityId
, rDescription
);
149 void XMLSignatureHelper::SetSignatureLineId(sal_Int32 nSecurityId
, const OUString
& rSignatureLineId
)
151 mpXSecController
->setSignatureLineId(nSecurityId
, rSignatureLineId
);
154 void XMLSignatureHelper::AddForSigning( sal_Int32 nSecurityId
, const OUString
& uri
, bool bBinary
, bool bXAdESCompliantIfODF
)
156 mpXSecController
->signAStream( nSecurityId
, uri
, bBinary
, bXAdESCompliantIfODF
);
160 uno::Reference
<xml::sax::XWriter
> XMLSignatureHelper::CreateDocumentHandlerWithHeader(
161 const css::uno::Reference
< css::io::XOutputStream
>& xOutputStream
)
164 * get SAX writer component
166 uno::Reference
< xml::sax::XWriter
> xSaxWriter
= xml::sax::Writer::create(mxCtx
);
169 * connect XML writer to output stream
171 xSaxWriter
->setOutputStream( xOutputStream
);
174 * write the xml context for signatures
176 SvXMLAttributeList
*pAttributeList
= new SvXMLAttributeList();
179 sNamespace
= NS_DOCUMENTSIGNATURES
;
181 sNamespace
= NS_DOCUMENTSIGNATURES_ODF_1_2
;
183 pAttributeList
->AddAttribute(
187 xSaxWriter
->startDocument();
188 xSaxWriter
->startElement(
189 "document-signatures",
190 uno::Reference
< css::xml::sax::XAttributeList
> (pAttributeList
));
195 void XMLSignatureHelper::CloseDocumentHandler( const uno::Reference
<xml::sax::XDocumentHandler
>& xDocumentHandler
)
197 xDocumentHandler
->endElement( "document-signatures" );
198 xDocumentHandler
->endDocument();
201 void XMLSignatureHelper::ExportSignature(
202 const uno::Reference
< xml::sax::XDocumentHandler
>& xDocumentHandler
,
203 const SignatureInformation
& signatureInfo
,
204 bool bXAdESCompliantIfODF
)
206 XSecController::exportSignature(xDocumentHandler
, signatureInfo
, bXAdESCompliantIfODF
);
209 void XMLSignatureHelper::ExportOOXMLSignature(const uno::Reference
<embed::XStorage
>& xRootStorage
, const uno::Reference
<embed::XStorage
>& xSignatureStorage
, const SignatureInformation
& rInformation
, int nSignatureIndex
)
211 uno::Reference
<io::XOutputStream
> xOutputStream(xSignatureStorage
->openStreamElement("sig" + OUString::number(nSignatureIndex
) + ".xml", embed::ElementModes::READWRITE
), uno::UNO_QUERY
);
213 if (rInformation
.aSignatureBytes
.hasElements())
214 // This is a signature roundtrip, just write back the signature as-is.
215 xOutputStream
->writeBytes(rInformation
.aSignatureBytes
);
218 uno::Reference
<xml::sax::XWriter
> xSaxWriter
= xml::sax::Writer::create(mxCtx
);
219 xSaxWriter
->setOutputStream(xOutputStream
);
220 xSaxWriter
->startDocument();
222 uno::Reference
<xml::sax::XDocumentHandler
> xDocumentHandler(xSaxWriter
, uno::UNO_QUERY
);
223 mpXSecController
->exportOOXMLSignature(xRootStorage
, xDocumentHandler
, rInformation
);
225 xSaxWriter
->endDocument();
229 bool XMLSignatureHelper::CreateAndWriteSignature( const uno::Reference
< xml::sax::XDocumentHandler
>& xDocumentHandler
, bool bXAdESCompliantIfODF
)
233 if ( !mpXSecController
->WriteSignature( xDocumentHandler
, bXAdESCompliantIfODF
) )
241 bool XMLSignatureHelper::ReadAndVerifySignature( const css::uno::Reference
< css::io::XInputStream
>& xInputStream
)
245 SAL_WARN_IF(!xInputStream
.is(), "xmlsecurity.helper", "input stream missing");
247 // prepare ParserInputSrouce
248 xml::sax::InputSource aParserInput
;
249 aParserInput
.aInputStream
= xInputStream
;
251 // get SAX parser component
252 uno::Reference
< xml::sax::XParser
> xParser
= xml::sax::Parser::create(mxCtx
);
254 // create a signature reader
255 uno::Reference
< xml::sax::XDocumentHandler
> xHandler
256 = mpXSecController
->createSignatureReader(*this);
258 // setup the connection:
259 // Parser -> SignatureReader
260 xParser
->setDocumentHandler( xHandler
);
265 xParser
->parseStream( aParserInput
);
267 catch( uno::Exception
& )
272 // release the signature reader
273 mpXSecController
->releaseSignatureReader( );
278 SignatureInformation
XMLSignatureHelper::GetSignatureInformation( sal_Int32 nSecurityId
) const
280 return mpXSecController
->getSignatureInformation( nSecurityId
);
283 SignatureInformations
XMLSignatureHelper::GetSignatureInformations() const
285 return mpXSecController
->getSignatureInformations();
288 void XMLSignatureHelper::StartVerifySignatureElement()
290 if ( !maStartVerifySignatureHdl
.IsSet() || maStartVerifySignatureHdl
.Call(nullptr) )
292 sal_Int32 nSignatureId
= mpXSecController
->getNewSecurityId();
293 mpXSecController
->addSignature( nSignatureId
);
299 bool lcl_isSignatureType(const beans::StringPair
& rPair
)
301 return rPair
.First
== "Type" && rPair
.Second
== OOXML_SIGNATURE_SIGNATURE
;
303 bool lcl_isSignatureOriginType(const beans::StringPair
& rPair
)
305 return rPair
.First
== "Type" && rPair
.Second
== OOXML_SIGNATURE_ORIGIN
;
309 bool XMLSignatureHelper::ReadAndVerifySignatureStorage(const uno::Reference
<embed::XStorage
>& xStorage
, bool bCacheLastSignature
)
311 sal_Int32 nOpenMode
= embed::ElementModes::READ
;
312 uno::Reference
<container::XNameAccess
> xNameAccess(xStorage
, uno::UNO_QUERY
);
313 if (xNameAccess
.is() && !xNameAccess
->hasByName("_rels"))
315 SAL_WARN("xmlsecurity.helper", "expected stream, in signature storage but not found: _rels");
319 uno::Reference
<embed::XStorage
> xSubStorage
= xStorage
->openStorageElement("_rels", nOpenMode
);
320 uno::Reference
<io::XInputStream
> xRelStream(xSubStorage
->openStreamElement("origin.sigs.rels", nOpenMode
), uno::UNO_QUERY
);
321 uno::Sequence
< uno::Sequence
<beans::StringPair
> > aRelationsInfo
;
322 aRelationsInfo
= comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(xRelStream
, "origin.sigs.rels", mxCtx
);
324 for (sal_Int32 i
= 0; i
< aRelationsInfo
.getLength(); ++i
)
326 const uno::Sequence
<beans::StringPair
>& rRelation
= aRelationsInfo
[i
];
327 auto aRelation
= comphelper::sequenceToContainer
< std::vector
<beans::StringPair
> >(rRelation
);
328 if (std::find_if(aRelation
.begin(), aRelation
.end(), lcl_isSignatureType
) != aRelation
.end())
330 std::vector
<beans::StringPair
>::iterator it
= std::find_if(aRelation
.begin(), aRelation
.end(), [](const beans::StringPair
& rPair
) { return rPair
.First
== "Target"; });
331 if (it
!= aRelation
.end())
333 if (xNameAccess
.is() && !xNameAccess
->hasByName(it
->Second
))
335 SAL_WARN("xmlsecurity.helper", "expected stream, but not found: " << it
->Second
);
339 uno::Reference
<io::XInputStream
> xInputStream(xStorage
->openStreamElement(it
->Second
, nOpenMode
), uno::UNO_QUERY
);
340 if (!ReadAndVerifySignatureStorageStream(xInputStream
))
343 // By default, we cache. If it's requested, then we don't cache the last signature.
345 if (!bCacheLastSignature
&& i
== aRelationsInfo
.getLength() - 1)
350 // Store the contents of the stream as is, in case we need to write it back later.
351 xInputStream
.clear();
352 xInputStream
.set(xStorage
->openStreamElement(it
->Second
, nOpenMode
), uno::UNO_QUERY
);
353 uno::Reference
<beans::XPropertySet
> xPropertySet(xInputStream
, uno::UNO_QUERY
);
354 if (xPropertySet
.is())
357 xPropertySet
->getPropertyValue("Size") >>= nSize
;
358 uno::Sequence
<sal_Int8
> aData
;
359 xInputStream
->readBytes(aData
, nSize
);
360 mpXSecController
->setSignatureBytes(aData
);
370 bool XMLSignatureHelper::ReadAndVerifySignatureStorageStream(const css::uno::Reference
<css::io::XInputStream
>& xInputStream
)
374 // Create the input source.
375 xml::sax::InputSource aParserInput
;
376 aParserInput
.aInputStream
= xInputStream
;
378 // Create the sax parser.
379 uno::Reference
<xml::sax::XParser
> xParser
= xml::sax::Parser::create(mxCtx
);
381 // Create the signature reader.
382 uno::Reference
<xml::sax::XDocumentHandler
> xHandler
= mpXSecController
->createSignatureReader(*this, embed::StorageFormats::OFOPXML
);
384 // Parser -> signature reader.
385 xParser
->setDocumentHandler(xHandler
);
390 xParser
->parseStream(aParserInput
);
392 catch(const uno::Exception
& rException
)
394 SAL_WARN("xmlsecurity.helper", "XMLSignatureHelper::ReadAndVerifySignatureStorageStream: " << rException
);
397 mpXSecController
->releaseSignatureReader();
402 void XMLSignatureHelper::EnsureSignaturesRelation(const css::uno::Reference
<css::embed::XStorage
>& xStorage
, bool bAdd
)
404 sal_Int32 nOpenMode
= embed::ElementModes::READWRITE
;
405 uno::Reference
<embed::XStorage
> xSubStorage
= xStorage
->openStorageElement("_rels", nOpenMode
);
406 uno::Reference
<io::XInputStream
> xRelStream(xSubStorage
->openStreamElement(".rels", nOpenMode
), uno::UNO_QUERY
);
407 std::vector
< uno::Sequence
<beans::StringPair
> > aRelationsInfo
;
408 aRelationsInfo
= comphelper::sequenceToContainer
< std::vector
< uno::Sequence
<beans::StringPair
> > >(comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(xRelStream
, ".rels", mxCtx
));
410 // Do we have a relation already?
411 bool bHaveRelation
= false;
413 for (const uno::Sequence
<beans::StringPair
>& rRelation
: aRelationsInfo
)
415 auto aRelation
= comphelper::sequenceToContainer
< std::vector
<beans::StringPair
> >(rRelation
);
416 if (std::find_if(aRelation
.begin(), aRelation
.end(), lcl_isSignatureOriginType
) != aRelation
.end())
418 bHaveRelation
= true;
424 if (!bHaveRelation
&& bAdd
)
426 // No, and have to add one.
427 std::vector
<beans::StringPair
> aRelation
;
428 aRelation
.emplace_back("Id", "rId" + OUString::number(++nCount
));
429 aRelation
.emplace_back("Type", OOXML_SIGNATURE_ORIGIN
);
430 aRelation
.emplace_back("Target", "_xmlsignatures/origin.sigs");
431 aRelationsInfo
.push_back(comphelper::containerToSequence(aRelation
));
433 else if (bHaveRelation
&& !bAdd
)
435 // Yes, and need to remove it.
436 for (std::vector
< uno::Sequence
<beans::StringPair
> >::iterator it
= aRelationsInfo
.begin(); it
!= aRelationsInfo
.end();)
438 auto aRelation
= comphelper::sequenceToContainer
< std::vector
<beans::StringPair
> >(*it
);
439 if (std::find_if(aRelation
.begin(), aRelation
.end(), lcl_isSignatureOriginType
) != aRelation
.end())
440 it
= aRelationsInfo
.erase(it
);
447 uno::Reference
<io::XTruncate
> xTruncate(xRelStream
, uno::UNO_QUERY
);
448 xTruncate
->truncate();
449 uno::Reference
<io::XOutputStream
> xOutputStream(xRelStream
, uno::UNO_QUERY
);
450 comphelper::OFOPXMLHelper::WriteRelationsInfoSequence(xOutputStream
, comphelper::containerToSequence(aRelationsInfo
), mxCtx
);
453 uno::Reference
<embed::XTransactedObject
> xTransact(xSubStorage
, uno::UNO_QUERY
);
455 xTransact
.set(xStorage
, uno::UNO_QUERY
);
459 void XMLSignatureHelper::ExportSignatureRelations(const css::uno::Reference
<css::embed::XStorage
>& xStorage
, int nSignatureCount
)
461 // Write the empty file, its relations will be the signatures.
462 sal_Int32 nOpenMode
= embed::ElementModes::READWRITE
;
463 uno::Reference
<io::XOutputStream
> xOriginStream(xStorage
->openStreamElement("origin.sigs", nOpenMode
), uno::UNO_QUERY
);
464 uno::Reference
<io::XTruncate
> xTruncate(xOriginStream
, uno::UNO_QUERY
);
465 xTruncate
->truncate();
466 xOriginStream
->closeOutput();
468 // Write the relations.
469 uno::Reference
<embed::XStorage
> xSubStorage(xStorage
->openStorageElement("_rels", nOpenMode
), uno::UNO_QUERY
);
470 uno::Reference
<io::XOutputStream
> xRelStream(xSubStorage
->openStreamElement("origin.sigs.rels", nOpenMode
), uno::UNO_QUERY
);
471 std::vector
< uno::Sequence
<beans::StringPair
> > aRelations
;
472 for (int i
= 0; i
< nSignatureCount
; ++i
)
474 std::vector
<beans::StringPair
> aRelation
;
475 aRelation
.emplace_back("Id", "rId" + OUString::number(i
+ 1));
476 aRelation
.emplace_back("Type", OOXML_SIGNATURE_SIGNATURE
);
477 aRelation
.emplace_back("Target", "sig" + OUString::number(i
+ 1) + ".xml");
478 aRelations
.push_back(comphelper::containerToSequence(aRelation
));
480 comphelper::OFOPXMLHelper::WriteRelationsInfoSequence(xRelStream
, comphelper::containerToSequence(aRelations
), mxCtx
);
481 uno::Reference
<embed::XTransactedObject
> xTransact(xSubStorage
, uno::UNO_QUERY
);
485 void XMLSignatureHelper::ExportSignatureContentTypes(const css::uno::Reference
<css::embed::XStorage
>& xStorage
, int nSignatureCount
)
487 uno::Reference
<io::XStream
> xStream(xStorage
->openStreamElement("[Content_Types].xml", embed::ElementModes::READWRITE
), uno::UNO_QUERY
);
488 uno::Reference
<io::XInputStream
> xInputStream
= xStream
->getInputStream();
489 uno::Sequence
< uno::Sequence
<beans::StringPair
> > aContentTypeInfo
= comphelper::OFOPXMLHelper::ReadContentTypeSequence(xInputStream
, mxCtx
);
490 if (aContentTypeInfo
.getLength() < 2)
492 SAL_WARN("xmlsecurity.helper", "no defaults or overrides in aContentTypeInfo");
496 // Append rels and sigs to defaults, if it's not there already.
497 uno::Sequence
<beans::StringPair
>& rDefaults
= aContentTypeInfo
[0];
498 auto aDefaults
= comphelper::sequenceToContainer
< std::vector
<beans::StringPair
> >(rDefaults
);
499 auto it
= std::find_if(rDefaults
.begin(), rDefaults
.end(), [](const beans::StringPair
& rPair
)
501 return rPair
.First
== "rels";
503 if (it
== rDefaults
.end())
504 aDefaults
.emplace_back("rels", "application/vnd.openxmlformats-package.relationships+xml");
506 it
= std::find_if(rDefaults
.begin(), rDefaults
.end(), [](const beans::StringPair
& rPair
)
508 return rPair
.First
== "sigs";
510 if (it
== rDefaults
.end())
511 aDefaults
.emplace_back("sigs", "application/vnd.openxmlformats-package.digital-signature-origin");
512 rDefaults
= comphelper::containerToSequence(aDefaults
);
514 // Remove existing signature overrides.
515 uno::Sequence
<beans::StringPair
>& rOverrides
= aContentTypeInfo
[1];
516 auto aOverrides
= comphelper::sequenceToContainer
< std::vector
<beans::StringPair
> >(rOverrides
);
517 aOverrides
.erase(std::remove_if(aOverrides
.begin(), aOverrides
.end(), [](const beans::StringPair
& rPair
)
519 return rPair
.First
.startsWith("/_xmlsignatures/sig");
520 }), aOverrides
.end());
522 // Add our signature overrides.
523 for (int i
= 1; i
<= nSignatureCount
; ++i
)
524 aOverrides
.emplace_back("/_xmlsignatures/sig" + OUString::number(i
) + ".xml", "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml");
526 rOverrides
= comphelper::containerToSequence(aOverrides
);
527 uno::Reference
<io::XOutputStream
> xOutputStream
= xStream
->getOutputStream();
528 uno::Reference
<io::XTruncate
> xTruncate(xOutputStream
, uno::UNO_QUERY
);
529 xTruncate
->truncate();
530 comphelper::OFOPXMLHelper::WriteContentSequence(xOutputStream
, rDefaults
, rOverrides
, mxCtx
);
531 uno::Reference
<embed::XTransactedObject
> xTransact(xStorage
, uno::UNO_QUERY
);
534 bool XMLSignatureHelper::CreateAndWriteOOXMLSignature(const uno::Reference
<embed::XStorage
>& xRootStorage
, const uno::Reference
<embed::XStorage
>& xSignatureStorage
, int nSignatureIndex
)
536 uno::Reference
<io::XOutputStream
> xOutputStream(xSignatureStorage
->openStreamElement("sig" + OUString::number(nSignatureIndex
) + ".xml", embed::ElementModes::READWRITE
), uno::UNO_QUERY
);
537 uno::Reference
<xml::sax::XWriter
> xSaxWriter
= xml::sax::Writer::create(mxCtx
);
538 xSaxWriter
->setOutputStream(xOutputStream
);
539 xSaxWriter
->startDocument();
542 uno::Reference
<xml::sax::XDocumentHandler
> xDocumentHandler(xSaxWriter
, uno::UNO_QUERY
);
543 if (!mpXSecController
->WriteOOXMLSignature(xRootStorage
, xDocumentHandler
))
546 xSaxWriter
->endDocument();
551 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */