From 891d4b5d91c86951bb06d413808f75a3f4e6cb28 Mon Sep 17 00:00:00 2001 From: Thorsten Behrens Date: Tue, 20 Jun 2017 23:52:18 +0200 Subject: gpg4libre: write PGPData info, get more metadata out for gpg key Change-Id: Ia560869ec02fca7fe4219136e1fe939e13f1e4c2 --- xmlsecurity/inc/sigstruct.hxx | 3 + xmlsecurity/inc/xmlsignaturehelper.hxx | 3 + xmlsecurity/inc/xsecctl.hxx | 5 ++ xmlsecurity/source/gpg/CertificateImpl.cxx | 52 +++++++++--- xmlsecurity/source/gpg/CertificateImpl.hxx | 3 +- xmlsecurity/source/gpg/SecurityEnvironment.cxx | 9 +- xmlsecurity/source/gpg/xmlsignature_gpgimpl.cxx | 4 + .../source/helper/documentsignaturemanager.cxx | 97 +++++++++++++++------- xmlsecurity/source/helper/xmlsignaturehelper.cxx | 10 +++ xmlsecurity/source/helper/xsecctl.cxx | 78 +++++++++++------ xmlsecurity/source/helper/xsecsign.cxx | 23 +++++ 11 files changed, 219 insertions(+), 68 deletions(-) diff --git a/xmlsecurity/inc/sigstruct.hxx b/xmlsecurity/inc/sigstruct.hxx index 479ba3d8f745..c6c706705aba 100644 --- a/xmlsecurity/inc/sigstruct.hxx +++ b/xmlsecurity/inc/sigstruct.hxx @@ -74,6 +74,9 @@ struct SignatureInformation OUString ouX509IssuerName; OUString ouX509SerialNumber; OUString ouX509Certificate; + + OUString ouGpgCertificate; + OUString ouSignatureValue; css::util::DateTime stDateTime; diff --git a/xmlsecurity/inc/xmlsignaturehelper.hxx b/xmlsecurity/inc/xmlsignaturehelper.hxx index 3d0c91eb7ed8..7a39b6955ca6 100644 --- a/xmlsecurity/inc/xmlsignaturehelper.hxx +++ b/xmlsecurity/inc/xmlsignaturehelper.hxx @@ -150,6 +150,9 @@ public: void AddEncapsulatedX509Certificate(const OUString& ouEncapsulatedX509Certificate); + void SetGpgCertificate(sal_Int32 nSecurityId, const OUString& ouGpgCertDigest, + const OUString& ouGpgCert); + void SetDateTime( sal_Int32 nSecurityId, const Date& rDate, const tools::Time& rTime ); void SetDescription(sal_Int32 nSecurityId, const OUString& rDescription); diff --git a/xmlsecurity/inc/xsecctl.hxx b/xmlsecurity/inc/xsecctl.hxx index b7ee1f8310b9..02450d1197bd 100644 --- a/xmlsecurity/inc/xsecctl.hxx +++ b/xmlsecurity/inc/xsecctl.hxx @@ -382,6 +382,11 @@ public: void addEncapsulatedX509Certificate(const OUString& rEncapsulatedX509Certificate); + void setGpgCertificate( + sal_Int32 nSecurityId, + const OUString& ouCertDigest, + const OUString& ouCert); + void setDate( sal_Int32 nSecurityId, const css::util::DateTime& rDateTime ); diff --git a/xmlsecurity/source/gpg/CertificateImpl.cxx b/xmlsecurity/source/gpg/CertificateImpl.cxx index 3eabed319b8c..c0f48e309c7c 100644 --- a/xmlsecurity/source/gpg/CertificateImpl.cxx +++ b/xmlsecurity/source/gpg/CertificateImpl.cxx @@ -10,9 +10,14 @@ #include "CertificateImpl.hxx" #include +#include #include +#include +#include +#include + using namespace css; using namespace css::uno; using namespace css::security; @@ -25,6 +30,7 @@ CertificateImpl::CertificateImpl() : CertificateImpl::~CertificateImpl() { + // TODO: cleanup key } //Methods from XCertificateImpl @@ -35,8 +41,10 @@ sal_Int16 SAL_CALL CertificateImpl::getVersion() Sequence< sal_Int8 > SAL_CALL CertificateImpl::getSerialNumber() { - // Empty for gpg - return Sequence< sal_Int8 > (); + // This is mapped to the fingerprint for gpg + const char* keyId = m_pKey.primaryFingerprint(); + return comphelper::arrayToSequence( + keyId, strlen(keyId)); } OUString SAL_CALL CertificateImpl::getIssuerName() @@ -113,8 +121,8 @@ Reference< XCertificateExtension > SAL_CALL CertificateImpl::findCertificateExte Sequence< sal_Int8 > SAL_CALL CertificateImpl::getEncoded() { - // Empty for gpg - return Sequence< sal_Int8 > (); + // Export key to base64Empty for gpg + return m_aBits; } OUString SAL_CALL CertificateImpl::getSubjectPublicKeyAlgorithm() @@ -146,20 +154,26 @@ OUString SAL_CALL CertificateImpl::getSignatureAlgorithm() Sequence< sal_Int8 > SAL_CALL CertificateImpl::getSHA1Thumbprint() { - // Empty for gpg - return Sequence< sal_Int8 > (); + // This is mapped to the short keyID for gpg + const char* keyId = m_pKey.shortKeyID(); + return comphelper::arrayToSequence( + keyId, strlen(keyId)); } uno::Sequence CertificateImpl::getSHA256Thumbprint() { - // Empty for gpg - return Sequence< sal_Int8 > (); + // This is mapped to the long keyID for gpg + const char* keyId = m_pKey.keyID(); + return comphelper::arrayToSequence( + keyId, strlen(keyId)); } Sequence< sal_Int8 > SAL_CALL CertificateImpl::getMD5Thumbprint() { - // Empty for gpg - return Sequence< sal_Int8 > (); + // This is mapped to the short keyID for gpg + const char* keyId = m_pKey.shortKeyID(); + return comphelper::arrayToSequence( + keyId, strlen(keyId)); } CertificateKind SAL_CALL CertificateImpl::getCertificateKind() @@ -192,9 +206,25 @@ const Sequence< sal_Int8>& CertificateImpl::getUnoTunnelId() { return CertificateImplUnoTunnelId::get().getSeq(); } -void CertificateImpl::setCertificate(const GpgME::Key& key) +void CertificateImpl::setCertificate(GpgME::Context* ctx, const GpgME::Key& key) { m_pKey = key; + + // extract key data, store into m_aBits + GpgME::Data data_out; + ctx->exportPublicKeys(key.keyID(), data_out); + + // TODO: needs some error handling + data_out.seek(0,SEEK_SET); + int len=0, curr=0; char buf; + while( (curr=data_out.read(&buf, 1)) ) + len += curr; + + // write bits to sequence of bytes + m_aBits.realloc(len); + data_out.seek(0,SEEK_SET); + if( data_out.read(m_aBits.getArray(), len) != len ) + throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); } const GpgME::Key* CertificateImpl::getCertificate() const diff --git a/xmlsecurity/source/gpg/CertificateImpl.hxx b/xmlsecurity/source/gpg/CertificateImpl.hxx index 11a862c42b58..9db3ab85de14 100644 --- a/xmlsecurity/source/gpg/CertificateImpl.hxx +++ b/xmlsecurity/source/gpg/CertificateImpl.hxx @@ -34,6 +34,7 @@ class CertificateImpl : public cppu::WeakImplHelper< css::security::XCertificate { private: GpgME::Key m_pKey; + css::uno::Sequence< sal_Int8 > m_aBits; public: CertificateImpl(); @@ -81,7 +82,7 @@ public: virtual css::security::CertificateKind getCertificateKind() override; // Helper methods - void setCertificate(const GpgME::Key& key); + void setCertificate(GpgME::Context* ctx, const GpgME::Key& key); const GpgME::Key* getCertificate() const; } ; diff --git a/xmlsecurity/source/gpg/SecurityEnvironment.cxx b/xmlsecurity/source/gpg/SecurityEnvironment.cxx index 83e6170a98c1..d120b2a985fa 100644 --- a/xmlsecurity/source/gpg/SecurityEnvironment.cxx +++ b/xmlsecurity/source/gpg/SecurityEnvironment.cxx @@ -59,11 +59,13 @@ OUString SecurityEnvironmentGpg::getSecurityEnvironmentInformation() Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::getPersonalCertificates() { + // TODO: move to central init GpgME::initializeLibrary(); GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP); if (err) throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); + // TODO: keep that around for SecurityEnvironmentGpg lifetime GpgME::Context* ctx = GpgME::Context::createForProtocol(GpgME::OpenPGP); if (ctx == nullptr) throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); @@ -79,7 +81,7 @@ Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::getPersonalCertif break; if (!k.isInvalid()) { xCert = new CertificateImpl(); - xCert->setCertificate(k); + xCert->setCertificate(ctx,k); certsList.push_back(xCert); } } @@ -96,11 +98,13 @@ Sequence< Reference < XCertificate > > SecurityEnvironmentGpg::getPersonalCertif Reference< XCertificate > SecurityEnvironmentGpg::getCertificate( const OUString& issuerName, const Sequence< sal_Int8 >& /*serialNumber*/ ) { + // TODO: move to central init GpgME::initializeLibrary(); GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP); if (err) throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); + // TODO: keep that around for SecurityEnvironmentGpg lifetime GpgME::Context* ctx = GpgME::Context::createForProtocol(GpgME::OpenPGP); if (ctx == nullptr) throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); @@ -117,13 +121,14 @@ Reference< XCertificate > SecurityEnvironmentGpg::getCertificate( const OUString break; if (!k.isInvalid()) { xCert = new CertificateImpl(); - xCert->setCertificate(k); + xCert->setCertificate(ctx, k); ctx->endKeyListing(); return xCert; } } ctx->endKeyListing(); + // TODO: cleanup ctx return nullptr; } diff --git a/xmlsecurity/source/gpg/xmlsignature_gpgimpl.cxx b/xmlsecurity/source/gpg/xmlsignature_gpgimpl.cxx index 686258eeca00..b9219f0f3281 100644 --- a/xmlsecurity/source/gpg/xmlsignature_gpgimpl.cxx +++ b/xmlsecurity/source/gpg/xmlsignature_gpgimpl.cxx @@ -171,10 +171,12 @@ SAL_CALL XMLSignature_GpgImpl::generate( throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); //Sign the template via gpgme + // TODO move init to central place GpgME::initializeLibrary(); if( GpgME::checkEngine(GpgME::OpenPGP) ) throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); + // TODO get ctx from SecurityEnv? GpgME::Context* ctx = GpgME::Context::createForProtocol(GpgME::OpenPGP); if( ctx == nullptr ) throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); @@ -315,10 +317,12 @@ SAL_CALL XMLSignature_GpgImpl::validate( throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); // Validate the template via gpgme + // TODO move init to central place GpgME::initializeLibrary(); if( GpgME::checkEngine(GpgME::OpenPGP) ) throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); + // TODO get ctx from SecurityEnv? GpgME::Context* ctx = GpgME::Context::createForProtocol(GpgME::OpenPGP); if( ctx == nullptr ) throw RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol."); diff --git a/xmlsecurity/source/helper/documentsignaturemanager.cxx b/xmlsecurity/source/helper/documentsignaturemanager.cxx index 041c3f2a0113..b2e42076be5e 100644 --- a/xmlsecurity/source/helper/documentsignaturemanager.cxx +++ b/xmlsecurity/source/helper/documentsignaturemanager.cxx @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,7 @@ #include using namespace css; +namespace cssu = com::sun::star::uno; DocumentSignatureManager::DocumentSignatureManager(const uno::Reference& xContext, DocumentSignatureMode eMode) : mxContext(xContext), @@ -263,52 +265,87 @@ bool DocumentSignatureManager::add(const uno::Reference& return false; } - // TODO: no serial number currently on gpg keys - better/more - // discriminative error handling? - OUString aCertSerial = xmlsecurity::bigIntegerToNumericString(xCert->getSerialNumber()); - if (aCertSerial.isEmpty()) + // GPG or X509 key? + uno::Reference< lang::XServiceInfo > xServiceInfo( xSecurityContext, cssu::UNO_QUERY ); + if (xServiceInfo->getImplementationName() == "com.sun.star.xml.security.gpg.XMLSecurityContext_GpgImpl") { - SAL_WARN("xmlsecurity.helper", "Error in Certificate, problem with serial number!"); - } - - if (!mxStore.is()) - { - // Something not ZIP based, try PDF. - nSecurityId = getPDFSignatureHelper().GetNewSecurityId(); - getPDFSignatureHelper().SetX509Certificate(xCert); - getPDFSignatureHelper().SetDescription(rDescription); - uno::Reference xInputStream(mxSignatureStream, uno::UNO_QUERY); - if (!getPDFSignatureHelper().Sign(xInputStream, bAdESCompliant)) + // GPG keys only really have PGPKeyId and PGPKeyPacket + // TODO: prevent selection of gpg keys for pdfs and ooxml early on! + if (!mxStore.is()) { - SAL_WARN("xmlsecurity.helper", "PDFSignatureHelper::Sign() failed"); + SAL_WARN("xmlsecurity.helper", "cannot sign pdfs with GPG keys"); return false; } - return true; - } - maSignatureHelper.StartMission(xSecurityContext); + maSignatureHelper.StartMission(xSecurityContext); - nSecurityId = maSignatureHelper.GetNewSecurityId(); + nSecurityId = maSignatureHelper.GetNewSecurityId(); - OUStringBuffer aStrBuffer; - sax::Converter::encodeBase64(aStrBuffer, xCert->getEncoded()); + OUStringBuffer aStrBuffer; + sax::Converter::encodeBase64(aStrBuffer, xCert->getEncoded()); - OUString aCertDigest; - if (auto pCertificate = dynamic_cast(xCert.get())) - { - OUStringBuffer aBuffer; - sax::Converter::encodeBase64(aBuffer, pCertificate->getSHA256Thumbprint()); - aCertDigest = aBuffer.makeStringAndClear(); + OUString aKeyId; + if (auto pCertificate = dynamic_cast(xCert.get())) + { + OUStringBuffer aBuffer; + sax::Converter::encodeBase64(aBuffer, pCertificate->getSHA256Thumbprint()); + aKeyId = aBuffer.makeStringAndClear(); + } + else + SAL_WARN("xmlsecurity.helper", "XCertificate implementation without an xmlsecurity::Certificate one"); + + maSignatureHelper.SetGpgCertificate(nSecurityId, aKeyId, aStrBuffer.makeStringAndClear()); } else - SAL_WARN("xmlsecurity.helper", "XCertificate implementation without an xmlsecurity::Certificate one"); + { + OUString aCertSerial = xmlsecurity::bigIntegerToNumericString(xCert->getSerialNumber()); + if (aCertSerial.isEmpty()) + { + SAL_WARN("xmlsecurity.helper", "Error in Certificate, problem with serial number!"); + return false; + } + + if (!mxStore.is()) + { + // Something not ZIP based, try PDF. + nSecurityId = getPDFSignatureHelper().GetNewSecurityId(); + getPDFSignatureHelper().SetX509Certificate(xCert); + getPDFSignatureHelper().SetDescription(rDescription); + uno::Reference xInputStream(mxSignatureStream, uno::UNO_QUERY); + if (!getPDFSignatureHelper().Sign(xInputStream, bAdESCompliant)) + { + SAL_WARN("xmlsecurity.helper", "PDFSignatureHelper::Sign() failed"); + return false; + } + return true; + } + + maSignatureHelper.StartMission(xSecurityContext); + + nSecurityId = maSignatureHelper.GetNewSecurityId(); - maSignatureHelper.SetX509Certificate(nSecurityId, xCert->getIssuerName(), aCertSerial, aStrBuffer.makeStringAndClear(), aCertDigest); + OUStringBuffer aStrBuffer; + sax::Converter::encodeBase64(aStrBuffer, xCert->getEncoded()); + + OUString aCertDigest; + if (auto pCertificate = dynamic_cast(xCert.get())) + { + OUStringBuffer aBuffer; + sax::Converter::encodeBase64(aBuffer, pCertificate->getSHA256Thumbprint()); + aCertDigest = aBuffer.makeStringAndClear(); + } + else + SAL_WARN("xmlsecurity.helper", "XCertificate implementation without an xmlsecurity::Certificate one"); + + maSignatureHelper.SetX509Certificate(nSecurityId, xCert->getIssuerName(), aCertSerial, aStrBuffer.makeStringAndClear(), aCertDigest); + + } uno::Sequence< uno::Reference< security::XCertificate > > aCertPath = xSecurityContext->getSecurityEnvironment()->buildCertificatePath(xCert); const uno::Reference< security::XCertificate >* pCertPath = aCertPath.getConstArray(); sal_Int32 nCnt = aCertPath.getLength(); + OUStringBuffer aStrBuffer; for (int i = 0; i < nCnt; i++) { sax::Converter::encodeBase64(aStrBuffer, pCertPath[i]->getEncoded()); diff --git a/xmlsecurity/source/helper/xmlsignaturehelper.cxx b/xmlsecurity/source/helper/xmlsignaturehelper.cxx index df8af13653b2..6242518ce4e1 100644 --- a/xmlsecurity/source/helper/xmlsignaturehelper.cxx +++ b/xmlsecurity/source/helper/xmlsignaturehelper.cxx @@ -123,6 +123,16 @@ void XMLSignatureHelper::AddEncapsulatedX509Certificate(const OUString& ouEncaps mpXSecController->addEncapsulatedX509Certificate(ouEncapsulatedX509Certificate); } +void XMLSignatureHelper::SetGpgCertificate(sal_Int32 nSecurityId, + const OUString& ouGpgCertDigest, + const OUString& ouGpgCert) +{ + mpXSecController->setGpgCertificate( + nSecurityId, + ouGpgCertDigest, + ouGpgCert); +} + void XMLSignatureHelper::SetDateTime( sal_Int32 nSecurityId, const ::Date& rDate, const tools::Time& rTime ) { css::util::DateTime stDateTime = ::DateTime(rDate, rTime).GetUNODateTime(); diff --git a/xmlsecurity/source/helper/xsecctl.cxx b/xmlsecurity/source/helper/xsecctl.cxx index 31de4e897d2d..acac83ca2fe3 100644 --- a/xmlsecurity/source/helper/xsecctl.cxx +++ b/xmlsecurity/source/helper/xsecctl.cxx @@ -719,43 +719,73 @@ void XSecController::exportSignature( "KeyInfo", cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); { - /* Write X509Data element */ - xDocumentHandler->startElement( - "X509Data", - cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); + // GPG or X509 key? + if (!signatureInfo.ouGpgCertificate.isEmpty()) { - /* Write X509IssuerSerial element */ + /* Write PGPData element */ xDocumentHandler->startElement( - "X509IssuerSerial", + "PGPData", cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); { - /* Write X509IssuerName element */ + /* Write keyid element */ xDocumentHandler->startElement( - "X509IssuerName", + "PGPKeyID", cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); - xDocumentHandler->characters( signatureInfo.ouX509IssuerName ); - xDocumentHandler->endElement( "X509IssuerName" ); + xDocumentHandler->characters( signatureInfo.ouCertDigest ); + xDocumentHandler->endElement( "PGPKeyID" ); - /* Write X509SerialNumber element */ - xDocumentHandler->startElement( - "X509SerialNumber", - cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); - xDocumentHandler->characters( signatureInfo.ouX509SerialNumber ); - xDocumentHandler->endElement( "X509SerialNumber" ); + /* Write PGPKeyPacket element */ + if (!signatureInfo.ouX509Certificate.isEmpty()) + { + xDocumentHandler->startElement( + "PGPKeyPacket", + cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); + xDocumentHandler->characters( signatureInfo.ouGpgCertificate ); + xDocumentHandler->endElement( "PGPKeyPacket" ); + } } - xDocumentHandler->endElement( "X509IssuerSerial" ); - - /* Write X509Certificate element */ - if (!signatureInfo.ouX509Certificate.isEmpty()) + xDocumentHandler->endElement( "PGPData" ); + } + else + { + /* Write X509Data element */ + xDocumentHandler->startElement( + "X509Data", + cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); { + /* Write X509IssuerSerial element */ xDocumentHandler->startElement( - "X509Certificate", + "X509IssuerSerial", cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); - xDocumentHandler->characters( signatureInfo.ouX509Certificate ); - xDocumentHandler->endElement( "X509Certificate" ); + { + /* Write X509IssuerName element */ + xDocumentHandler->startElement( + "X509IssuerName", + cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); + xDocumentHandler->characters( signatureInfo.ouX509IssuerName ); + xDocumentHandler->endElement( "X509IssuerName" ); + + /* Write X509SerialNumber element */ + xDocumentHandler->startElement( + "X509SerialNumber", + cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); + xDocumentHandler->characters( signatureInfo.ouX509SerialNumber ); + xDocumentHandler->endElement( "X509SerialNumber" ); + } + xDocumentHandler->endElement( "X509IssuerSerial" ); + + /* Write X509Certificate element */ + if (!signatureInfo.ouX509Certificate.isEmpty()) + { + xDocumentHandler->startElement( + "X509Certificate", + cssu::Reference< cssxs::XAttributeList > (new SvXMLAttributeList())); + xDocumentHandler->characters( signatureInfo.ouX509Certificate ); + xDocumentHandler->endElement( "X509Certificate" ); + } } + xDocumentHandler->endElement( "X509Data" ); } - xDocumentHandler->endElement( "X509Data" ); } xDocumentHandler->endElement( "KeyInfo" ); diff --git a/xmlsecurity/source/helper/xsecsign.cxx b/xmlsecurity/source/helper/xsecsign.cxx index 092b8fb001e6..452613b4d10b 100644 --- a/xmlsecurity/source/helper/xsecsign.cxx +++ b/xmlsecurity/source/helper/xsecsign.cxx @@ -239,6 +239,29 @@ void XSecController::setX509Certificate( } } +void XSecController::setGpgCertificate( + sal_Int32 nSecurityId, + const OUString& ouCertDigest, + const OUString& ouCert) +{ + int index = findSignatureInfor( nSecurityId ); + + if ( index == -1 ) + { + InternalSignatureInformation isi(nSecurityId, nullptr); + isi.signatureInfor.ouGpgCertificate = ouCert; + isi.signatureInfor.ouCertDigest = ouCertDigest; + m_vInternalSignatureInformations.push_back( isi ); + } + else + { + SignatureInformation &si + = m_vInternalSignatureInformations[index].signatureInfor; + si.ouGpgCertificate = ouCert; + si.ouCertDigest = ouCertDigest; + } +} + void XSecController::setDate( sal_Int32 nSecurityId, const css::util::DateTime& rDateTime ) -- cgit