0de900cec7
The final goal of this API is to give time & hash information about the PDF signature, so once a 3rd-party produces the PKCS#7 signature, that can be added to the document and the actual PDF sign can be re-run with the same parameters. This commit continues the replacement of XCertificate with svl::crypto::SigningContext up to the point that the timestamp used in svl/ can be exposed on the LOK API. This is done by updating DocumentSignatureManager::add(), PDFSignatureHelper::SetX509Certificate(), vcl::filter::PDFDocument::Sign() and finally the svl::crypto::Signing ctor to work with the signing context instead of an XCertificate directly. Time reporting works now, so add a test for that. The digest part still needs doing. Change-Id: I83f1274cd420b67194b7caf12b1027e623d4f7fe Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176404 Reviewed-by: Miklos Vajna <vmiklos@collabora.com> Tested-by: Jenkins
628 lines
20 KiB
C++
628 lines
20 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include <pdfsignaturehelper.hxx>
|
|
|
|
#include <memory>
|
|
|
|
#include <com/sun/star/io/XTruncate.hpp>
|
|
#include <com/sun/star/io/XStream.hpp>
|
|
#include <com/sun/star/security/CertificateValidity.hpp>
|
|
#include <com/sun/star/uno/SecurityException.hpp>
|
|
#include <com/sun/star/security/DocumentSignatureInformation.hpp>
|
|
#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
|
|
#include <com/sun/star/drawing/XShapes.hpp>
|
|
#include <com/sun/star/frame/XModel.hpp>
|
|
#include <com/sun/star/frame/XStorable.hpp>
|
|
#include <com/sun/star/beans/XPropertySet.hpp>
|
|
#include <com/sun/star/drawing/XDrawView.hpp>
|
|
|
|
#include <comphelper/propertysequence.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <comphelper/diagnose_ex.hxx>
|
|
#include <unotools/mediadescriptor.hxx>
|
|
#include <unotools/streamwrap.hxx>
|
|
#include <unotools/ucbstreamhelper.hxx>
|
|
#include <vcl/filter/pdfdocument.hxx>
|
|
#include <vcl/checksum.hxx>
|
|
#include <svl/cryptosign.hxx>
|
|
#include <vcl/filter/PDFiumLibrary.hxx>
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
namespace
|
|
{
|
|
/// Gets the current page of the current view from xModel and puts it to the 1-based rPage.
|
|
bool GetSignatureLinePage(const uno::Reference<frame::XModel>& xModel, sal_Int32& rPage)
|
|
{
|
|
uno::Reference<drawing::XDrawView> xController(xModel->getCurrentController(), uno::UNO_QUERY);
|
|
if (!xController.is())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uno::Reference<beans::XPropertySet> xPage(xController->getCurrentPage(), uno::UNO_QUERY);
|
|
if (!xPage.is())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return xPage->getPropertyValue(u"Number"_ustr) >>= rPage;
|
|
}
|
|
|
|
/// If the currently selected shape is a Draw signature line, export that to PDF.
|
|
void GetSignatureLineShape(const uno::Reference<frame::XModel>& xModel, sal_Int32& rPage,
|
|
std::vector<sal_Int8>& rSignatureLineShape)
|
|
{
|
|
if (!xModel.is())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!GetSignatureLinePage(xModel, rPage))
|
|
{
|
|
return;
|
|
}
|
|
|
|
uno::Reference<drawing::XShapes> xShapes(xModel->getCurrentSelection(), uno::UNO_QUERY);
|
|
if (!xShapes.is() || xShapes->getCount() < 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
uno::Reference<beans::XPropertySet> xShapeProps(xShapes->getByIndex(0), uno::UNO_QUERY);
|
|
if (!xShapeProps.is())
|
|
{
|
|
return;
|
|
}
|
|
|
|
comphelper::SequenceAsHashMap aMap(xShapeProps->getPropertyValue(u"InteropGrabBag"_ustr));
|
|
auto it = aMap.find(u"SignatureCertificate"_ustr);
|
|
if (it == aMap.end())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// We know that we add a signature line shape to an existing PDF at this point.
|
|
|
|
uno::Reference<frame::XStorable> xStorable(xModel, uno::UNO_QUERY);
|
|
if (!xStorable.is())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Export just the signature line.
|
|
utl::MediaDescriptor aMediaDescriptor;
|
|
aMediaDescriptor[u"FilterName"_ustr] <<= u"draw_pdf_Export"_ustr;
|
|
SvMemoryStream aStream;
|
|
uno::Reference<io::XOutputStream> xStream(new utl::OStreamWrapper(aStream));
|
|
aMediaDescriptor[u"OutputStream"_ustr] <<= xStream;
|
|
uno::Sequence<beans::PropertyValue> aFilterData(
|
|
comphelper::InitPropertySequence({ { "Selection", uno::Any(xShapes) } }));
|
|
aMediaDescriptor[u"FilterData"_ustr] <<= aFilterData;
|
|
xStorable->storeToURL(u"private:stream"_ustr, aMediaDescriptor.getAsConstPropertyValueList());
|
|
xStream->flush();
|
|
|
|
aStream.Seek(0);
|
|
rSignatureLineShape = std::vector<sal_Int8>(aStream.GetSize());
|
|
aStream.ReadBytes(rSignatureLineShape.data(), rSignatureLineShape.size());
|
|
}
|
|
|
|
/// Represents a parsed signature.
|
|
struct Signature
|
|
{
|
|
std::unique_ptr<vcl::pdf::PDFiumSignature> m_pSignature;
|
|
/// Offset+length pairs.
|
|
std::vector<std::pair<size_t, size_t>> m_aByteRanges;
|
|
};
|
|
|
|
/// Turns an array of floats into offset + length pairs.
|
|
void GetByteRangesFromPDF(const std::unique_ptr<vcl::pdf::PDFiumSignature>& pSignature,
|
|
std::vector<std::pair<size_t, size_t>>& rByteRanges)
|
|
{
|
|
std::vector<int> aByteRange = pSignature->getByteRange();
|
|
if (aByteRange.empty())
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "GetByteRangesFromPDF: no byte ranges");
|
|
return;
|
|
}
|
|
|
|
size_t nByteRangeOffset = 0;
|
|
for (size_t i = 0; i < aByteRange.size(); ++i)
|
|
{
|
|
if (i % 2 == 0)
|
|
{
|
|
nByteRangeOffset = aByteRange[i];
|
|
continue;
|
|
}
|
|
|
|
size_t nLength = aByteRange[i];
|
|
rByteRanges.emplace_back(nByteRangeOffset, nLength);
|
|
}
|
|
}
|
|
|
|
/// Determines the last position that is covered by a signature.
|
|
bool GetEOFOfSignature(const Signature& rSignature, size_t& rEOF)
|
|
{
|
|
if (rSignature.m_aByteRanges.size() < 2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
rEOF = rSignature.m_aByteRanges[1].first + rSignature.m_aByteRanges[1].second;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get the value of the "modification detection and prevention" permission:
|
|
* Valid values are 1, 2 and 3: only 3 allows annotations after signing.
|
|
*/
|
|
int GetMDPPerm(const std::vector<Signature>& rSignatures)
|
|
{
|
|
int nRet = 3;
|
|
|
|
if (rSignatures.empty())
|
|
{
|
|
return nRet;
|
|
}
|
|
|
|
for (const auto& rSignature : rSignatures)
|
|
{
|
|
int nPerm = rSignature.m_pSignature->getDocMDPPermission();
|
|
if (nPerm != 0)
|
|
{
|
|
return nPerm;
|
|
}
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
|
|
/// Checks if there are unsigned incremental updates between the signatures or after the last one.
|
|
bool IsCompleteSignature(SvStream& rStream, const Signature& rSignature,
|
|
const std::set<unsigned int>& rSignedEOFs,
|
|
const std::vector<unsigned int>& rAllEOFs)
|
|
{
|
|
size_t nSignatureEOF = 0;
|
|
if (!GetEOFOfSignature(rSignature, nSignatureEOF))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bFoundOwn = false;
|
|
for (const auto& rEOF : rAllEOFs)
|
|
{
|
|
if (rEOF == nSignatureEOF)
|
|
{
|
|
bFoundOwn = true;
|
|
continue;
|
|
}
|
|
|
|
if (!bFoundOwn)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (rSignedEOFs.find(rEOF) == rSignedEOFs.end())
|
|
{
|
|
// Unsigned incremental update found.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Make sure we find the incremental update of the signature itself.
|
|
if (!bFoundOwn)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// No additional content after the last incremental update.
|
|
rStream.Seek(STREAM_SEEK_TO_END);
|
|
size_t nFileEnd = rStream.Tell();
|
|
return std::find(rAllEOFs.begin(), rAllEOFs.end(), nFileEnd) != rAllEOFs.end();
|
|
}
|
|
|
|
/**
|
|
* Contains checksums of a PDF page, which is rendered without annotations. It also contains
|
|
* the geometry of a few dangerous annotation types.
|
|
*/
|
|
struct PageChecksum
|
|
{
|
|
BitmapChecksum m_nPageContent;
|
|
std::vector<basegfx::B2DRectangle> m_aAnnotations;
|
|
bool operator==(const PageChecksum& rChecksum) const;
|
|
};
|
|
|
|
bool PageChecksum::operator==(const PageChecksum& rChecksum) const
|
|
{
|
|
if (m_nPageContent != rChecksum.m_nPageContent)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return m_aAnnotations == rChecksum.m_aAnnotations;
|
|
}
|
|
|
|
/// Collects the checksum of each page of one version of the PDF.
|
|
void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<PageChecksum>& rPageChecksums,
|
|
int nMDPPerm)
|
|
{
|
|
auto pPdfium = vcl::pdf::PDFiumLibrary::get();
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
|
|
= pPdfium->openDocument(rStream.GetData(), rStream.GetSize(), OString());
|
|
if (!pPdfDocument)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int nPageCount = pPdfDocument->getPageCount();
|
|
for (int nPage = 0; nPage < nPageCount; ++nPage)
|
|
{
|
|
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(nPage);
|
|
if (!pPdfPage)
|
|
{
|
|
return;
|
|
}
|
|
|
|
PageChecksum aPageChecksum;
|
|
aPageChecksum.m_nPageContent = pPdfPage->getChecksum(nMDPPerm);
|
|
for (int i = 0; i < pPdfPage->getAnnotationCount(); ++i)
|
|
{
|
|
std::unique_ptr<vcl::pdf::PDFiumAnnotation> pPdfAnnotation = pPdfPage->getAnnotation(i);
|
|
if (!pPdfAnnotation)
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "Cannot get PDFiumAnnotation");
|
|
continue;
|
|
}
|
|
vcl::pdf::PDFAnnotationSubType eType = pPdfAnnotation->getSubType();
|
|
switch (eType)
|
|
{
|
|
case vcl::pdf::PDFAnnotationSubType::Unknown:
|
|
case vcl::pdf::PDFAnnotationSubType::FreeText:
|
|
case vcl::pdf::PDFAnnotationSubType::Stamp:
|
|
case vcl::pdf::PDFAnnotationSubType::Redact:
|
|
aPageChecksum.m_aAnnotations.push_back(pPdfAnnotation->getRectangle());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
rPageChecksums.push_back(aPageChecksum);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if incremental updates after singing performed valid modifications only.
|
|
* nMDPPerm decides if annotations/commenting is OK, other changes are always not.
|
|
*/
|
|
bool IsValidSignature(SvStream& rStream, const Signature& rSignature, int nMDPPerm)
|
|
{
|
|
size_t nSignatureEOF = 0;
|
|
if (!GetEOFOfSignature(rSignature, nSignatureEOF))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SvMemoryStream aSignatureStream;
|
|
sal_uInt64 nPos = rStream.Tell();
|
|
rStream.Seek(0);
|
|
aSignatureStream.WriteStream(rStream, nSignatureEOF);
|
|
rStream.Seek(nPos);
|
|
aSignatureStream.Seek(0);
|
|
std::vector<PageChecksum> aSignedPages;
|
|
AnalyizeSignatureStream(aSignatureStream, aSignedPages, nMDPPerm);
|
|
|
|
SvMemoryStream aFullStream;
|
|
nPos = rStream.Tell();
|
|
rStream.Seek(0);
|
|
aFullStream.WriteStream(rStream);
|
|
rStream.Seek(nPos);
|
|
aFullStream.Seek(0);
|
|
std::vector<PageChecksum> aAllPages;
|
|
AnalyizeSignatureStream(aFullStream, aAllPages, nMDPPerm);
|
|
|
|
// Fail if any page looks different after signing and at the end. Annotations/commenting doesn't
|
|
// count, though.
|
|
return aSignedPages == aAllPages;
|
|
}
|
|
|
|
/**
|
|
* @param rInformation The actual result.
|
|
* @param rDocument the parsed document to see if the signature is partial.
|
|
* @return If we can determinate a result.
|
|
*/
|
|
bool ValidateSignature(SvStream& rStream, const Signature& rSignature,
|
|
SignatureInformation& rInformation, int nMDPPerm,
|
|
const std::set<unsigned int>& rSignatureEOFs,
|
|
const std::vector<unsigned int>& rTrailerEnds)
|
|
{
|
|
std::vector<unsigned char> aContents = rSignature.m_pSignature->getContents();
|
|
if (aContents.empty())
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "ValidateSignature: no contents");
|
|
return false;
|
|
}
|
|
|
|
OString aSubFilter = rSignature.m_pSignature->getSubFilter();
|
|
|
|
const bool bNonDetached = aSubFilter == "adbe.pkcs7.sha1";
|
|
if (aSubFilter.isEmpty()
|
|
|| (aSubFilter != "adbe.pkcs7.detached" && !bNonDetached
|
|
&& aSubFilter != "ETSI.CAdES.detached"))
|
|
{
|
|
if (aSubFilter.isEmpty())
|
|
SAL_WARN("xmlsecurity.helper", "ValidateSignature: missing sub-filter");
|
|
else
|
|
SAL_WARN("xmlsecurity.helper",
|
|
"ValidateSignature: unsupported sub-filter: '" << aSubFilter << "'");
|
|
return false;
|
|
}
|
|
|
|
// Reason / comment / description is optional.
|
|
rInformation.ouDescription = rSignature.m_pSignature->getReason();
|
|
|
|
// Date: used only when the time of signing is not available in the
|
|
// signature.
|
|
rInformation.stDateTime = rSignature.m_pSignature->getTime();
|
|
|
|
// Detect if the byte ranges don't cover everything, but the signature itself.
|
|
if (rSignature.m_aByteRanges.size() < 2)
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "ValidateSignature: expected 2 byte ranges");
|
|
return false;
|
|
}
|
|
if (rSignature.m_aByteRanges[0].first != 0)
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "ValidateSignature: first range start is not 0");
|
|
return false;
|
|
}
|
|
// Binary vs hex dump and 2 is the leading "<" and the trailing ">" around the hex string.
|
|
size_t nSignatureLength = aContents.size() * 2 + 2;
|
|
if (rSignature.m_aByteRanges[1].first
|
|
!= (rSignature.m_aByteRanges[0].second + nSignatureLength))
|
|
{
|
|
SAL_WARN("xmlsecurity.helper",
|
|
"ValidateSignature: second range start is not the end of the signature");
|
|
return false;
|
|
}
|
|
rInformation.bPartialDocumentSignature
|
|
= !IsCompleteSignature(rStream, rSignature, rSignatureEOFs, rTrailerEnds);
|
|
if (!IsValidSignature(rStream, rSignature, nMDPPerm))
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "ValidateSignature: invalid incremental update detected");
|
|
return false;
|
|
}
|
|
|
|
// At this point there is no obviously missing info to validate the
|
|
// signature.
|
|
return svl::crypto::Signing::Verify(rStream, rSignature.m_aByteRanges, bNonDetached, aContents,
|
|
rInformation);
|
|
}
|
|
}
|
|
|
|
PDFSignatureHelper::PDFSignatureHelper() = default;
|
|
|
|
bool PDFSignatureHelper::ReadAndVerifySignature(
|
|
const uno::Reference<io::XInputStream>& xInputStream)
|
|
{
|
|
if (!xInputStream.is())
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "input stream missing");
|
|
return false;
|
|
}
|
|
|
|
std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
|
|
return ReadAndVerifySignatureSvStream(*pStream);
|
|
}
|
|
|
|
bool PDFSignatureHelper::ReadAndVerifySignatureSvStream(SvStream& rStream)
|
|
{
|
|
auto pPdfium = vcl::pdf::PDFiumLibrary::get();
|
|
if (!pPdfium)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
SvMemoryStream aStream;
|
|
sal_uInt64 nPos = rStream.Tell();
|
|
rStream.Seek(0);
|
|
aStream.WriteStream(rStream);
|
|
rStream.Seek(nPos);
|
|
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
|
|
= pPdfium->openDocument(aStream.GetData(), aStream.GetSize(), OString());
|
|
if (!pPdfDocument)
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "failed to read the document");
|
|
return false;
|
|
}
|
|
|
|
int nSignatureCount = pPdfDocument->getSignatureCount();
|
|
if (nSignatureCount <= 0)
|
|
{
|
|
return true;
|
|
}
|
|
std::vector<Signature> aSignatures(nSignatureCount);
|
|
for (int i = 0; i < nSignatureCount; ++i)
|
|
{
|
|
std::unique_ptr<vcl::pdf::PDFiumSignature> pSignature = pPdfDocument->getSignature(i);
|
|
std::vector<std::pair<size_t, size_t>> aByteRanges;
|
|
GetByteRangesFromPDF(pSignature, aByteRanges);
|
|
aSignatures[i] = Signature{ std::move(pSignature), std::move(aByteRanges) };
|
|
}
|
|
|
|
std::set<unsigned int> aSignatureEOFs;
|
|
for (const auto& rSignature : aSignatures)
|
|
{
|
|
size_t nEOF = 0;
|
|
if (GetEOFOfSignature(rSignature, nEOF))
|
|
{
|
|
aSignatureEOFs.insert(nEOF);
|
|
}
|
|
}
|
|
|
|
std::vector<unsigned int> aTrailerEnds = pPdfDocument->getTrailerEnds();
|
|
|
|
m_aSignatureInfos.clear();
|
|
|
|
int nMDPPerm = GetMDPPerm(aSignatures);
|
|
|
|
for (size_t i = 0; i < aSignatures.size(); ++i)
|
|
{
|
|
SignatureInformation aInfo(i);
|
|
|
|
if (!ValidateSignature(rStream, aSignatures[i], aInfo, nMDPPerm, aSignatureEOFs,
|
|
aTrailerEnds))
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "failed to determine digest match");
|
|
}
|
|
|
|
m_aSignatureInfos.push_back(aInfo);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
SignatureInformations const& PDFSignatureHelper::GetSignatureInformations() const
|
|
{
|
|
return m_aSignatureInfos;
|
|
}
|
|
|
|
uno::Sequence<security::DocumentSignatureInformation>
|
|
PDFSignatureHelper::GetDocumentSignatureInformations(
|
|
const uno::Reference<xml::crypto::XSecurityEnvironment>& xSecEnv) const
|
|
{
|
|
uno::Sequence<security::DocumentSignatureInformation> aRet(m_aSignatureInfos.size());
|
|
auto aRetRange = asNonConstRange(aRet);
|
|
|
|
for (size_t i = 0; i < m_aSignatureInfos.size(); ++i)
|
|
{
|
|
const SignatureInformation& rInternal = m_aSignatureInfos[i];
|
|
security::DocumentSignatureInformation& rExternal = aRetRange[i];
|
|
rExternal.SignatureIsValid
|
|
= rInternal.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
|
|
if (rInternal.GetSigningCertificate()
|
|
&& !rInternal.GetSigningCertificate()->X509Certificate.isEmpty())
|
|
{
|
|
rExternal.Signer = xSecEnv->createCertificateFromAscii(
|
|
rInternal.GetSigningCertificate()->X509Certificate);
|
|
}
|
|
rExternal.PartialDocumentSignature = rInternal.bPartialDocumentSignature;
|
|
|
|
// Verify certificate.
|
|
if (rExternal.Signer.is())
|
|
{
|
|
try
|
|
{
|
|
rExternal.CertificateStatus = xSecEnv->verifyCertificate(rExternal.Signer, {});
|
|
}
|
|
catch (const uno::SecurityException&)
|
|
{
|
|
DBG_UNHANDLED_EXCEPTION("xmlsecurity.helper", "failed to verify certificate");
|
|
rExternal.CertificateStatus = security::CertificateValidity::INVALID;
|
|
}
|
|
}
|
|
else
|
|
rExternal.CertificateStatus = security::CertificateValidity::INVALID;
|
|
}
|
|
|
|
return aRet;
|
|
}
|
|
|
|
sal_Int32 PDFSignatureHelper::GetNewSecurityId() const { return m_aSignatureInfos.size(); }
|
|
|
|
void PDFSignatureHelper::SetX509Certificate(svl::crypto::SigningContext& rSigningContext)
|
|
{
|
|
m_pSigningContext = &rSigningContext;
|
|
}
|
|
|
|
void PDFSignatureHelper::SetDescription(const OUString& rDescription)
|
|
{
|
|
m_aDescription = rDescription;
|
|
}
|
|
|
|
bool PDFSignatureHelper::Sign(const uno::Reference<frame::XModel>& xModel,
|
|
const uno::Reference<io::XInputStream>& xInputStream, bool bAdES)
|
|
{
|
|
std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
|
|
vcl::filter::PDFDocument aDocument;
|
|
if (!aDocument.Read(*pStream))
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "failed to read the document");
|
|
return false;
|
|
}
|
|
|
|
sal_Int32 nPage = 0;
|
|
std::vector<sal_Int8> aSignatureLineShape;
|
|
GetSignatureLineShape(xModel, nPage, aSignatureLineShape);
|
|
if (nPage > 0)
|
|
{
|
|
// UNO page number is 1-based.
|
|
aDocument.SetSignaturePage(nPage - 1);
|
|
}
|
|
if (!aSignatureLineShape.empty())
|
|
{
|
|
aDocument.SetSignatureLine(std::move(aSignatureLineShape));
|
|
}
|
|
|
|
if (!m_pSigningContext || !aDocument.Sign(*m_pSigningContext, m_aDescription, bAdES))
|
|
{
|
|
if (m_pSigningContext && m_pSigningContext->m_xCertificate.is())
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "failed to sign");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uno::Reference<io::XStream> xStream(xInputStream, uno::UNO_QUERY);
|
|
std::unique_ptr<SvStream> pOutStream(utl::UcbStreamHelper::CreateStream(xStream, true));
|
|
if (!aDocument.Write(*pOutStream))
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "failed to write signed data");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PDFSignatureHelper::RemoveSignature(const uno::Reference<io::XInputStream>& xInputStream,
|
|
sal_uInt16 nPosition)
|
|
{
|
|
std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
|
|
vcl::filter::PDFDocument aDocument;
|
|
if (!aDocument.Read(*pStream))
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "failed to read the document");
|
|
return false;
|
|
}
|
|
|
|
if (!aDocument.RemoveSignature(nPosition))
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "failed to remove signature");
|
|
return false;
|
|
}
|
|
|
|
uno::Reference<io::XStream> xStream(xInputStream, uno::UNO_QUERY);
|
|
uno::Reference<io::XTruncate> xTruncate(xStream, uno::UNO_QUERY);
|
|
if (!xTruncate.is())
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "failed to truncate");
|
|
return false;
|
|
}
|
|
xTruncate->truncate();
|
|
std::unique_ptr<SvStream> pOutStream(utl::UcbStreamHelper::CreateStream(xStream, true));
|
|
if (!aDocument.Write(*pOutStream))
|
|
{
|
|
SAL_WARN("xmlsecurity.helper", "failed to write without signature");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|