diff options
22 files changed, 679 insertions, 214 deletions
diff --git a/include/svl/sigstruct.hxx b/include/svl/sigstruct.hxx index 7a0296fa9fae..f00cbce6e4b8 100644 --- a/include/svl/sigstruct.hxx +++ b/include/svl/sigstruct.hxx @@ -89,9 +89,30 @@ struct SignatureInformation sal_Int32 nSecurityId; css::xml::crypto::SecurityOperationStatus nStatus; SignatureReferenceInformations vSignatureReferenceInfors; - OUString ouX509IssuerName; - OUString ouX509SerialNumber; - OUString ouX509Certificate; + struct X509CertInfo + { + OUString X509IssuerName; + OUString X509SerialNumber; + OUString X509Certificate; + /// OOXML certificate SHA-256 digest, empty for ODF except when doing XAdES signature. + OUString CertDigest; + /// The certificate owner (aka subject). + OUString X509Subject; + }; + typedef std::vector<X509CertInfo> X509Data; + // note: at parse time, it's unkown which one is the signing certificate; + // ImplVerifySignatures() figures it out and puts it at the back + std::vector<X509Data> X509Datas; + + X509CertInfo const* GetSigningCertificate() const + { + if (X509Datas.empty()) + { + return nullptr; + } + assert(!X509Datas.back().empty()); + return & X509Datas.back().back(); + } OUString ouGpgKeyID; OUString ouGpgCertificate; @@ -124,8 +145,6 @@ struct SignatureInformation OUString ouDescription; /// The Id attribute of the <SignatureProperty> element that contains the <dc:description>. OUString ouDescriptionPropertyId; - /// OOXML certificate SHA-256 digest, empty for ODF except when doing XAdES signature. - OUString ouCertDigest; /// Valid and invalid signature line images css::uno::Reference<css::graphic::XGraphic> aValidSignatureImage; css::uno::Reference<css::graphic::XGraphic> aInvalidSignatureImage; @@ -140,9 +159,6 @@ struct SignatureInformation /// For PDF: the byte range doesn't cover the whole document. bool bPartialDocumentSignature; - /// The certificate owner (aka subject). - OUString ouSubject; - svl::crypto::SignatureMethodAlgorithm eAlgorithmID; SignatureInformation( sal_Int32 nId ) diff --git a/svl/source/crypto/cryptosign.cxx b/svl/source/crypto/cryptosign.cxx index 5a3f0271c40d..1b882bb89deb 100644 --- a/svl/source/crypto/cryptosign.cxx +++ b/svl/source/crypto/cryptosign.cxx @@ -2097,8 +2097,12 @@ bool Signing::Verify(const std::vector<unsigned char>& aData, aDerCert[i] = pCertificate->derCert.data[i]; OUStringBuffer aBuffer; comphelper::Base64::encode(aBuffer, aDerCert); - rInformation.ouX509Certificate = aBuffer.makeStringAndClear(); - rInformation.ouSubject = OUString(pCertificate->subjectName, PL_strlen(pCertificate->subjectName), RTL_TEXTENCODING_UTF8); + SignatureInformation::X509Data temp; + temp.emplace_back(); + temp.back().X509Certificate = aBuffer.makeStringAndClear(); + temp.back().X509Subject = OUString(pCertificate->subjectName, PL_strlen(pCertificate->subjectName), RTL_TEXTENCODING_UTF8); + rInformation.X509Datas.clear(); + rInformation.X509Datas.emplace_back(temp); } PRTime nSigningTime; @@ -2277,8 +2281,12 @@ bool Signing::Verify(const std::vector<unsigned char>& aData, aDerCert[i] = pSignerCertContext->pbCertEncoded[i]; OUStringBuffer aBuffer; comphelper::Base64::encode(aBuffer, aDerCert); - rInformation.ouX509Certificate = aBuffer.makeStringAndClear(); - rInformation.ouSubject = GetSubjectName(pSignerCertContext); + SignatureInformation::X509Data temp; + temp.emplace_back(); + temp.back().X509Certificate = aBuffer.makeStringAndClear(); + temp.back().X509Subject = GetSubjectName(pSignerCertContext); + rInformation.X509Datas.clear(); + rInformation.X509Datas.emplace_back(temp); } if (bNonDetached) diff --git a/sw/source/core/edit/edfcol.cxx b/sw/source/core/edit/edfcol.cxx index 2b49ee16ecc8..abbed5e40e94 100644 --- a/sw/source/core/edit/edfcol.cxx +++ b/sw/source/core/edit/edfcol.cxx @@ -411,7 +411,8 @@ std::pair<bool, OUString> lcl_MakeParagraphSignatureFieldText(const SignatureDes valid = valid && aInfo.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED; - msg = SwResId(STR_SIGNED_BY) + ": " + aInfo.ouSubject + ", " + + assert(aInfo.GetSigningCertificate()); // it was valid + msg = SwResId(STR_SIGNED_BY) + ": " + aInfo.GetSigningCertificate()->X509Subject + ", " + aDescr.msDate; msg += (!aDescr.msUsage.isEmpty() ? (" (" + aDescr.msUsage + "): ") : OUString(": ")); msg += (valid ? SwResId(STR_VALID) : SwResId(STR_INVALID)); diff --git a/xmlsecurity/inc/biginteger.hxx b/xmlsecurity/inc/biginteger.hxx index d07ecf45d8af..8b4d8a9143b5 100644 --- a/xmlsecurity/inc/biginteger.hxx +++ b/xmlsecurity/inc/biginteger.hxx @@ -31,6 +31,9 @@ namespace xmlsecurity { XSECXMLSEC_DLLPUBLIC OUString bigIntegerToNumericString( const css::uno::Sequence< sal_Int8 >& serial ); XSECXMLSEC_DLLPUBLIC css::uno::Sequence< sal_Int8 > numericStringToBigInteger ( const OUString& serialNumber ); + +XSECXMLSEC_DLLPUBLIC bool EqualDistinguishedNames(OUString const& rName1, + OUString const& rName2); } #endif diff --git a/xmlsecurity/inc/xmlsignaturehelper.hxx b/xmlsecurity/inc/xmlsignaturehelper.hxx index 0fcbd665251f..2456ddd437ec 100644 --- a/xmlsecurity/inc/xmlsignaturehelper.hxx +++ b/xmlsecurity/inc/xmlsignaturehelper.hxx @@ -28,6 +28,9 @@ #include "xmlsignaturehelper.hxx" #include "xsecctl.hxx" +#include <com/sun/star/security/XCertificate.hpp> +#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp> + class DateTime; class UriBindingHelper; @@ -93,6 +96,15 @@ public: // After signing/verifying, get information about signatures SignatureInformation GetSignatureInformation( sal_Int32 nSecurityId ) const; SignatureInformations GetSignatureInformations() const; + /// ImplVerifySignature calls this to figure out which X509Data is the + /// signing certificate and update the internal state with the result. + /// @return + /// A sequence with the signing certificate at the back on success. + /// An empty sequence on failure. + std::vector<css::uno::Reference<css::security::XCertificate>> + CheckAndUpdateSignatureInformation( + css::uno::Reference<css::xml::crypto::XSecurityEnvironment> const& xSecEnv, + SignatureInformation const& rInfo); // See XSecController for documentation void StartMission(const css::uno::Reference<css::xml::crypto::XXMLSecurityContext>& xSecurityContext); diff --git a/xmlsecurity/inc/xsecctl.hxx b/xmlsecurity/inc/xsecctl.hxx index 782e286ae85a..7ce35cea22bf 100644 --- a/xmlsecurity/inc/xsecctl.hxx +++ b/xmlsecurity/inc/xsecctl.hxx @@ -263,9 +263,13 @@ private: sal_Int32 nDigestID ); void setReferenceCount() const; - void setX509IssuerName( OUString const & ouX509IssuerName ); - void setX509SerialNumber( OUString const & ouX509SerialNumber ); - void setX509Certificate( OUString const & ouX509Certificate ); + void setX509Data( + std::vector<std::pair<OUString, OUString>> & rX509IssuerSerials, + std::vector<OUString> const& rX509Certificates); + void setX509CertDigest( + OUString const& rCertDigest, sal_Int32 const nReferenceDigestID, + OUString const& rX509IssuerName, OUString const& rX509SerialNumber); + void setSignatureValue( OUString const & ouSignatureValue ); void setDigestValue( sal_Int32 nDigestID, OUString const & ouDigestValue ); void setGpgKeyID( OUString const & ouKeyID ); @@ -274,7 +278,6 @@ private: void setDate(OUString const& rId, OUString const& ouDate); void setDescription(OUString const& rId, OUString const& rDescription); - void setCertDigest(const OUString& rCertDigest); void setValidSignatureImage(const OUString& rValidSigImg); void setInvalidSignatureImage(const OUString& rInvalidSigImg); void setSignatureLineId(const OUString& rSignatureLineId); @@ -303,6 +306,9 @@ public: SignatureInformation getSignatureInformation( sal_Int32 nSecurityId ) const; SignatureInformations getSignatureInformations() const; + /// only verify can figure out which X509Data is the signing certificate + void UpdateSignatureInformation(sal_Int32 nSecurityId, + std::vector<SignatureInformation::X509Data> const& rDatas); static void exportSignature( const css::uno::Reference< css::xml::sax::XDocumentHandler >& xDocumentHandler, diff --git a/xmlsecurity/source/component/documentdigitalsignatures.cxx b/xmlsecurity/source/component/documentdigitalsignatures.cxx index 71251eda2f8a..59759e70999b 100644 --- a/xmlsecurity/source/component/documentdigitalsignatures.cxx +++ b/xmlsecurity/source/component/documentdigitalsignatures.cxx @@ -525,30 +525,36 @@ DocumentDigitalSignatures::ImplVerifySignatures( const SignatureInformation& rInfo = aSignInfos[n]; css::security::DocumentSignatureInformation& rSigInfo = arInfos[n]; - if (rInfo.ouGpgCertificate.isEmpty()) // X.509 + if (!rInfo.X509Datas.empty()) // X.509 { - if (!rInfo.ouX509Certificate.isEmpty()) - rSigInfo.Signer = xSecEnv->createCertificateFromAscii(rInfo.ouX509Certificate); - if (!rSigInfo.Signer.is()) - rSigInfo.Signer = xSecEnv->getCertificate( - rInfo.ouX509IssuerName, - xmlsecurity::numericStringToBigInteger(rInfo.ouX509SerialNumber)); - - // On Windows checking the certificate path is buggy. It does name matching (issuer, subject name) - // to find the parent certificate. It does not take into account that there can be several certificates - // with the same subject name. - try + std::vector<uno::Reference<XCertificate>> certs( + rSignatureHelper.CheckAndUpdateSignatureInformation( + xSecEnv, rInfo)); + if (certs.empty()) { - rSigInfo.CertificateStatus = xSecEnv->verifyCertificate( - rSigInfo.Signer, Sequence<Reference<css::security::XCertificate>>()); + rSigInfo.CertificateStatus = css::security::CertificateValidity::INVALID; } - catch (SecurityException&) + else { - OSL_FAIL("Verification of certificate failed"); - rSigInfo.CertificateStatus = css::security::CertificateValidity::INVALID; + rSigInfo.Signer = certs.back(); + // get only intermediates + certs.pop_back(); + // On Windows checking the certificate path is buggy. It does name matching (issuer, subject name) + // to find the parent certificate. It does not take into account that there can be several certificates + // with the same subject name. + try + { + rSigInfo.CertificateStatus = xSecEnv->verifyCertificate( + rSigInfo.Signer, comphelper::containerToSequence(certs)); + } + catch (SecurityException&) + { + SAL_WARN("xmlsecurity.comp", "Verification of certificate failed"); + rSigInfo.CertificateStatus = css::security::CertificateValidity::INVALID; + } } } - else if (xGpgSecEnv.is()) // GPG + else if (!rInfo.ouGpgCertificate.isEmpty() && xGpgSecEnv.is()) // GPG { // TODO not ideal to retrieve cert by keyID, might // collide, or PGPKeyID format might change - can't we @@ -634,15 +640,19 @@ void DocumentDigitalSignatures::showCertificate( } sal_Bool DocumentDigitalSignatures::isAuthorTrusted( - const Reference< css::security::XCertificate >& Author ) + const Reference<css::security::XCertificate>& xAuthor) { - OUString sSerialNum = xmlsecurity::bigIntegerToNumericString( Author->getSerialNumber() ); + if (!xAuthor.is()) + { + return false; + } + OUString sSerialNum = xmlsecurity::bigIntegerToNumericString(xAuthor->getSerialNumber()); Sequence< SvtSecurityOptions::Certificate > aTrustedAuthors = SvtSecurityOptions().GetTrustedAuthors(); return std::any_of(aTrustedAuthors.begin(), aTrustedAuthors.end(), - [&Author, &sSerialNum](const SvtSecurityOptions::Certificate& rAuthor) { - return ( rAuthor[0] == Author->getIssuerName() ) + [&xAuthor, &sSerialNum](const SvtSecurityOptions::Certificate& rAuthor) { + return xmlsecurity::EqualDistinguishedNames(rAuthor[0], xAuthor->getIssuerName()) && ( rAuthor[1] == sSerialNum ); }); } diff --git a/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx index ef67c7167c04..18ccaf2d2166 100644 --- a/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx +++ b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx @@ -588,7 +588,7 @@ void DigitalSignaturesDialog::ImplFillSignaturesBox() if (!rInfo.ouGpgCertificate.isEmpty()) aType = "OpenPGP"; // XML based: XAdES or not. - else if (!rInfo.ouCertDigest.isEmpty()) + else if (rInfo.GetSigningCertificate() && !rInfo.GetSigningCertificate()->CertDigest.isEmpty()) aType = "XAdES"; else aType = "XML-DSig"; @@ -700,8 +700,8 @@ uno::Reference<security::XCertificate> DigitalSignaturesDialog::getCertificate(c uno::Reference<security::XCertificate> xCert; //First we try to get the certificate which is embedded in the XML Signature - if (xSecEnv.is() && !rInfo.ouX509Certificate.isEmpty()) - xCert = xSecEnv->createCertificateFromAscii(rInfo.ouX509Certificate); + if (xSecEnv.is() && rInfo.GetSigningCertificate() && !rInfo.GetSigningCertificate()->X509Certificate.isEmpty()) + xCert = xSecEnv->createCertificateFromAscii(rInfo.GetSigningCertificate()->X509Certificate); else { //There must be an embedded certificate because we use it to get the //issuer name. We cannot use /Signature/KeyInfo/X509Data/X509IssuerName @@ -713,9 +713,12 @@ uno::Reference<security::XCertificate> DigitalSignaturesDialog::getCertificate(c } //In case there is no embedded certificate we try to get it from a local store - if (!xCert.is() && xSecEnv.is()) - xCert = xSecEnv->getCertificate( rInfo.ouX509IssuerName, xmlsecurity::numericStringToBigInteger( rInfo.ouX509SerialNumber ) ); - if (!xCert.is() && xGpgSecEnv.is()) + if (!xCert.is() && xSecEnv.is() && rInfo.GetSigningCertificate()) + { + xCert = xSecEnv->getCertificate(rInfo.GetSigningCertificate()->X509IssuerName, + xmlsecurity::numericStringToBigInteger(rInfo.GetSigningCertificate()->X509SerialNumber)); + } + if (!xCert.is() && xGpgSecEnv.is() && !rInfo.ouGpgKeyID.isEmpty()) xCert = xGpgSecEnv->getCertificate( rInfo.ouGpgKeyID, xmlsecurity::numericStringToBigInteger("") ); SAL_WARN_IF( !xCert.is(), "xmlsecurity.dialogs", "Certificate not found and can't be created!" ); diff --git a/xmlsecurity/source/helper/documentsignaturehelper.cxx b/xmlsecurity/source/helper/documentsignaturehelper.cxx index 482ae6cc4126..ddff308ee52f 100644 --- a/xmlsecurity/source/helper/documentsignaturehelper.cxx +++ b/xmlsecurity/source/helper/documentsignaturehelper.cxx @@ -492,6 +492,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, @@ -508,26 +531,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 a0e674c3bd1b..aa08dc5c499e 100644 --- a/xmlsecurity/source/helper/documentsignaturemanager.cxx +++ b/xmlsecurity/source/helper/documentsignaturemanager.cxx @@ -585,6 +585,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 e38aba90ee66..1e031644b0b5 100644 --- a/xmlsecurity/source/helper/ooxmlsecexporter.cxx +++ b/xmlsecurity/source/helper/ooxmlsecexporter.cxx @@ -199,13 +199,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 b0795cb8f33f..843000a9c326 100644 --- a/xmlsecurity/source/helper/pdfsignaturehelper.cxx +++ b/xmlsecurity/source/helper/pdfsignaturehelper.cxx @@ -85,8 +85,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 22c056e70da1..bcb79039e342 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" @@ -547,4 +550,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 e3df9a85f6da..6bd88e24f91e 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 f46277f96ea1..f2e00fca99c9 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 92ebfb6c72e8..89141ed1dfd4 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 } } -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*/, + OUString const& rX509IssuerName, OUString 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 { diff --git a/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx index 0e619b2802f8..244cd46ac564 100644 --- a/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx +++ b/xmlsecurity/source/xmlsec/mscrypt/x509certificate_mscryptimpl.cxx @@ -26,6 +26,7 @@ #include <cppuhelper/supportsservice.hxx> #include "x509certificate_mscryptimpl.hxx" #include <certificateextension_xmlsecimpl.hxx> +#include <biginteger.hxx> #include "sanextension_mscryptimpl.hxx" #include "oid.hxx" @@ -649,4 +650,50 @@ Sequence<OUString> SAL_CALL X509Certificate_MSCryptImpl::getSupportedServiceName return { OUString() }; } +namespace xmlsecurity { + +static bool EncodeDistinguishedName(OUString const& rName, CERT_NAME_BLOB & rBlob) +{ + LPCWSTR pszError; + if (!CertStrToNameW(X509_ASN_ENCODING, + reinterpret_cast<LPCWSTR>(rName.getStr()), CERT_X500_NAME_STR, + nullptr, nullptr, &rBlob.cbData, &pszError)) + { + SAL_INFO("xmlsecurity.xmlsec", "CertStrToNameW failed: " << WindowsErrorString(GetLastError()) << "; " << reinterpret_cast<char16_t const*>(pszError)); + return false; + } + rBlob.pbData = new BYTE[rBlob.cbData]; + if (!CertStrToNameW(X509_ASN_ENCODING, + reinterpret_cast<LPCWSTR>(rName.getStr()), CERT_X500_NAME_STR, + nullptr, rBlob.pbData, &rBlob.cbData, &pszError)) + { + SAL_INFO("xmlsecurity.xmlsec", "CertStrToNameW failed: " << WindowsErrorString(GetLastError()) << "; " << reinterpret_cast<char16_t const*>(pszError)); + return false; + } + return true; +} + +bool EqualDistinguishedNames( + OUString const& rName1, OUString const& rName2) +{ + CERT_NAME_BLOB blob1; + if (!EncodeDistinguishedName(rName1, blob1)) + { + return false; + } + CERT_NAME_BLOB blob2; + if (!EncodeDistinguishedName(rName2, blob2)) + { + delete[] blob1.pbData; + return false; + } + bool const ret(CertCompareCertificateName(X509_ASN_ENCODING, + &blob1, &blob2) == TRUE); + delete[] blob2.pbData; + delete[] blob1.pbData; + return ret; +} + +} // namespace xmlsecurity + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx b/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx index a0acd81786d7..db400e6f1ed9 100644 --- a/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx +++ b/xmlsecurity/source/xmlsec/mscrypt/xmlsignature_mscryptimpl.cxx @@ -18,6 +18,7 @@ */ #include <sal/config.h> +#include <sal/log.hxx> #include <rtl/uuid.h> #include <com/sun/star/xml/crypto/SecurityOperationStatus.hpp> @@ -250,6 +251,7 @@ SAL_CALL XMLSignature_MSCryptImpl::validate( ++nReferenceGood; } } + SAL_INFO("xmlsecurity.xmlsec", "xmlSecDSigCtxVerify status " << pDsigCtx->status << ", references good " << nReferenceGood << " of " << nReferenceCount); if (rs == 0 && nReferenceCount == nReferenceGood) { diff --git a/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx b/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx index 1a323d33f32f..d6143a81883c 100644 --- a/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx +++ b/xmlsecurity/source/xmlsec/nss/x509certificate_nssimpl.cxx @@ -31,6 +31,7 @@ #include <rtl/ref.hxx> #include "x509certificate_nssimpl.hxx" +#include <biginteger.hxx> #include <certificateextension_xmlsecimpl.hxx> #include "sanextension_nssimpl.hxx" @@ -533,4 +534,28 @@ sal_Bool SAL_CALL X509Certificate_NssImpl::supportsService(const OUString& servi /* XServiceInfo */ Sequence<OUString> SAL_CALL X509Certificate_NssImpl::getSupportedServiceNames() { return { OUString() }; } +namespace xmlsecurity { + +bool EqualDistinguishedNames( + OUString const& rName1, OUString const& rName2) +{ + CERTName *const pName1(CERT_AsciiToName(OUStringToOString(rName1, RTL_TEXTENCODING_UTF8).getStr())); + if (pName1 == nullptr) + { + return false; + } + CERTName *const pName2(CERT_AsciiToName(OUStringToOString(rName2, RTL_TEXTENCODING_UTF8).getStr())); + if (pName2 == nullptr) + { + CERT_DestroyName(pName1); + return false; + } + bool const ret(CERT_CompareName(pName1, pName2) == SECEqual); + CERT_DestroyName(pName2); + CERT_DestroyName(pName1); + return ret; +} + +} // namespace xmlsecurity + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx b/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx index c39f6cc72f76..9d2a5251026a 100644 --- a/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx +++ b/xmlsecurity/source/xmlsec/nss/xmlsignature_nssimpl.cxx @@ -26,6 +26,9 @@ #include "securityenvironment_nssimpl.hxx" #include <xmlsec-wrapper.h> + +#include <sal/log.hxx> + #include <com/sun/star/xml/crypto/XXMLSignature.hpp> #include <memory> @@ -257,6 +260,7 @@ SAL_CALL XMLSignature_NssImpl::validate( ++nReferenceGood; } } + SAL_INFO("xmlsecurity.xmlsec", "xmlSecDSigCtxVerify status " << pDsigCtx->status << ", references good " << nReferenceGood << " of " << nReferenceCount); if (rs == 0 && pDsigCtx->status == xmlSecDSigStatusSucceeded && nReferenceCount == nReferenceGood) { |