diff options
Diffstat (limited to 'xmlsecurity/source/helper')
-rw-r--r-- | xmlsecurity/source/helper/documentsignaturehelper.cxx | 63 | ||||
-rw-r--r-- | xmlsecurity/source/helper/documentsignaturemanager.cxx | 12 | ||||
-rw-r--r-- | xmlsecurity/source/helper/ooxmlsecexporter.cxx | 22 | ||||
-rw-r--r-- | xmlsecurity/source/helper/ooxmlsecparser.cxx | 22 | ||||
-rw-r--r-- | xmlsecurity/source/helper/pdfsignaturehelper.cxx | 8 | ||||
-rw-r--r-- | xmlsecurity/source/helper/xmlsignaturehelper.cxx | 161 | ||||
-rw-r--r-- | xmlsecurity/source/helper/xsecctl.cxx | 80 | ||||
-rw-r--r-- | xmlsecurity/source/helper/xsecparser.cxx | 144 | ||||
-rw-r--r-- | xmlsecurity/source/helper/xsecsign.cxx | 30 | ||||
-rw-r--r-- | xmlsecurity/source/helper/xsecverify.cxx | 124 |
10 files changed, 497 insertions, 169 deletions
diff --git a/xmlsecurity/source/helper/documentsignaturehelper.cxx b/xmlsecurity/source/helper/documentsignaturehelper.cxx index f1c48daa3a0c..94f7d9a813fe 100644 --- a/xmlsecurity/source/helper/documentsignaturehelper.cxx +++ b/xmlsecurity/source/helper/documentsignaturehelper.cxx @@ -491,6 +491,29 @@ void DocumentSignatureHelper::writeDigestMethod( xDocumentHandler->endElement("DigestMethod"); } +static void WriteXadesCert( + uno::Reference<xml::sax::XDocumentHandler> const& xDocumentHandler, + SignatureInformation::X509CertInfo const& rCertInfo) +{ + xDocumentHandler->startElement("xd:Cert", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); + xDocumentHandler->startElement("xd:CertDigest", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); + DocumentSignatureHelper::writeDigestMethod(xDocumentHandler); + xDocumentHandler->startElement("DigestValue", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); + assert(!rCertInfo.CertDigest.isEmpty()); + xDocumentHandler->characters(rCertInfo.CertDigest); + xDocumentHandler->endElement("DigestValue"); + xDocumentHandler->endElement("xd:CertDigest"); + xDocumentHandler->startElement("xd:IssuerSerial", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); + xDocumentHandler->startElement("X509IssuerName", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); + xDocumentHandler->characters(rCertInfo.X509IssuerName); + xDocumentHandler->endElement("X509IssuerName"); + xDocumentHandler->startElement("X509SerialNumber", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); + xDocumentHandler->characters(rCertInfo.X509SerialNumber); + xDocumentHandler->endElement("X509SerialNumber"); + xDocumentHandler->endElement("xd:IssuerSerial"); + xDocumentHandler->endElement("xd:Cert"); +} + void DocumentSignatureHelper::writeSignedProperties( const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler, const SignatureInformation& signatureInfo, @@ -507,26 +530,26 @@ void DocumentSignatureHelper::writeSignedProperties( xDocumentHandler->characters(sDate); xDocumentHandler->endElement("xd:SigningTime"); xDocumentHandler->startElement("xd:SigningCertificate", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); - xDocumentHandler->startElement("xd:Cert", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); - xDocumentHandler->startElement("xd:CertDigest", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); - writeDigestMethod(xDocumentHandler); - - xDocumentHandler->startElement("DigestValue", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); - // TODO: this is empty for gpg signatures currently - //assert(!signatureInfo.ouCertDigest.isEmpty()); - xDocumentHandler->characters(signatureInfo.ouCertDigest); - xDocumentHandler->endElement("DigestValue"); - - xDocumentHandler->endElement("xd:CertDigest"); - xDocumentHandler->startElement("xd:IssuerSerial", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); - xDocumentHandler->startElement("X509IssuerName", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); - xDocumentHandler->characters(signatureInfo.ouX509IssuerName); - xDocumentHandler->endElement("X509IssuerName"); - xDocumentHandler->startElement("X509SerialNumber", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); - xDocumentHandler->characters(signatureInfo.ouX509SerialNumber); - xDocumentHandler->endElement("X509SerialNumber"); - xDocumentHandler->endElement("xd:IssuerSerial"); - xDocumentHandler->endElement("xd:Cert"); + assert(signatureInfo.GetSigningCertificate() || !signatureInfo.ouGpgKeyID.isEmpty()); + if (signatureInfo.GetSigningCertificate()) + { + // how should this deal with multiple X509Data elements? + // for now, let's write all of the certificates ... + for (auto const& rData : signatureInfo.X509Datas) + { + for (auto const& it : rData) + { + WriteXadesCert(xDocumentHandler, it); + } + } + } + else + { + // for PGP, write empty mandatory X509IssuerName, X509SerialNumber + SignatureInformation::X509CertInfo temp; + temp.CertDigest = signatureInfo.ouGpgKeyID; + WriteXadesCert(xDocumentHandler, temp); + } xDocumentHandler->endElement("xd:SigningCertificate"); xDocumentHandler->startElement("xd:SignaturePolicyIdentifier", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); xDocumentHandler->startElement("xd:SignaturePolicyImplied", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); diff --git a/xmlsecurity/source/helper/documentsignaturemanager.cxx b/xmlsecurity/source/helper/documentsignaturemanager.cxx index 295522775951..d0ac3d0fc11a 100644 --- a/xmlsecurity/source/helper/documentsignaturemanager.cxx +++ b/xmlsecurity/source/helper/documentsignaturemanager.cxx @@ -587,6 +587,18 @@ void DocumentSignatureManager::read(bool bUseTempStream, bool bCacheLastSignatur bCacheLastSignature); maSignatureHelper.EndMission(); + // this parses the XML independently from ImplVerifySignatures() - check + // certificates here too ... + for (auto const& it : maSignatureHelper.GetSignatureInformations()) + { + if (!it.X509Datas.empty()) + { + uno::Reference<xml::crypto::XSecurityEnvironment> const xSecEnv( + getSecurityEnvironment()); + getSignatureHelper().CheckAndUpdateSignatureInformation(xSecEnv, it); + } + } + maCurrentSignatureInformations = maSignatureHelper.GetSignatureInformations(); } else diff --git a/xmlsecurity/source/helper/ooxmlsecexporter.cxx b/xmlsecurity/source/helper/ooxmlsecexporter.cxx index f09c388686aa..8a60279404f8 100644 --- a/xmlsecurity/source/helper/ooxmlsecexporter.cxx +++ b/xmlsecurity/source/helper/ooxmlsecexporter.cxx @@ -201,13 +201,21 @@ void OOXMLSecExporter::Impl::writeKeyInfo() { m_xDocumentHandler->startElement( "KeyInfo", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); - m_xDocumentHandler->startElement( - "X509Data", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); - m_xDocumentHandler->startElement( - "X509Certificate", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); - m_xDocumentHandler->characters(m_rInformation.ouX509Certificate); - m_xDocumentHandler->endElement("X509Certificate"); - m_xDocumentHandler->endElement("X509Data"); + assert(m_rInformation.GetSigningCertificate()); + for (auto const& rData : m_rInformation.X509Datas) + { + m_xDocumentHandler->startElement( + "X509Data", uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); + for (auto const& it : rData) + { + m_xDocumentHandler->startElement( + "X509Certificate", + uno::Reference<xml::sax::XAttributeList>(new SvXMLAttributeList())); + m_xDocumentHandler->characters(it.X509Certificate); + m_xDocumentHandler->endElement("X509Certificate"); + } + m_xDocumentHandler->endElement("X509Data"); + } m_xDocumentHandler->endElement("KeyInfo"); } diff --git a/xmlsecurity/source/helper/ooxmlsecparser.cxx b/xmlsecurity/source/helper/ooxmlsecparser.cxx index a200de60c07a..a25872fc057d 100644 --- a/xmlsecurity/source/helper/ooxmlsecparser.cxx +++ b/xmlsecurity/source/helper/ooxmlsecparser.cxx @@ -185,9 +185,22 @@ void SAL_CALL OOXMLSecParser::endElement(const OUString& rName) m_pXSecController->setSignatureValue(m_aSignatureValue); m_bInSignatureValue = false; } + else if (rName == "X509Data") + { + std::vector<std::pair<OUString, OUString>> X509IssuerSerials; + std::vector<OUString> X509Certificates; + if (!m_aX509Certificate.isEmpty()) + { + X509Certificates.emplace_back(m_aX509Certificate); + } + if (!m_aX509IssuerName.isEmpty() && !m_aX509SerialNumber.isEmpty()) + { + X509IssuerSerials.emplace_back(m_aX509IssuerName, m_aX509SerialNumber); + } + m_pXSecController->setX509Data(X509IssuerSerials, X509Certificates); + } else if (rName == "X509Certificate") { - m_pXSecController->setX509Certificate(m_aX509Certificate); m_bInX509Certificate = false; } else if (rName == "mdssi:Value") @@ -202,17 +215,18 @@ void SAL_CALL OOXMLSecParser::endElement(const OUString& rName) } else if (rName == "X509IssuerName") { - m_pXSecController->setX509IssuerName(m_aX509IssuerName); m_bInX509IssuerName = false; } else if (rName == "X509SerialNumber") { - m_pXSecController->setX509SerialNumber(m_aX509SerialNumber); m_bInX509SerialNumber = false; } + else if (rName == "xd:Cert") + { + m_pXSecController->setX509CertDigest(m_aCertDigest, css::xml::crypto::DigestID::SHA1, m_aX509IssuerName, m_aX509SerialNumber); + } else if (rName == "xd:CertDigest") { - m_pXSecController->setCertDigest(m_aCertDigest); m_bInCertDigest = false; } else if (rName == "Object") diff --git a/xmlsecurity/source/helper/pdfsignaturehelper.cxx b/xmlsecurity/source/helper/pdfsignaturehelper.cxx index a9ab7a84546c..ffadecfaea14 100644 --- a/xmlsecurity/source/helper/pdfsignaturehelper.cxx +++ b/xmlsecurity/source/helper/pdfsignaturehelper.cxx @@ -543,8 +543,12 @@ PDFSignatureHelper::GetDocumentSignatureInformations( security::DocumentSignatureInformation& rExternal = aRet[i]; rExternal.SignatureIsValid = rInternal.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED; - if (!rInternal.ouX509Certificate.isEmpty()) - rExternal.Signer = xSecEnv->createCertificateFromAscii(rInternal.ouX509Certificate); + if (rInternal.GetSigningCertificate() + && !rInternal.GetSigningCertificate()->X509Certificate.isEmpty()) + { + rExternal.Signer = xSecEnv->createCertificateFromAscii( + rInternal.GetSigningCertificate()->X509Certificate); + } rExternal.PartialDocumentSignature = rInternal.bPartialDocumentSignature; // Verify certificate. diff --git a/xmlsecurity/source/helper/xmlsignaturehelper.cxx b/xmlsecurity/source/helper/xmlsignaturehelper.cxx index 1857e58f94ad..9ba588da9b85 100644 --- a/xmlsecurity/source/helper/xmlsignaturehelper.cxx +++ b/xmlsecurity/source/helper/xmlsignaturehelper.cxx @@ -21,6 +21,7 @@ #include <xmlsignaturehelper.hxx> #include <documentsignaturehelper.hxx> #include <xsecctl.hxx> +#include <biginteger.hxx> #include <xmlsignaturehelper2.hxx> @@ -45,6 +46,8 @@ #include <tools/diagnose_ex.h> #include <sal/log.hxx> +#include <optional> + #define NS_DOCUMENTSIGNATURES "http://openoffice.org/2004/documentsignatures" #define NS_DOCUMENTSIGNATURES_ODF_1_2 "urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0" #define OOXML_SIGNATURE_ORIGIN "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin" @@ -546,4 +549,162 @@ void XMLSignatureHelper::CreateAndWriteOOXMLSignature(const uno::Reference<embed 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. + */ +static auto 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"); + return false; + } + 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"); + return false; + } + } + + // 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()); + return false; + } + start = i; // issuer isn't in the list + break; + } + if (xmlsecurity::EqualDistinguishedNames(certs[i]->getIssuerName(), certs[j]->getSubjectName())) + { + 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()); + return false; + } + 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"); + return false; + } + chain.emplace_back(*start); + + // second, check that there is a chain, no tree or cycle... + for (size_t i = 0; 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())) + { + if (chain.size() != i + 1) // already found issuee? + { + SAL_WARN("xmlsecurity.comp", "X509Data do not form a chain: certificate issued 2 others: " << certs[chain[i]]->getSubjectName()); + return false; + } + 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()); + return false; + } + } + else if (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()); + return false; + } + } + + // success + assert(chain.size() == rX509CertInfos.size()); + for (auto const& it : chain) + { + rSorted.emplace_back(rX509CertInfos[it]); + rCerts.emplace_back(certs[it]); + } + return true; +} + +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 (auto const& rData : rInfo.X509Datas) + { + for (auto const& it : rData) + { + temp.emplace_back(it); + } + } + if (CheckX509Data(xSecEnv, temp, certs, tempResult)) + { + datas.emplace_back(tempResult); + } + + // rInfo is a copy, update the original + mpXSecController->UpdateSignatureInformation(rInfo.nSecurityId, datas); + return certs; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlsecurity/source/helper/xsecctl.cxx b/xmlsecurity/source/helper/xsecctl.cxx index 4cb639d91674..3f18d109494d 100644 --- a/xmlsecurity/source/helper/xsecctl.cxx +++ b/xmlsecurity/source/helper/xsecctl.cxx @@ -734,7 +734,7 @@ void XSecController::exportSignature( xDocumentHandler->startElement( "PGPKeyID", css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); - xDocumentHandler->characters( signatureInfo.ouCertDigest ); + xDocumentHandler->characters(signatureInfo.ouGpgKeyID); xDocumentHandler->endElement( "PGPKeyID" ); /* Write PGPKeyPacket element */ @@ -758,43 +758,50 @@ void XSecController::exportSignature( } else { - /* Write X509Data element */ - xDocumentHandler->startElement( - "X509Data", - css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); + assert(signatureInfo.GetSigningCertificate()); + for (auto const& rData : signatureInfo.X509Datas) { - /* Write X509IssuerSerial element */ + /* Write X509Data element */ xDocumentHandler->startElement( - "X509IssuerSerial", + "X509Data", css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); { - /* Write X509IssuerName element */ - xDocumentHandler->startElement( - "X509IssuerName", - css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); - xDocumentHandler->characters( signatureInfo.ouX509IssuerName ); - xDocumentHandler->endElement( "X509IssuerName" ); - - /* Write X509SerialNumber element */ - xDocumentHandler->startElement( - "X509SerialNumber", - css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); - xDocumentHandler->characters( signatureInfo.ouX509SerialNumber ); - xDocumentHandler->endElement( "X509SerialNumber" ); - } - xDocumentHandler->endElement( "X509IssuerSerial" ); - - /* Write X509Certificate element */ - if (!signatureInfo.ouX509Certificate.isEmpty()) - { - xDocumentHandler->startElement( - "X509Certificate", - css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); - xDocumentHandler->characters( signatureInfo.ouX509Certificate ); - xDocumentHandler->endElement( "X509Certificate" ); + for (auto const& it : rData) + { + /* Write X509IssuerSerial element */ + xDocumentHandler->startElement( + "X509IssuerSerial", + css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); + { + /* Write X509IssuerName element */ + xDocumentHandler->startElement( + "X509IssuerName", + css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); + xDocumentHandler->characters(it.X509IssuerName); + xDocumentHandler->endElement( "X509IssuerName" ); + + /* Write X509SerialNumber element */ + xDocumentHandler->startElement( + "X509SerialNumber", + css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); + xDocumentHandler->characters(it.X509SerialNumber); + xDocumentHandler->endElement( "X509SerialNumber" ); + } + xDocumentHandler->endElement( "X509IssuerSerial" ); + + /* Write X509Certificate element */ + if (!it.X509Certificate.isEmpty()) + { + xDocumentHandler->startElement( + "X509Certificate", + css::uno::Reference< css::xml::sax::XAttributeList > (new SvXMLAttributeList())); + xDocumentHandler->characters(it.X509Certificate); + xDocumentHandler->endElement( "X509Certificate" ); + } + } } + xDocumentHandler->endElement( "X509Data" ); } - xDocumentHandler->endElement( "X509Data" ); } } xDocumentHandler->endElement( "KeyInfo" ); @@ -913,6 +920,15 @@ void XSecController::exportOOXMLSignature(const uno::Reference<embed::XStorage>& aExporter.writeSignature(); } +void XSecController::UpdateSignatureInformation(sal_Int32 const nSecurityId, + std::vector<SignatureInformation::X509Data> const& rDatas) +{ + SignatureInformation aInf( 0 ); + int const nIndex = findSignatureInfor(nSecurityId); + assert(nIndex != -1); // nothing should touch this between parsing and verify + m_vInternalSignatureInformations[nIndex].signatureInfor.X509Datas = rDatas; +} + SignatureInformation XSecController::getSignatureInformation( sal_Int32 nSecurityId ) const { SignatureInformation aInf( 0 ); diff --git a/xmlsecurity/source/helper/xsecparser.cxx b/xmlsecurity/source/helper/xsecparser.cxx index fe138c916448..326515ba5b39 100644 --- a/xmlsecurity/source/helper/xsecparser.cxx +++ b/xmlsecurity/source/helper/xsecparser.cxx @@ -243,98 +243,79 @@ class XSecParser::DsX509CertificateContext : public XSecParser::Context { private: - OUString m_Value; + OUString & m_rValue; public: DsX509CertificateContext(XSecParser & rParser, - std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap) + std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap, + OUString & rValue) : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + , m_rValue(rValue) { } - virtual void EndElement() override - { - m_rParser.m_pXSecController->setX509Certificate(m_Value); - } - virtual void Characters(OUString const& rChars) override { - m_Value += rChars; + m_rValue += rChars; } }; class XSecParser::DsX509SerialNumberContext - : public XSecParser::ReferencedContextImpl + : public XSecParser::Context { private: - OUString m_Value; + OUString & m_rValue; public: DsX509SerialNumberContext(XSecParser & rParser, std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap, - bool const isReferenced) - : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) - { - } - - virtual void EndElement() override + OUString & rValue) + : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + , m_rValue(rValue) { - if (m_isReferenced) - { - m_rParser.m_pXSecController->setX509SerialNumber(m_Value); - } - else - { - SAL_INFO("xmlsecurity.helper", "ignoring unsigned X509SerialNumber"); - } } virtual void Characters(OUString const& rChars) override { - m_Value += rChars; + m_rValue += rChars; } }; class XSecParser::DsX509IssuerNameContext - : public XSecParser::ReferencedContextImpl + : public XSecParser::Context { private: - OUString m_Value; + OUString & m_rValue; public: DsX509IssuerNameContext(XSecParser & rParser, std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap, - bool const isReferenced) - : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) - { - } - - virtual void EndElement() override + OUString & rValue) + : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + , m_rValue(rValue) { - if (m_isReferenced) - { - m_rParser.m_pXSecController->setX509IssuerName(m_Value); - } - else - { - SAL_INFO("xmlsecurity.helper", "ignoring unsigned X509IssuerName"); - } } virtual void Characters(OUString const& rChars) override { - m_Value += rChars; + m_rValue += rChars; } }; class XSecParser::DsX509IssuerSerialContext - : public XSecParser::ReferencedContextImpl + : public XSecParser::Context { + private: + OUString & m_rX509IssuerName; + OUString & m_rX509SerialNumber; + public: DsX509IssuerSerialContext(XSecParser & rParser, std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap, - bool const isReferenced) - : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) + OUString & rIssuerName, OUString & rSerialNumber) + : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + , m_rX509IssuerName(rIssuerName) + , m_rX509SerialNumber(rSerialNumber) { } @@ -344,20 +325,27 @@ class XSecParser::DsX509IssuerSerialContext { if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerName") { - return std::make_unique<DsX509IssuerNameContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + return std::make_unique<DsX509IssuerNameContext>(m_rParser, std::move(pOldNamespaceMap), m_rX509IssuerName); } if (nNamespace == XML_NAMESPACE_DS && rName == "X509SerialNumber") { - return std::make_unique<DsX509SerialNumberContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + return std::make_unique<DsX509SerialNumberContext>(m_rParser, std::move(pOldNamespaceMap), m_rX509SerialNumber); } // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); } }; +/// can't be sure what is supposed to happen here because the spec is clear as mud class XSecParser::DsX509DataContext : public XSecParser::Context { + private: + // sigh... "No ordering is implied by the above constraints." + // so store the ball of mud in vectors and try to figure it out later. + std::vector<std::pair<OUString, OUString>> m_X509IssuerSerials; + std::vector<OUString> m_X509Certificates; + public: DsX509DataContext(XSecParser & rParser, std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap) @@ -365,18 +353,24 @@ class XSecParser::DsX509DataContext { } + virtual void EndElement() override + { + m_rParser.m_pXSecController->setX509Data(m_X509IssuerSerials, m_X509Certificates); + } + virtual std::unique_ptr<Context> CreateChildContext( std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap, sal_uInt16 const nNamespace, OUString const& rName) override { if (nNamespace == XML_NAMESPACE_DS && rName == "X509IssuerSerial") { - // can't require KeyInfo to be signed so pass in *true* - return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), true); + m_X509IssuerSerials.emplace_back(); + return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerSerials.back().first, m_X509IssuerSerials.back().second); } if (nNamespace == XML_NAMESPACE_DS && rName == "X509Certificate") { - return std::make_unique<DsX509CertificateContext>(m_rParser, std::move(pOldNamespaceMap)); + m_X509Certificates.emplace_back(); + return std::make_unique<DsX509CertificateContext>(m_rParser, std::move(pOldNamespaceMap), m_X509Certificates.back()); } // missing: ds:X509SKI, ds:X509SubjectName, ds:X509CRL return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); @@ -968,30 +962,20 @@ class XSecParser::LoSignatureLineContext }; class XSecParser::XadesCertDigestContext - : public XSecParser::ReferencedContextImpl + : public XSecParser::Context { private: - OUString m_Value; - sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1; + OUString & m_rDigestValue; + sal_Int32 & m_rReferenceDigestID; public: XadesCertDigestContext(XSecParser & rParser, std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap, - bool const isReferenced) - : ReferencedContextImpl(rParser, std::move(pOldNamespaceMap), isReferenced) - { - } - - virtual void EndElement() override + OUString & rDigestValue, sal_Int32 & rReferenceDigestID) + : XSecParser::Context(rParser, std::move(pOldNamespaceMap)) + , m_rDigestValue(rDigestValue) + , m_rReferenceDigestID(rReferenceDigestID) { - if (m_isReferenced) - { - m_rParser.m_pXSecController->setCertDigest(m_Value/* FIXME , m_nReferenceDigestID*/); - } - else - { - SAL_INFO("xmlsecurity.helper", "ignoring unsigned CertDigest"); - } } virtual std::unique_ptr<Context> CreateChildContext( @@ -1000,11 +984,11 @@ class XSecParser::XadesCertDigestContext { if (nNamespace == XML_NAMESPACE_DS && rName == "DigestMethod") { - return std::make_unique<DsDigestMethodContext>(m_rParser, std::move(pOldNamespaceMap), m_nReferenceDigestID); + return std::make_unique<DsDigestMethodContext>(m_rParser, std::move(pOldNamespaceMap), m_rReferenceDigestID); } if (nNamespace == XML_NAMESPACE_DS && rName == "DigestValue") { - return std::make_unique<DsDigestValueContext>(m_rParser, std::move(pOldNamespaceMap), m_Value); + return std::make_unique<DsDigestValueContext>(m_rParser, std::move(pOldNamespaceMap), m_rDigestValue); } return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); } @@ -1013,6 +997,12 @@ class XSecParser::XadesCertDigestContext class XSecParser::XadesCertContext : public XSecParser::ReferencedContextImpl { + private: + sal_Int32 m_nReferenceDigestID = css::xml::crypto::DigestID::SHA1; + OUString m_CertDigest; + OUString m_X509IssuerName; + OUString m_X509SerialNumber; + public: XadesCertContext(XSecParser & rParser, std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap, @@ -1021,17 +1011,29 @@ class XSecParser::XadesCertContext { } + virtual void EndElement() override + { + if (m_isReferenced) + { + m_rParser.m_pXSecController->setX509CertDigest(m_CertDigest, m_nReferenceDigestID, m_X509IssuerName, m_X509SerialNumber); + } + else + { + SAL_INFO("xmlsecurity.helper", "ignoring unsigned xades:Cert"); + } + } + virtual std::unique_ptr<Context> CreateChildContext( std::unique_ptr<SvXMLNamespaceMap> pOldNamespaceMap, sal_uInt16 const nNamespace, OUString const& rName) override { if (nNamespace == XML_NAMESPACE_XADES132 && rName == "CertDigest") { - return std::make_unique<XadesCertDigestContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + return std::make_unique<XadesCertDigestContext>(m_rParser, std::move(pOldNamespaceMap), m_CertDigest, m_nReferenceDigestID); } if (nNamespace == XML_NAMESPACE_XADES132 && rName == "IssuerSerial") { - return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_isReferenced); + return std::make_unique<DsX509IssuerSerialContext>(m_rParser, std::move(pOldNamespaceMap), m_X509IssuerName, m_X509SerialNumber); } return XSecParser::Context::CreateChildContext(std::move(pOldNamespaceMap), nNamespace, rName); } diff --git a/xmlsecurity/source/helper/xsecsign.cxx b/xmlsecurity/source/helper/xsecsign.cxx index 1e1688767f00..fd33a320d9bd 100644 --- a/xmlsecurity/source/helper/xsecsign.cxx +++ b/xmlsecurity/source/helper/xsecsign.cxx @@ -193,6 +193,7 @@ void XSecController::signAStream( sal_Int32 securityId, const OUString& uri, boo } } +// note: this is called when creating a new signature from scratch void XSecController::setX509Certificate( sal_Int32 nSecurityId, const OUString& ouX509IssuerName, @@ -206,10 +207,13 @@ void XSecController::setX509Certificate( if ( index == -1 ) { InternalSignatureInformation isi(nSecurityId, nullptr); - isi.signatureInfor.ouX509IssuerName = ouX509IssuerName; - isi.signatureInfor.ouX509SerialNumber = ouX509SerialNumber; - isi.signatureInfor.ouX509Certificate = ouX509Cert; - isi.signatureInfor.ouCertDigest = ouX509CertDigest; + isi.signatureInfor.X509Datas.clear(); + isi.signatureInfor.X509Datas.emplace_back(); + isi.signatureInfor.X509Datas.back().emplace_back(); + isi.signatureInfor.X509Datas.back().back().X509IssuerName = ouX509IssuerName; + isi.signatureInfor.X509Datas.back().back().X509SerialNumber = ouX509SerialNumber; + isi.signatureInfor.X509Datas.back().back().X509Certificate = ouX509Cert; + isi.signatureInfor.X509Datas.back().back().CertDigest = ouX509CertDigest; isi.signatureInfor.eAlgorithmID = eAlgorithmID; m_vInternalSignatureInformations.push_back( isi ); } @@ -217,16 +221,19 @@ void XSecController::setX509Certificate( { SignatureInformation &si = m_vInternalSignatureInformations[index].signatureInfor; - si.ouX509IssuerName = ouX509IssuerName; - si.ouX509SerialNumber = ouX509SerialNumber; - si.ouX509Certificate = ouX509Cert; - si.ouCertDigest = ouX509CertDigest; + si.X509Datas.clear(); + si.X509Datas.emplace_back(); + si.X509Datas.back().emplace_back(); + si.X509Datas.back().back().X509IssuerName = ouX509IssuerName; + si.X509Datas.back().back().X509SerialNumber = ouX509SerialNumber; + si.X509Datas.back().back().X509Certificate = ouX509Cert; + si.X509Datas.back().back().CertDigest = ouX509CertDigest; } } void XSecController::setGpgCertificate( sal_Int32 nSecurityId, - const OUString& ouCertDigest, + const OUString& ouKeyDigest, const OUString& ouCert, const OUString& ouOwner) { @@ -237,16 +244,17 @@ void XSecController::setGpgCertificate( InternalSignatureInformation isi(nSecurityId, nullptr); isi.signatureInfor.ouGpgCertificate = ouCert; isi.signatureInfor.ouGpgOwner = ouOwner; - isi.signatureInfor.ouCertDigest = ouCertDigest; + isi.signatureInfor.ouGpgKeyID = ouKeyDigest; m_vInternalSignatureInformations.push_back( isi ); } else { SignatureInformation &si = m_vInternalSignatureInformations[index].signatureInfor; + si.X509Datas.clear(); // it is a PGP signature now si.ouGpgCertificate = ouCert; si.ouGpgOwner = ouOwner; - si.ouCertDigest = ouCertDigest; + si.ouGpgKeyID = ouKeyDigest; } } diff --git a/xmlsecurity/source/helper/xsecverify.cxx b/xmlsecurity/source/helper/xsecverify.cxx index 4b0a8a9759d2..adec866f9ddb 100644 --- a/xmlsecurity/source/helper/xsecverify.cxx +++ b/xmlsecurity/source/helper/xsecverify.cxx @@ -22,6 +22,7 @@ #include <xsecctl.hxx> #include "xsecparser.hxx" #include "ooxmlsecparser.hxx" +#include <biginteger.hxx> #include <framework/signatureverifierimpl.hxx> #include <framework/saxeventkeeperimpl.hxx> #include <gpg/xmlsignature_gpgimpl.hxx> @@ -240,7 +241,9 @@ void XSecController::setReferenceCount() const xReferenceCollector->setReferenceCount( referenceCount ); } -void XSecController::setX509IssuerName( OUString const & ouX509IssuerName ) +void XSecController::setX509Data( + std::vector<std::pair<OUString, OUString>> & rX509IssuerSerials, + std::vector<OUString> const& rX509Certificates) { if (m_vInternalSignatureInformations.empty()) { @@ -248,29 +251,52 @@ void XSecController::setX509IssuerName( OUString const & ouX509IssuerName ) return; } InternalSignatureInformation &isi = m_vInternalSignatureInformations.back(); - isi.signatureInfor.ouX509IssuerName = ouX509IssuerName; -} - -void XSecController::setX509SerialNumber( OUString const & ouX509SerialNumber ) -{ - if (m_vInternalSignatureInformations.empty()) + SignatureInformation::X509Data data; + // due to the excessive flexibility of the spec it's possible that there + // is both a reference to a cert and the cert itself in one X509Data + for (OUString const& it : rX509Certificates) { - SAL_INFO("xmlsecurity.helper","XSecController::setX509SerialNumber: no signature"); - return; + try + { + data.emplace_back(); + data.back().X509Certificate = it; + uno::Reference<xml::crypto::XSecurityEnvironment> const xSecEnv(m_xSecurityContext->getSecurityEnvironment()); + uno::Reference<security::XCertificate> const xCert(xSecEnv->createCertificateFromAscii(it)); + if (!xCert.is()) + { + SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate"); + continue; // will be handled in CheckX509Data + } + OUString const issuerName(xCert->getIssuerName()); + OUString const serialNumber(xmlsecurity::bigIntegerToNumericString(xCert->getSerialNumber())); + auto const iter = std::find_if(rX509IssuerSerials.begin(), rX509IssuerSerials.end(), + [&](auto const& rX509IssuerSerial) { + return xmlsecurity::EqualDistinguishedNames(issuerName, rX509IssuerSerial.first) + && serialNumber == rX509IssuerSerial.second; + }); + if (iter != rX509IssuerSerials.end()) + { + data.back().X509IssuerName = iter->first; + data.back().X509SerialNumber = iter->second; + rX509IssuerSerials.erase(iter); + } + } + catch (uno::Exception const&) + { + SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate"); + } } - InternalSignatureInformation &isi = m_vInternalSignatureInformations.back(); - isi.signatureInfor.ouX509SerialNumber = ouX509SerialNumber; -} - -void XSecController::setX509Certificate( OUString const & ouX509Certificate ) -{ - if (m_vInternalSignatureInformations.empty()) + // now handle any that are left... + for (auto const& it : rX509IssuerSerials) { - SAL_INFO("xmlsecurity.helper","XSecController::setX509Certificate: no signature"); - return; + data.emplace_back(); + data.back().X509IssuerName = it.first; + data.back().X509SerialNumber = it.second; + } + if (!data.empty()) + { + isi.signatureInfor.X509Datas.push_back(data); } - InternalSignatureInformation &isi = m_vInternalSignatureInformations.back(); - isi.signatureInfor.ouX509Certificate = ouX509Certificate; } void XSecController::setSignatureValue( OUString const & ouSignatureValue ) @@ -380,13 +406,67 @@ void XSecController::setSignatureBytes(const uno::Sequence<sal_Int8>& rBytes) rInformation.signatureInfor.aSignatureBytes = rBytes; } -void XSecController::setCertDigest(const OUString& rCertDigest) +void XSecController::setX509CertDigest( + OUString const& rCertDigest, sal_Int32 const /*TODO nReferenceDigestID*/, + std::u16string_view const& rX509IssuerName, std::u16string_view const& rX509SerialNumber) { if (m_vInternalSignatureInformations.empty()) return; InternalSignatureInformation& rInformation = m_vInternalSignatureInformations.back(); - rInformation.signatureInfor.ouCertDigest = rCertDigest; + for (auto & rData : rInformation.signatureInfor.X509Datas) + { + for (auto & it : rData) + { + if (xmlsecurity::EqualDistinguishedNames(it.X509IssuerName, rX509IssuerName) + && it.X509SerialNumber == rX509SerialNumber) + { + it.CertDigest = rCertDigest; + return; + } + } + } + // fall-back: read the actual certificates + for (auto & rData : rInformation.signatureInfor.X509Datas) + { + for (auto & it : rData) + { + if (!it.X509Certificate.isEmpty()) + { + try + { + uno::Reference<xml::crypto::XSecurityEnvironment> const xSecEnv(m_xSecurityContext->getSecurityEnvironment()); + uno::Reference<security::XCertificate> const xCert(xSecEnv->createCertificateFromAscii(it.X509Certificate)); + if (!xCert.is()) + { + SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate"); + } + else if (xmlsecurity::EqualDistinguishedNames(xCert->getIssuerName(),rX509IssuerName) + && xmlsecurity::bigIntegerToNumericString(xCert->getSerialNumber()) == rX509SerialNumber) + { + it.CertDigest = rCertDigest; + // note: testInsertCertificate_PEM_DOCX requires these! + it.X509SerialNumber = rX509SerialNumber; + it.X509IssuerName = rX509IssuerName; + return; + } + } + catch (uno::Exception const&) + { + SAL_INFO("xmlsecurity.helper", "cannot parse X509Certificate"); + } + } + } + } + if (!rInformation.signatureInfor.ouGpgCertificate.isEmpty()) + { + SAL_INFO_IF(rCertDigest != rInformation.signatureInfor.ouGpgKeyID, + "xmlsecurity.helper", "PGPKeyID vs CertDigest mismatch"); + } + else + { + SAL_INFO("xmlsecurity.helper", "cannot find X509Data for CertDigest"); + } } namespace { |