/* -*- 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/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
/* * connect XML writer to output stream
*/
xSaxWriter->setOutputStream( xOutputStream );
/* * write the xml context for signatures
*/
rtl::Reference<comphelper::AttributeList> pAttributeList = new comphelper::AttributeList();
OUString sNamespace; if (mbODFPre1_2)
sNamespace = NS_DOCUMENTSIGNATURES; else
sNamespace = NS_DOCUMENTSIGNATURES_ODF_1_2;
if (rInformation.aSignatureBytes.hasElements()) // This is a signature roundtrip, just write back the signature as-is.
xOutputStream->writeBytes(rInformation.aSignatureBytes); else
{
uno::Reference<xml::sax::XWriter> xSaxWriter = xml::sax::Writer::create(mxCtx);
xSaxWriter->setOutputStream(xOutputStream);
xSaxWriter->startDocument();
for (sal_Int32 i = 0; i < aRelationsInfo.getLength(); ++i)
{ const uno::Sequence<beans::StringPair>& rRelation = aRelationsInfo[i]; if (std::any_of(rRelation.begin(), rRelation.end(), lcl_isSignatureType))
{ auto it = std::find_if(rRelation.begin(), rRelation.end(), [](const beans::StringPair& rPair) { return rPair.First == "Target"; }); if (it != rRelation.end())
{ if (xStorage.is() && !xStorage->hasByName(it->Second))
{
SAL_WARN("xmlsecurity.helper", "expected stream, but not found: " << it->Second); continue;
}
uno::Reference<io::XInputStream> xInputStream(xStorage->openStreamElement(it->Second, nOpenMode), uno::UNO_QUERY); if (!ReadAndVerifySignatureStorageStream(xInputStream)) returnfalse;
// By default, we cache. If it's requested, then we don't cache the last signature. bool bCache = true; if (!bCacheLastSignature && i == aRelationsInfo.getLength() - 1)
bCache = false;
if (!bCache) continue; // Store the contents of the stream as is, in case we need to write it back later.
xInputStream.clear();
xInputStream.set(xStorage->openStreamElement(it->Second, nOpenMode), uno::UNO_QUERY);
uno::Reference<beans::XPropertySet> xPropertySet(xInputStream, uno::UNO_QUERY); if (!xPropertySet.is()) continue;
// Do we have a relation already? bool bHaveRelation = false; int nCount = 0; for (const uno::Sequence<beans::StringPair>& rRelation : aRelationsInfo)
{ auto aRelation = comphelper::sequenceToContainer< std::vector<beans::StringPair> >(rRelation); if (std::any_of(aRelation.begin(), aRelation.end(), lcl_isSignatureOriginType))
{
bHaveRelation = true; break;
}
++nCount;
}
if (!bHaveRelation && bAdd)
{ // No, and have to add one.
std::vector<beans::StringPair> aRelation;
aRelation.emplace_back("Id", "rId" + OUString::number(++nCount));
aRelation.emplace_back("Type", OOXML_SIGNATURE_ORIGIN);
aRelation.emplace_back("Target", "_xmlsignatures/origin.sigs");
aRelationsInfo.push_back(comphelper::containerToSequence(aRelation));
} elseif (bHaveRelation && !bAdd)
{ // Yes, and need to remove it. for (std::vector< uno::Sequence<beans::StringPair> >::iterator it = aRelationsInfo.begin(); it != aRelationsInfo.end();)
{ auto aRelation = comphelper::sequenceToContainer< std::vector<beans::StringPair> >(*it); if (std::any_of(aRelation.begin(), aRelation.end(), lcl_isSignatureOriginType))
it = aRelationsInfo.erase(it); else
++it;
}
}
mbError = false; if (!mpXSecController->WriteOOXMLSignature(xRootStorage, xSaxWriter))
mbError = true;
xSaxWriter->endDocument();
}
/** check this constraint from xmldsig-core 4.5.4:
All certificates appearing in an X509Data element MUST relate to the validation key by either containing it or being part of a certification chain that terminates in a certificate containing the validation key.
*/ staticauto CheckX509Data(
uno::Reference<xml::crypto::XSecurityEnvironment> const& xSecEnv,
std::vector<SignatureInformation::X509CertInfo> const& rX509CertInfos,
std::vector<uno::Reference<security::XCertificate>> & rCerts,
std::vector<SignatureInformation::X509CertInfo> & rSorted) -> bool
{
assert(rCerts.empty());
assert(rSorted.empty()); if (rX509CertInfos.empty())
{
SAL_WARN("xmlsecurity.comp", "no X509Data"); returnfalse;
}
std::vector<uno::Reference<security::XCertificate>> certs; for (SignatureInformation::X509CertInfo const& it : rX509CertInfos)
{ if (!it.X509Certificate.isEmpty())
{
certs.emplace_back(xSecEnv->createCertificateFromAscii(it.X509Certificate));
} else
{
certs.emplace_back(xSecEnv->getCertificate(
it.X509IssuerName,
xmlsecurity::numericStringToBigInteger(it.X509SerialNumber)));
} if (!certs.back().is())
{
SAL_WARN("xmlsecurity.comp", "X509Data cannot be parsed"); returnfalse;
}
}
// first, search one whose issuer isn't in the list, or a self-signed one
std::optional<size_t> start; for (size_t i = 0; i < certs.size(); ++i)
{ for (size_t j = 0; ; ++j)
{ if (j == certs.size())
{ if (start)
{
SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate has no issuer but already have start of chain: " << certs[i]->getSubjectName()); returnfalse;
}
start = i; // issuer isn't in the list break;
} if (xmlsecurity::EqualDistinguishedNames(certs[i]->getIssuerName(), certs[j]->getSubjectName(), xmlsecurity::NOCOMPAT))
{ if (i == j) // self signed
{ if (start)
{
SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate is self-signed but already have start of chain: " << certs[i]->getSubjectName()); returnfalse;
}
start = i;
} break;
}
}
}
std::vector<size_t> chain; if (!start)
{ // this can only be a cycle?
SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: cycle detected"); returnfalse;
}
chain.emplace_back(*start);
// second, check that there is a chain, no tree or cycle... for (size_t i = 0, certs_size = certs.size(); i < certs_size; ++i)
{
assert(chain.size() == i + 1); for (size_t j = 0; j < certs_size; ++j)
{ if (chain[i] != j)
{ if (xmlsecurity::EqualDistinguishedNames(
certs[chain[i]]->getSubjectName(), certs[j]->getIssuerName(), xmlsecurity::NOCOMPAT))
{ if (chain.size() != i + 1) // already found issue?
{
SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate issued 2 others: " << certs[chain[i]]->getSubjectName()); returnfalse;
}
chain.emplace_back(j);
}
}
} if (i == certs_size - 1)
{ // last one: must be a leaf if (chain.size() != i + 1)
{
SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate in cycle: " << certs[chain[i]]->getSubjectName()); returnfalse;
}
} elseif (chain.size() != i + 2)
{ // not issuer of another?
SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate issued 0 others: " << certs[chain[i]]->getSubjectName()); returnfalse;
}
}
// success
assert(chain.size() == rX509CertInfos.size()); for (autoconst& it : chain)
{
rSorted.emplace_back(rX509CertInfos[it]);
rCerts.emplace_back(certs[it]);
} returntrue;
}
std::vector<uno::Reference<security::XCertificate>>
XMLSignatureHelper::CheckAndUpdateSignatureInformation(
uno::Reference<xml::crypto::XSecurityEnvironment> const& xSecEnv,
SignatureInformation const& rInfo)
{ // if the check fails, it's not possible to determine which X509Data // contained the signing certificate - the UI cannot display something // useful in this case, so prevent anything misleading by clearing the // X509Datas.
std::vector<uno::Reference<security::XCertificate>> certs;
std::vector<SignatureInformation::X509Data> datas; // TODO: for now, just merge all X509Datas together for checking... // (this will probably break round-trip of signature with multiple X509Data, // no idea if that is a problem)
SignatureInformation::X509Data temp;
SignatureInformation::X509Data tempResult; for (autoconst& rData : rInfo.X509Datas)
{ for (autoconst& it : rData)
{
temp.emplace_back(it);
}
} if (CheckX509Data(xSecEnv, temp, certs, tempResult))
{ if (rInfo.maEncapsulatedX509Certificates.empty()) // optional, XAdES
{
datas.emplace_back(tempResult);
} else
{ // check for consistency between X509Data and EncapsulatedX509Certificate // (LO produces just the signing certificate in X509Data and // the entire chain in EncapsulatedX509Certificate so in this case // using EncapsulatedX509Certificate yields additional intermediate // certificates that may help in verifying)
std::vector<SignatureInformation::X509CertInfo> encapsulatedCertInfos; for (OUString const& it : rInfo.maEncapsulatedX509Certificates)
{
encapsulatedCertInfos.emplace_back();
encapsulatedCertInfos.back().X509Certificate = it;
}
std::vector<uno::Reference<security::XCertificate>> encapsulatedCerts;
SignatureInformation::X509Data encapsulatedResult; if (CheckX509Data(xSecEnv, encapsulatedCertInfos, encapsulatedCerts, encapsulatedResult))
{ autoconst pXCertificate(dynamic_cast<xmlsecurity::Certificate*>(certs.back().get())); autoconst pECertificate(dynamic_cast<xmlsecurity::Certificate*>(encapsulatedCerts.back().get()));
assert(pXCertificate && pECertificate); // was just created by CheckX509Data if (pXCertificate->getSHA256Thumbprint() == pECertificate->getSHA256Thumbprint())
{ // both are chains - take the longer one if (encapsulatedCerts.size() < certs.size())
{
datas.emplace_back(tempResult);
} else
{ #if 0 // extra info needed in testSigningMultipleTimes_ODT // ... but with it, it fails with BROKEN signature? // fails even on the first signature, because somehow // the xd:SigningCertificate element was signed // containing only one certificate, but in the final // file it contains all 3 certificates due to this here. for (size_t i = 0; i < encapsulatedResult.size(); ++i)
{
encapsulatedResult[i].X509IssuerName = encapsulatedCerts[i]->getIssuerName();
encapsulatedResult[i].X509SerialNumber = xmlsecurity::bigIntegerToNumericString(encapsulatedCerts[i]->getSerialNumber());
encapsulatedResult[i].X509Subject = encapsulatedCerts[i]->getSubjectName(); autoconst pCertificate(dynamic_cast<xmlsecurity::Certificate*>(encapsulatedCerts[i].get()));
assert(pCertificate); // this was just created by CheckX509Data
OUStringBuffer aBuffer;
comphelper::Base64::encode(aBuffer, pCertificate->getSHA256Thumbprint());
encapsulatedResult[i].CertDigest = aBuffer.makeStringAndClear();
}
datas.emplace_back(encapsulatedResult); #else // keep the X509Data stuff in datas but return the // longer EncapsulatedX509Certificate chain
datas.emplace_back(tempResult); #endif
certs = std::move(encapsulatedCerts); // overwrite this seems easier
}
} else
{
SAL_WARN("xmlsecurity.comp", "X509Data and EncapsulatedX509Certificate contain different certificates");
}
}
}
}
// rInfo is a copy, update the original
mpXSecController->UpdateSignatureInformation(rInfo.nSecurityId, std::move(datas)); return certs;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.