From 9c5d3e5c59a9d0ffcb5fd99f5d4c98b0f6b5560e Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Fri, 9 Nov 2018 16:42:16 +0100 Subject: tdf#118581 Correctly display issuer name in signature line Move the xmlsec helper methods to comphelper so that we can use them in cui Change-Id: If9b10cfff5f5abd6b16e48f043af7959edbb1142 Reviewed-on: https://gerrit.libreoffice.org/63198 Tested-by: Jenkins Reviewed-by: Samuel Mehrbrodt --- comphelper/Library_comphelper.mk | 1 + comphelper/source/misc/xmlsechelper.cxx | 335 ++++++++++++++++++++++++++++++++ 2 files changed, 336 insertions(+) create mode 100644 comphelper/source/misc/xmlsechelper.cxx (limited to 'comphelper') diff --git a/comphelper/Library_comphelper.mk b/comphelper/Library_comphelper.mk index 04b333db9af9..a5275f971be4 100644 --- a/comphelper/Library_comphelper.mk +++ b/comphelper/Library_comphelper.mk @@ -143,6 +143,7 @@ $(eval $(call gb_Library_add_exception_objects,comphelper,\ comphelper/source/misc/types \ comphelper/source/misc/weak \ comphelper/source/misc/weakeventlistener \ + comphelper/source/misc/xmlsechelper \ comphelper/source/officeinstdir/officeinstallationdirectories \ comphelper/source/processfactory/processfactory \ comphelper/source/property/ChainablePropertySet \ diff --git a/comphelper/source/misc/xmlsechelper.cxx b/comphelper/source/misc/xmlsechelper.cxx new file mode 100644 index 000000000000..84ccd203092f --- /dev/null +++ b/comphelper/source/misc/xmlsechelper.cxx @@ -0,0 +1,335 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace comphelper +{ +namespace xmlsec +{ + OUString GetCertificateKind( const css::security::CertificateKind &rKind ) + { + switch (rKind) + { + case css::security::CertificateKind_X509: + return OUString("X.509"); + case css::security::CertificateKind_OPENPGP: + return OUString("OpenPGP"); + default: + return OUString(); + } + } + + /* + Creates two strings based on the distinguished name which are displayed in the + certificate details view. The first string contains only the values of the attribute + and values pairs, which are separated by commas. All escape characters ('"') are + removed. + The second string is for the details view at the bottom. It shows the attribute/value + pairs on different lines. All escape characters ('"') are removed. + */ + pair< OUString, OUString> GetDNForCertDetailsView( const OUString & rRawString) + { + vector< pair< OUString, OUString > > vecAttrValueOfDN = parseDN(rRawString); + OUStringBuffer s1, s2; + for (auto i = vecAttrValueOfDN.cbegin(); i < vecAttrValueOfDN.cend(); ++i) + { + if (i != vecAttrValueOfDN.cbegin()) + { + s1.append(','); + s2.append('\n'); + } + s1.append(i->second); + s2.append(i->first).append(" = ").append(i->second); + } + return make_pair(s1.makeStringAndClear(), s2.makeStringAndClear()); + } + +/* + Whenever the attribute value contains special characters, such as '"' or ',' (without '') + then the value will be enclosed in double quotes by the respective Windows or NSS function + which we use to retrieve, for example, the subject name. If double quotes appear in the value then + they are escaped with a double quote. This function removes the escape characters. +*/ +#ifdef _WIN32 +vector< pair< OUString, OUString> > parseDN(const OUString& rRawString) +{ + vector< pair > retVal; + bool bInEscape = false; + bool bInValue = false; + bool bInType = true; + sal_Int32 nTypeNameStart = 0; + OUString sType; + OUStringBuffer sbufValue; + sal_Int32 length = rRawString.getLength(); + + for (sal_Int32 i = 0; i < length; i++) + { + sal_Unicode c = rRawString[i]; + + if (c == '=') + { + if (! bInValue) + { + sType = rRawString.copy(nTypeNameStart, i - nTypeNameStart); + sType = sType.trim(); + bInType = false; + } + else + { + sbufValue.append(c); + } + } + else if (c == '"') + { + if (!bInEscape) + { + //If this is the quote is the first of the couple which enclose the + //whole value, because the value contains special characters + //then we just drop it. That is, this character must be followed by + //a character which is not '"'. + if ( i + 1 < length && rRawString[i+1] == '"') + bInEscape = true; + else + bInValue = !bInValue; //value is enclosed in " " + } + else + { + //This quote is escaped by a preceding quote and therefore is + //part of the value + sbufValue.append(c); + bInEscape = false; + } + } + else if (c == ',' || c == '+') + { + //The comma separate the attribute value pairs. + //If the comma is not part of a value (the value would then be enclosed in '"'), + //then we have reached the end of the value + if (!bInValue) + { + OSL_ASSERT(!sType.isEmpty()); + retVal.push_back(make_pair(sType, sbufValue.makeStringAndClear())); + sType.clear(); + //The next char is the start of the new type + nTypeNameStart = i + 1; + bInType = true; + } + else + { + //The whole string is enclosed because it contains special characters. + //The enclosing '"' are not part of certificate but will be added by + //the function (Windows or NSS) which retrieves DN + sbufValue.append(c); + } + } + else + { + if (!bInType) + sbufValue.append(c); + } + } + if (sbufValue.getLength()) + { + OSL_ASSERT(!sType.isEmpty()); + retVal.push_back(make_pair(sType, sbufValue.makeStringAndClear())); + } + return retVal; + } +#else +vector< pair< OUString, OUString> > parseDN(const OUString& rRawString) + { + vector< pair > retVal; + //bInEscape == true means that the preceding character is an escape character + bool bInEscape = false; + bool bInValue = false; + bool bInType = true; + sal_Int32 nTypeNameStart = 0; + OUString sType; + OUStringBuffer sbufValue; + sal_Int32 length = rRawString.getLength(); + + for (sal_Int32 i = 0; i < length; i++) + { + sal_Unicode c = rRawString[i]; + + if (c == '=') + { + if (! bInValue) + { + sType = rRawString.copy(nTypeNameStart, i - nTypeNameStart); + sType = sType.trim(); + bInType = false; + } + else + { + sbufValue.append(c); + } + } + else if (c == '\\') + { + if (!bInEscape) + { + bInEscape = true; + } + else + { // bInEscape is true + sbufValue.append(c); + bInEscape = false; + } + } + else if (c == '"') + { + //an unescaped '"' is either at the beginning or end of the value + if (!bInEscape) + { + if ( !bInValue) + bInValue = true; + else if (bInValue) + bInValue = false; + } + else + { + //This quote is escaped by a preceding quote and therefore is + //part of the value + sbufValue.append(c); + bInEscape = false; + } + } + else if (c == ',' || c == '+') + { + //The comma separate the attribute value pairs. + //If the comma is not part of a value (the value would then be enclosed in '"'), + //then we have reached the end of the value + if (!bInValue) + { + OSL_ASSERT(!sType.isEmpty()); + retVal.emplace_back(sType, sbufValue.makeStringAndClear()); + sType.clear(); + //The next char is the start of the new type + nTypeNameStart = i + 1; + bInType = true; + } + else + { + //The whole string is enclosed because it contains special characters. + //The enclosing '"' are not part of certificate but will be added by + //the function (Windows or NSS) which retrieves DN + sbufValue.append(c); + } + } + else + { + if (!bInType) + { + sbufValue.append(c); + bInEscape = false; + } + } + } + if (!sbufValue.isEmpty()) + { + OSL_ASSERT(!sType.isEmpty()); + retVal.emplace_back(sType, sbufValue.makeStringAndClear()); + } + return retVal; + } + +#endif + + OUString GetContentPart( const OUString& _rRawString ) + { + char const * aIDs[] = { "CN", "OU", "O", "E", nullptr }; + bool shouldBeParsed = false; + int i = 0; + while ( aIDs[i] ) + { + if (_rRawString.startsWith(OUString::createFromAscii(aIDs[i++]))) + { + shouldBeParsed = true; + break; + } + } + + if (!shouldBeParsed) + return _rRawString; + + OUString retVal; + i = 0; + vector< pair< OUString, OUString > > vecAttrValueOfDN = parseDN(_rRawString); + while ( aIDs[i] ) + { + OUString sPartId = OUString::createFromAscii( aIDs[i++] ); + auto idn = std::find_if(vecAttrValueOfDN.cbegin(), vecAttrValueOfDN.cend(), + [&sPartId](const pair< OUString, OUString >& dn) { return dn.first == sPartId; }); + if (idn != vecAttrValueOfDN.cend()) + retVal = idn->second; + if (!retVal.isEmpty()) + break; + } + return retVal; + } + + OUString GetHexString( const css::uno::Sequence< sal_Int8 >& _rSeq, const char* _pSep, sal_uInt16 _nLineBreak ) + { + const sal_Int8* pSerNumSeq = _rSeq.getConstArray(); + int nCnt = _rSeq.getLength(); + OUStringBuffer aStr; + const char pHexDigs[ 17 ] = "0123456789ABCDEF"; + char pBuffer[ 3 ] = " "; + sal_uInt8 nNum; + sal_uInt16 nBreakStart = _nLineBreak? _nLineBreak : 1; + sal_uInt16 nBreak = nBreakStart; + for( int i = 0 ; i < nCnt ; ++i ) + { + nNum = sal_uInt8( pSerNumSeq[ i ] ); + + // exchange the buffer[0] and buffer[1], which make it consistent with Mozilla and Windows + pBuffer[ 1 ] = pHexDigs[ nNum & 0x0F ]; + nNum >>= 4; + pBuffer[ 0 ] = pHexDigs[ nNum ]; + aStr.appendAscii( pBuffer ); + + --nBreak; + if( nBreak ) + aStr.appendAscii( _pSep ); + else + { + nBreak = nBreakStart; + aStr.append( '\n' ); + } + } + + return aStr.makeStringAndClear(); + } +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3