2016-10-13 10:37:02 +02:00
|
|
|
/* -*- 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/.
|
|
|
|
*/
|
|
|
|
|
2017-02-24 13:46:52 +01:00
|
|
|
#include <xmlsecurity/pdfio/pdfdocument.hxx>
|
2016-10-13 10:37:02 +02:00
|
|
|
|
|
|
|
#include <map>
|
|
|
|
#include <memory>
|
|
|
|
#include <vector>
|
|
|
|
|
2016-10-13 21:07:55 +02:00
|
|
|
#include <com/sun/star/uno/Sequence.hxx>
|
|
|
|
|
2016-10-14 10:41:34 +02:00
|
|
|
#include <comphelper/processfactory.hxx>
|
2016-10-13 10:37:02 +02:00
|
|
|
#include <comphelper/scopeguard.hxx>
|
2016-10-18 12:27:27 +02:00
|
|
|
#include <comphelper/string.hxx>
|
2016-10-26 17:55:02 +02:00
|
|
|
#include <filter/msfilter/mscodec.hxx>
|
2016-10-13 10:37:02 +02:00
|
|
|
#include <rtl/strbuf.hxx>
|
|
|
|
#include <rtl/string.hxx>
|
|
|
|
#include <sal/log.hxx>
|
|
|
|
#include <sal/types.h>
|
2016-10-13 21:07:55 +02:00
|
|
|
#include <sax/tools/converter.hxx>
|
2016-10-28 17:34:55 +02:00
|
|
|
#include <tools/zcodec.hxx>
|
2016-10-14 10:41:34 +02:00
|
|
|
#include <unotools/calendarwrapper.hxx>
|
|
|
|
#include <unotools/datetime.hxx>
|
2016-10-20 08:44:43 +02:00
|
|
|
#include <vcl/pdfwriter.hxx>
|
2016-10-14 10:41:34 +02:00
|
|
|
#include <xmloff/xmluconv.hxx>
|
2017-02-01 10:28:56 +01:00
|
|
|
#include <o3tl/make_unique.hxx>
|
2016-10-13 10:37:02 +02:00
|
|
|
|
2017-02-24 13:46:52 +01:00
|
|
|
#include <sigstruct.hxx>
|
|
|
|
|
2016-10-13 10:37:02 +02:00
|
|
|
#ifdef XMLSEC_CRYPTO_NSS
|
|
|
|
#include <cert.h>
|
|
|
|
#include <cms.h>
|
|
|
|
#include <nss.h>
|
2016-11-30 17:00:58 +01:00
|
|
|
#include <secerr.h>
|
2016-10-13 10:37:02 +02:00
|
|
|
#include <sechash.h>
|
|
|
|
#endif
|
|
|
|
|
2016-11-02 17:57:25 +01:00
|
|
|
#ifdef XMLSEC_CRYPTO_MSCRYPTO
|
|
|
|
#include <prewin.h>
|
|
|
|
#include <wincrypt.h>
|
|
|
|
#include <postwin.h>
|
|
|
|
#include <comphelper/windowserrorstring.hxx>
|
|
|
|
#endif
|
|
|
|
|
2016-10-13 10:37:02 +02:00
|
|
|
using namespace com::sun::star;
|
|
|
|
|
|
|
|
namespace xmlsecurity
|
|
|
|
{
|
|
|
|
namespace pdfio
|
|
|
|
{
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
namespace
|
2016-10-13 10:37:02 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
#ifdef XMLSEC_CRYPTO_NSS
|
|
|
|
/// Similar to NSS_CMSAttributeArray_FindAttrByOidTag(), but works directly with a SECOidData.
|
|
|
|
NSSCMSAttribute* CMSAttributeArray_FindAttrByOidData(NSSCMSAttribute** attrs, SECOidData* oid, PRBool only)
|
2016-10-13 10:37:02 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
NSSCMSAttribute* attr1, *attr2;
|
2016-10-13 10:37:02 +02:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
if (attrs == nullptr)
|
|
|
|
return nullptr;
|
2016-11-02 11:10:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
if (oid == nullptr)
|
|
|
|
return nullptr;
|
2016-10-13 10:37:02 +02:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
while ((attr1 = *attrs++) != nullptr)
|
2016-10-21 16:00:58 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
if (attr1->type.len == oid->oid.len && PORT_Memcmp(attr1->type.data,
|
|
|
|
oid->oid.data,
|
|
|
|
oid->oid.len) == 0)
|
|
|
|
break;
|
2016-10-21 16:00:58 +02:00
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
if (attr1 == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
if (!only)
|
|
|
|
return attr1;
|
|
|
|
|
|
|
|
while ((attr2 = *attrs++) != nullptr)
|
2016-10-21 16:00:58 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
if (attr2->type.len == oid->oid.len && PORT_Memcmp(attr2->type.data,
|
|
|
|
oid->oid.data,
|
|
|
|
oid->oid.len) == 0)
|
|
|
|
break;
|
2016-10-21 16:00:58 +02:00
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
if (attr2 != nullptr)
|
|
|
|
return nullptr;
|
2016-10-21 16:00:58 +02:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
return attr1;
|
2016-10-21 16:00:58 +02:00
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
/// Same as SEC_StringToOID(), which is private to us.
|
|
|
|
SECStatus StringToOID(SECItem* to, const char* from, PRUint32 len)
|
2016-11-09 16:32:48 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
PRUint32 decimal_numbers = 0;
|
|
|
|
PRUint32 result_bytes = 0;
|
|
|
|
SECStatus rv;
|
|
|
|
PRUint8 result[1024];
|
2016-11-09 16:32:48 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
static const PRUint32 max_decimal = (0xffffffff / 10);
|
|
|
|
static const char OIDstring[] = {"OID."};
|
2016-11-09 16:32:48 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
if (!from || !to)
|
|
|
|
{
|
|
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
|
|
return SECFailure;
|
2016-11-09 16:32:48 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
if (!len)
|
2016-10-20 17:09:04 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
len = PL_strlen(from);
|
2016-10-20 17:09:04 +02:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
if (len >= 4 && !PL_strncasecmp(from, OIDstring, 4))
|
|
|
|
{
|
|
|
|
from += 4; /* skip leading "OID." if present */
|
|
|
|
len -= 4;
|
|
|
|
}
|
|
|
|
if (!len)
|
|
|
|
{
|
|
|
|
bad_data:
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_DATA);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
do
|
2016-10-18 12:27:27 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
PRUint32 decimal = 0;
|
|
|
|
while (len > 0 && isdigit(*from))
|
2016-11-09 09:50:31 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
PRUint32 addend = (*from++ - '0');
|
|
|
|
--len;
|
|
|
|
if (decimal > max_decimal) /* overflow */
|
|
|
|
goto bad_data;
|
|
|
|
decimal = (decimal * 10) + addend;
|
|
|
|
if (decimal < addend) /* overflow */
|
|
|
|
goto bad_data;
|
2016-11-09 09:50:31 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
if (len != 0 && *from != '.')
|
2016-11-09 09:50:31 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
goto bad_data;
|
2016-11-09 09:50:31 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
if (decimal_numbers == 0)
|
2016-11-09 09:50:31 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
if (decimal > 2)
|
|
|
|
goto bad_data;
|
|
|
|
result[0] = decimal * 40;
|
|
|
|
result_bytes = 1;
|
2016-11-09 09:50:31 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
else if (decimal_numbers == 1)
|
2016-11-09 09:50:31 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
if (decimal > 40)
|
|
|
|
goto bad_data;
|
|
|
|
result[0] += decimal;
|
2016-11-09 09:50:31 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
else
|
2016-11-09 09:50:31 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
/* encode the decimal number, */
|
|
|
|
PRUint8* rp;
|
|
|
|
PRUint32 num_bytes = 0;
|
|
|
|
PRUint32 tmp = decimal;
|
|
|
|
while (tmp)
|
|
|
|
{
|
|
|
|
num_bytes++;
|
|
|
|
tmp >>= 7;
|
|
|
|
}
|
|
|
|
if (!num_bytes)
|
|
|
|
++num_bytes; /* use one byte for a zero value */
|
|
|
|
if (static_cast<size_t>(num_bytes) + result_bytes > sizeof result)
|
|
|
|
goto bad_data;
|
|
|
|
tmp = num_bytes;
|
|
|
|
rp = result + result_bytes - 1;
|
|
|
|
rp[tmp] = (PRUint8)(decimal & 0x7f);
|
|
|
|
decimal >>= 7;
|
|
|
|
while (--tmp > 0)
|
|
|
|
{
|
|
|
|
rp[tmp] = (PRUint8)(decimal | 0x80);
|
|
|
|
decimal >>= 7;
|
|
|
|
}
|
|
|
|
result_bytes += num_bytes;
|
2016-11-09 09:50:31 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
++decimal_numbers;
|
|
|
|
if (len > 0) /* skip trailing '.' */
|
2016-11-09 09:50:31 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
++from;
|
|
|
|
--len;
|
2016-11-09 09:50:31 +01:00
|
|
|
}
|
2016-10-25 11:33:21 +02:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
while (len > 0);
|
|
|
|
/* now result contains result_bytes of data */
|
|
|
|
if (to->data && to->len >= result_bytes)
|
2016-11-08 15:54:15 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
PORT_Memcpy(to->data, result, to->len = result_bytes);
|
|
|
|
rv = SECSuccess;
|
2016-11-08 15:54:15 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
else
|
2016-10-18 12:27:27 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SECItem result_item = {siBuffer, nullptr, 0 };
|
|
|
|
result_item.data = result;
|
|
|
|
result_item.len = result_bytes;
|
|
|
|
rv = SECITEM_CopyItem(nullptr, to, &result_item);
|
2016-10-18 12:27:27 +02:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
#elif defined XMLSEC_CRYPTO_MSCRYPTO
|
|
|
|
/// Verifies a non-detached signature using CryptoAPI.
|
|
|
|
bool VerifyNonDetachedSignature(SvStream& rStream, std::vector<std::pair<size_t, size_t>>& rByteRanges, std::vector<BYTE>& rExpectedHash)
|
|
|
|
{
|
|
|
|
HCRYPTPROV hProv = 0;
|
|
|
|
if (!CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
|
2016-10-18 12:27:27 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "CryptAcquireContext() failed");
|
2016-10-18 12:27:27 +02:00
|
|
|
return false;
|
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
|
|
|
|
HCRYPTHASH hHash = 0;
|
|
|
|
if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash))
|
2016-10-18 12:27:27 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "CryptCreateHash() failed");
|
2016-10-18 12:27:27 +02:00
|
|
|
return false;
|
|
|
|
}
|
2016-11-09 14:19:05 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
for (const auto& rByteRange : rByteRanges)
|
2016-11-09 14:19:05 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
rStream.Seek(rByteRange.first);
|
|
|
|
const int nChunkLen = 4096;
|
|
|
|
std::vector<unsigned char> aBuffer(nChunkLen);
|
|
|
|
for (size_t nByte = 0; nByte < rByteRange.second;)
|
2016-11-09 14:19:05 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
size_t nRemainingSize = rByteRange.second - nByte;
|
|
|
|
if (nRemainingSize < nChunkLen)
|
2016-11-09 14:19:05 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
rStream.ReadBytes(aBuffer.data(), nRemainingSize);
|
|
|
|
if (!CryptHashData(hHash, aBuffer.data(), nRemainingSize, 0))
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "CryptHashData() failed");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
nByte = rByteRange.second;
|
2016-11-09 14:19:05 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
else
|
2016-11-09 14:19:05 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
rStream.ReadBytes(aBuffer.data(), nChunkLen);
|
|
|
|
if (!CryptHashData(hHash, aBuffer.data(), nChunkLen, 0))
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "CryptHashData() failed");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
nByte += nChunkLen;
|
2016-11-09 14:19:05 +01:00
|
|
|
}
|
|
|
|
}
|
2016-10-25 18:08:27 +02:00
|
|
|
}
|
2016-10-18 09:21:05 +02:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
DWORD nActualHash = 0;
|
|
|
|
if (!CryptGetHashParam(hHash, HP_HASHVAL, nullptr, &nActualHash, 0))
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "CryptGetHashParam() failed to provide the hash length");
|
|
|
|
return false;
|
|
|
|
}
|
2016-11-11 09:01:03 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
std::vector<unsigned char> aActualHash(nActualHash);
|
|
|
|
if (!CryptGetHashParam(hHash, HP_HASHVAL, aActualHash.data(), &nActualHash, 0))
|
2016-10-18 09:21:05 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "CryptGetHashParam() failed to provide the hash");
|
|
|
|
return false;
|
|
|
|
}
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
CryptDestroyHash(hHash);
|
|
|
|
CryptReleaseContext(hProv, 0);
|
2016-11-10 08:38:31 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
if (!std::memcmp(aActualHash.data(), rExpectedHash.data(), aActualHash.size()) && aActualHash.size() == rExpectedHash.size())
|
|
|
|
return true;
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignature, SignatureInformation& rInformation, bool bLast)
|
|
|
|
{
|
|
|
|
vcl::filter::PDFObjectElement* pValue = pSignature->LookupObject("V");
|
|
|
|
if (!pValue)
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: no value");
|
|
|
|
return false;
|
|
|
|
}
|
2016-11-10 08:38:31 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
auto pContents = dynamic_cast<vcl::filter::PDFHexStringElement*>(pValue->Lookup("Contents"));
|
|
|
|
if (!pContents)
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: no contents");
|
|
|
|
return false;
|
|
|
|
}
|
2016-11-10 08:38:31 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
auto pByteRange = dynamic_cast<vcl::filter::PDFArrayElement*>(pValue->Lookup("ByteRange"));
|
|
|
|
if (!pByteRange || pByteRange->GetElements().size() < 2)
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: no byte range or too few elements");
|
|
|
|
return false;
|
|
|
|
}
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
auto pSubFilter = dynamic_cast<vcl::filter::PDFNameElement*>(pValue->Lookup("SubFilter"));
|
|
|
|
bool bNonDetached = pSubFilter && pSubFilter->GetValue() == "adbe.pkcs7.sha1";
|
|
|
|
if (!pSubFilter || (pSubFilter->GetValue() != "adbe.pkcs7.detached" && !bNonDetached && pSubFilter->GetValue() != "ETSI.CAdES.detached"))
|
|
|
|
{
|
|
|
|
if (!pSubFilter)
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: missing sub-filter");
|
|
|
|
else
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: unsupported sub-filter: '"<<pSubFilter->GetValue()<<"'");
|
|
|
|
return false;
|
|
|
|
}
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Reason / comment / description is optional.
|
|
|
|
auto pReason = dynamic_cast<vcl::filter::PDFHexStringElement*>(pValue->Lookup("Reason"));
|
|
|
|
if (pReason)
|
|
|
|
{
|
|
|
|
// See appendUnicodeTextString() for the export equivalent of this.
|
|
|
|
std::vector<unsigned char> aReason = vcl::filter::PDFDocument::DecodeHexString(pReason);
|
|
|
|
OUStringBuffer aBuffer;
|
|
|
|
sal_uInt16 nByte = 0;
|
|
|
|
for (size_t i = 0; i < aReason.size(); ++i)
|
2016-11-09 15:49:35 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
if (i % 2 == 0)
|
|
|
|
nByte = aReason[i];
|
|
|
|
else
|
2016-11-09 15:49:35 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
sal_Unicode nUnicode;
|
|
|
|
nUnicode = (nByte << 8);
|
|
|
|
nUnicode |= aReason[i];
|
|
|
|
aBuffer.append(nUnicode);
|
2016-11-09 15:49:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
if (!aBuffer.isEmpty())
|
|
|
|
rInformation.ouDescription = aBuffer.makeStringAndClear();
|
|
|
|
}
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Date: used only when the time of signing is not available in the
|
|
|
|
// signature.
|
|
|
|
auto pM = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pValue->Lookup("M"));
|
|
|
|
if (pM)
|
|
|
|
{
|
|
|
|
// Example: "D:20161027100104".
|
|
|
|
const OString& rM = pM->GetValue();
|
|
|
|
if (rM.startsWith("D:") && rM.getLength() >= 16)
|
2016-11-09 15:49:35 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
rInformation.stDateTime.Year = rM.copy(2, 4).toInt32();
|
|
|
|
rInformation.stDateTime.Month = rM.copy(6, 2).toInt32();
|
|
|
|
rInformation.stDateTime.Day = rM.copy(8, 2).toInt32();
|
|
|
|
rInformation.stDateTime.Hours = rM.copy(10, 2).toInt32();
|
|
|
|
rInformation.stDateTime.Minutes = rM.copy(12, 2).toInt32();
|
|
|
|
rInformation.stDateTime.Seconds = rM.copy(14, 2).toInt32();
|
2016-11-09 15:49:35 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
}
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Build a list of offset-length pairs, representing the signed bytes.
|
|
|
|
std::vector<std::pair<size_t, size_t>> aByteRanges;
|
|
|
|
size_t nByteRangeOffset = 0;
|
|
|
|
const std::vector<vcl::filter::PDFElement*>& rByteRangeElements = pByteRange->GetElements();
|
|
|
|
for (size_t i = 0; i < rByteRangeElements.size(); ++i)
|
|
|
|
{
|
|
|
|
auto pNumber = dynamic_cast<vcl::filter::PDFNumberElement*>(rByteRangeElements[i]);
|
|
|
|
if (!pNumber)
|
2016-11-09 16:53:51 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: signature offset and length has to be a number");
|
|
|
|
return false;
|
2016-11-09 16:53:51 +01:00
|
|
|
}
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
if (i % 2 == 0)
|
2016-11-09 15:49:35 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
nByteRangeOffset = pNumber->GetValue();
|
|
|
|
continue;
|
2016-11-09 15:49:35 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
size_t nByteRangeLength = pNumber->GetValue();
|
|
|
|
aByteRanges.push_back(std::make_pair(nByteRangeOffset, nByteRangeLength));
|
|
|
|
}
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Detect if the byte ranges don't cover everything, but the signature itself.
|
|
|
|
if (aByteRanges.size() < 2)
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: expected 2 byte ranges");
|
|
|
|
return false;
|
2016-10-18 09:21:05 +02:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
if (aByteRanges[0].first != 0)
|
2016-10-18 09:21:05 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: first range start is not 0");
|
|
|
|
return false;
|
2016-11-09 15:49:35 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
// 2 is the leading "<" and the trailing ">" around the hex string.
|
|
|
|
size_t nSignatureLength = static_cast<size_t>(pContents->GetValue().getLength()) + 2;
|
|
|
|
if (aByteRanges[1].first != (aByteRanges[0].second + nSignatureLength))
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: second range start is not the end of the signature");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
rStream.Seek(STREAM_SEEK_TO_END);
|
|
|
|
size_t nFileEnd = rStream.Tell();
|
|
|
|
if (bLast && (aByteRanges[1].first + aByteRanges[1].second) != nFileEnd)
|
|
|
|
// Second range end is not the end of the file.
|
|
|
|
rInformation.bPartialDocumentSignature = true;
|
2016-11-11 09:01:03 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// At this point there is no obviously missing info to validate the
|
|
|
|
// signature.
|
|
|
|
std::vector<unsigned char> aSignature = vcl::filter::PDFDocument::DecodeHexString(pContents);
|
|
|
|
if (aSignature.empty())
|
2016-11-11 09:01:03 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: empty contents");
|
2016-11-11 09:01:03 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
#ifdef XMLSEC_CRYPTO_NSS
|
|
|
|
// Validate the signature. No need to call NSS_Init() here, assume that the
|
|
|
|
// caller did that already.
|
2016-11-11 09:01:03 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
SECItem aSignatureItem;
|
|
|
|
aSignatureItem.data = aSignature.data();
|
|
|
|
aSignatureItem.len = aSignature.size();
|
|
|
|
NSSCMSMessage* pCMSMessage = NSS_CMSMessage_CreateFromDER(&aSignatureItem,
|
|
|
|
/*cb=*/nullptr,
|
|
|
|
/*cb_arg=*/nullptr,
|
|
|
|
/*pwfn=*/nullptr,
|
|
|
|
/*pwfn_arg=*/nullptr,
|
|
|
|
/*decrypt_key_cb=*/nullptr,
|
|
|
|
/*decrypt_key_cb_arg=*/nullptr);
|
|
|
|
if (!NSS_CMSMessage_IsSigned(pCMSMessage))
|
2016-11-11 09:01:03 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: message is not signed");
|
2016-11-11 09:01:03 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
NSSCMSContentInfo* pCMSContentInfo = NSS_CMSMessage_ContentLevel(pCMSMessage, 0);
|
|
|
|
if (!pCMSContentInfo)
|
2016-11-11 09:01:03 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: NSS_CMSMessage_ContentLevel() failed");
|
2016-11-11 09:01:03 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
auto pCMSSignedData = static_cast<NSSCMSSignedData*>(NSS_CMSContentInfo_GetContent(pCMSContentInfo));
|
|
|
|
if (!pCMSSignedData)
|
2016-10-18 15:31:28 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: NSS_CMSContentInfo_GetContent() failed");
|
2016-10-18 15:31:28 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Import certificates from the signed data temporarily, so it'll be
|
|
|
|
// possible to verify the signature, even if we didn't have the certificate
|
|
|
|
// previously.
|
|
|
|
std::vector<CERTCertificate*> aDocumentCertificates;
|
|
|
|
for (size_t i = 0; pCMSSignedData->rawCerts[i]; ++i)
|
|
|
|
aDocumentCertificates.push_back(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), pCMSSignedData->rawCerts[i], nullptr, 0, 0));
|
|
|
|
|
|
|
|
NSSCMSSignerInfo* pCMSSignerInfo = NSS_CMSSignedData_GetSignerInfo(pCMSSignedData, 0);
|
|
|
|
if (!pCMSSignerInfo)
|
2016-10-18 15:31:28 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: NSS_CMSSignedData_GetSignerInfo() failed");
|
2016-10-18 15:31:28 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
SECItem aAlgorithm = NSS_CMSSignedData_GetDigestAlgs(pCMSSignedData)[0]->algorithm;
|
|
|
|
SECOidTag eOidTag = SECOID_FindOIDTag(&aAlgorithm);
|
2016-10-18 15:31:28 +02:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Map a sign algorithm to a digest algorithm.
|
|
|
|
// See NSS_CMSUtil_MapSignAlgs(), which is private to us.
|
|
|
|
switch (eOidTag)
|
|
|
|
{
|
|
|
|
case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
|
|
|
|
eOidTag = SEC_OID_SHA1;
|
|
|
|
break;
|
|
|
|
case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
|
|
|
|
eOidTag = SEC_OID_SHA256;
|
|
|
|
break;
|
|
|
|
case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
|
|
|
|
eOidTag = SEC_OID_SHA512;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2016-10-18 09:21:05 +02:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
HASH_HashType eHashType = HASH_GetHashTypeByOidTag(eOidTag);
|
|
|
|
HASHContext* pHASHContext = HASH_Create(eHashType);
|
|
|
|
if (!pHASHContext)
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: HASH_Create() failed");
|
|
|
|
return false;
|
|
|
|
}
|
2016-10-18 09:21:05 +02:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// We have a hash, update it with the byte ranges.
|
|
|
|
for (const auto& rByteRange : aByteRanges)
|
2016-10-13 10:37:02 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
rStream.Seek(rByteRange.first);
|
2016-10-13 10:37:02 +02:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// And now hash this byte range.
|
|
|
|
const int nChunkLen = 4096;
|
|
|
|
std::vector<unsigned char> aBuffer(nChunkLen);
|
|
|
|
for (size_t nByte = 0; nByte < rByteRange.second;)
|
2016-10-13 10:37:02 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
size_t nRemainingSize = rByteRange.second - nByte;
|
|
|
|
if (nRemainingSize < nChunkLen)
|
2016-12-01 15:30:25 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
rStream.ReadBytes(aBuffer.data(), nRemainingSize);
|
|
|
|
HASH_Update(pHASHContext, aBuffer.data(), nRemainingSize);
|
|
|
|
nByte = rByteRange.second;
|
2016-12-01 15:30:25 +01:00
|
|
|
}
|
2016-11-10 10:18:31 +01:00
|
|
|
else
|
2016-12-01 15:30:25 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
rStream.ReadBytes(aBuffer.data(), nChunkLen);
|
|
|
|
HASH_Update(pHASHContext, aBuffer.data(), nChunkLen);
|
|
|
|
nByte += nChunkLen;
|
2016-12-01 15:30:25 +01:00
|
|
|
}
|
2016-10-13 10:37:02 +02:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
}
|
2016-10-13 10:37:02 +02:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Find out what is the expected length of the hash.
|
|
|
|
unsigned int nMaxResultLen = 0;
|
|
|
|
switch (eOidTag)
|
2016-10-26 11:59:39 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
case SEC_OID_SHA1:
|
|
|
|
nMaxResultLen = msfilter::SHA1_HASH_LENGTH;
|
|
|
|
rInformation.nDigestID = xml::crypto::DigestID::SHA1;
|
|
|
|
break;
|
|
|
|
case SEC_OID_SHA256:
|
|
|
|
nMaxResultLen = msfilter::SHA256_HASH_LENGTH;
|
|
|
|
rInformation.nDigestID = xml::crypto::DigestID::SHA256;
|
|
|
|
break;
|
|
|
|
case SEC_OID_SHA512:
|
|
|
|
nMaxResultLen = msfilter::SHA512_HASH_LENGTH;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: unrecognized algorithm");
|
2016-10-26 11:59:39 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
auto pActualResultBuffer = static_cast<unsigned char*>(PORT_Alloc(nMaxResultLen));
|
|
|
|
unsigned int nActualResultLen;
|
|
|
|
HASH_End(pHASHContext, pActualResultBuffer, &nActualResultLen, nMaxResultLen);
|
2016-10-26 11:59:39 +02:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
CERTCertificate* pCertificate = NSS_CMSSignerInfo_GetSigningCertificate(pCMSSignerInfo, CERT_GetDefaultCertDB());
|
|
|
|
if (!pCertificate)
|
2016-10-26 11:59:39 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: NSS_CMSSignerInfo_GetSigningCertificate() failed");
|
2016-10-26 11:59:39 +02:00
|
|
|
return false;
|
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
else
|
2016-10-26 11:59:39 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
uno::Sequence<sal_Int8> aDerCert(pCertificate->derCert.len);
|
|
|
|
for (size_t i = 0; i < pCertificate->derCert.len; ++i)
|
|
|
|
aDerCert[i] = pCertificate->derCert.data[i];
|
|
|
|
OUStringBuffer aBuffer;
|
|
|
|
sax::Converter::encodeBase64(aBuffer, aDerCert);
|
|
|
|
rInformation.ouX509Certificate = aBuffer.makeStringAndClear();
|
2016-10-26 11:59:39 +02:00
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
PRTime nSigningTime;
|
|
|
|
// This may fail, in which case the date should be taken from the dictionary's "M" key.
|
|
|
|
if (NSS_CMSSignerInfo_GetSigningTime(pCMSSignerInfo, &nSigningTime) == SECSuccess)
|
2016-10-13 10:37:02 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
// First convert the UNIX timestamp to an ISO8601 string.
|
|
|
|
OUStringBuffer aBuffer;
|
|
|
|
uno::Reference<uno::XComponentContext> xComponentContext = comphelper::getProcessComponentContext();
|
|
|
|
CalendarWrapper aCalendarWrapper(xComponentContext);
|
|
|
|
// nSigningTime is in microseconds.
|
|
|
|
SvXMLUnitConverter::convertDateTime(aBuffer, static_cast<double>(nSigningTime) / 1000000 / tools::Time::secondPerDay, aCalendarWrapper.getEpochStart().GetUNODate());
|
2016-10-13 10:37:02 +02:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Then convert this string to a local UNO DateTime.
|
|
|
|
util::DateTime aUNODateTime;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
utl::ISO8601parseDateTime(aBuffer.toString(), aUNODateTime);
|
|
|
|
}
|
|
|
|
catch (const std::length_error&)
|
2017-02-24 11:47:40 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: failed to parse signature date string");
|
|
|
|
return false;
|
2017-02-24 11:47:40 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
DateTime aDateTime(aUNODateTime);
|
|
|
|
aDateTime.ConvertToLocalTime();
|
|
|
|
rInformation.stDateTime = aDateTime.GetUNODateTime();
|
2017-02-24 11:47:40 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
|
|
|
|
// Check if we have a signing certificate attribute.
|
|
|
|
SECOidData aOidData;
|
|
|
|
aOidData.oid.data = nullptr;
|
|
|
|
/*
|
|
|
|
* id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
|
|
|
|
* { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
|
|
|
|
* smime(16) id-aa(2) 47 }
|
|
|
|
*/
|
|
|
|
if (StringToOID(&aOidData.oid, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess)
|
2016-10-13 10:37:02 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "StringToOID() failed");
|
|
|
|
return false;
|
2016-10-13 10:37:02 +02:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
aOidData.offset = SEC_OID_UNKNOWN;
|
|
|
|
aOidData.desc = "id-aa-signingCertificateV2";
|
|
|
|
aOidData.mechanism = CKM_SHA_1;
|
|
|
|
aOidData.supportedExtension = UNSUPPORTED_CERT_EXTENSION;
|
|
|
|
NSSCMSAttribute* pAttribute = CMSAttributeArray_FindAttrByOidData(pCMSSignerInfo->authAttr, &aOidData, PR_TRUE);
|
|
|
|
if (pAttribute)
|
|
|
|
rInformation.bHasSigningCertificate = true;
|
2016-10-13 10:37:02 +02:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
SECItem* pContentInfoContentData = pCMSSignedData->contentInfo.content.data;
|
|
|
|
if (bNonDetached && pContentInfoContentData && pContentInfoContentData->data)
|
|
|
|
{
|
|
|
|
// Not a detached signature.
|
|
|
|
if (!std::memcmp(pActualResultBuffer, pContentInfoContentData->data, nMaxResultLen) && nActualResultLen == pContentInfoContentData->len)
|
|
|
|
rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
|
|
|
|
}
|
|
|
|
else
|
2016-10-13 10:37:02 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
// Detached, the usual case.
|
|
|
|
SECItem aActualResultItem;
|
|
|
|
aActualResultItem.data = pActualResultBuffer;
|
|
|
|
aActualResultItem.len = nActualResultLen;
|
|
|
|
if (NSS_CMSSignerInfo_Verify(pCMSSignerInfo, &aActualResultItem, nullptr) == SECSuccess)
|
|
|
|
rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
|
2016-10-13 10:37:02 +02:00
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Everything went fine
|
|
|
|
PORT_Free(pActualResultBuffer);
|
|
|
|
HASH_Destroy(pHASHContext);
|
|
|
|
NSS_CMSSignerInfo_Destroy(pCMSSignerInfo);
|
|
|
|
for (auto pDocumentCertificate : aDocumentCertificates)
|
|
|
|
CERT_DestroyCertificate(pDocumentCertificate);
|
2016-10-13 10:37:02 +02:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
return true;
|
|
|
|
#elif defined XMLSEC_CRYPTO_MSCRYPTO
|
|
|
|
// Open a message for decoding.
|
|
|
|
HCRYPTMSG hMsg = CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
|
|
|
|
CMSG_DETACHED_FLAG,
|
|
|
|
0,
|
|
|
|
NULL,
|
|
|
|
nullptr,
|
|
|
|
nullptr);
|
|
|
|
if (!hMsg)
|
2016-10-28 17:34:55 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgOpenToDecode() failed");
|
|
|
|
return false;
|
2016-10-28 17:34:55 +02:00
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Update the message with the encoded header blob.
|
|
|
|
if (!CryptMsgUpdate(hMsg, aSignature.data(), aSignature.size(), TRUE))
|
2016-10-28 17:34:55 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature, CryptMsgUpdate() for the header failed: " << WindowsErrorString(GetLastError()));
|
|
|
|
return false;
|
2016-10-28 17:34:55 +02:00
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Update the message with the content blob.
|
|
|
|
for (const auto& rByteRange : aByteRanges)
|
2016-10-28 17:34:55 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
rStream.Seek(rByteRange.first);
|
|
|
|
|
|
|
|
const int nChunkLen = 4096;
|
|
|
|
std::vector<unsigned char> aBuffer(nChunkLen);
|
|
|
|
for (size_t nByte = 0; nByte < rByteRange.second;)
|
2016-10-28 17:34:55 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
size_t nRemainingSize = rByteRange.second - nByte;
|
|
|
|
if (nRemainingSize < nChunkLen)
|
|
|
|
{
|
|
|
|
rStream.ReadBytes(aBuffer.data(), nRemainingSize);
|
|
|
|
if (!CryptMsgUpdate(hMsg, aBuffer.data(), nRemainingSize, FALSE))
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature, CryptMsgUpdate() for the content failed: " << WindowsErrorString(GetLastError()));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
nByte = rByteRange.second;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rStream.ReadBytes(aBuffer.data(), nChunkLen);
|
|
|
|
if (!CryptMsgUpdate(hMsg, aBuffer.data(), nChunkLen, FALSE))
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature, CryptMsgUpdate() for the content failed: " << WindowsErrorString(GetLastError()));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
nByte += nChunkLen;
|
|
|
|
}
|
2016-10-28 17:34:55 +02:00
|
|
|
}
|
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
if (!CryptMsgUpdate(hMsg, nullptr, 0, TRUE))
|
2016-10-28 17:34:55 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature, CryptMsgUpdate() for the last content failed: " << WindowsErrorString(GetLastError()));
|
|
|
|
return false;
|
2016-10-28 17:34:55 +02:00
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Get the CRYPT_ALGORITHM_IDENTIFIER from the message.
|
|
|
|
DWORD nDigestID = 0;
|
|
|
|
if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_HASH_ALGORITHM_PARAM, 0, nullptr, &nDigestID))
|
2016-10-28 17:34:55 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed: " << WindowsErrorString(GetLastError()));
|
|
|
|
return false;
|
2016-10-28 17:34:55 +02:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
std::unique_ptr<BYTE[]> pDigestBytes(new BYTE[nDigestID]);
|
|
|
|
if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_HASH_ALGORITHM_PARAM, 0, pDigestBytes.get(), &nDigestID))
|
2016-10-28 17:34:55 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed: " << WindowsErrorString(GetLastError()));
|
|
|
|
return false;
|
2016-10-28 17:34:55 +02:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
auto pDigestID = reinterpret_cast<CRYPT_ALGORITHM_IDENTIFIER*>(pDigestBytes.get());
|
|
|
|
if (OString(szOID_NIST_sha256) == pDigestID->pszObjId)
|
|
|
|
rInformation.nDigestID = xml::crypto::DigestID::SHA256;
|
|
|
|
else if (OString(szOID_RSA_SHA1RSA) == pDigestID->pszObjId || OString(szOID_OIWSEC_sha1) == pDigestID->pszObjId)
|
|
|
|
rInformation.nDigestID = xml::crypto::DigestID::SHA1;
|
|
|
|
else
|
|
|
|
// Don't error out here, we can still verify the message digest correctly, just the digest ID won't be set.
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: unhandled algorithm identifier '"<<pDigestID->pszObjId<<"'");
|
|
|
|
|
|
|
|
// Get the signer CERT_INFO from the message.
|
|
|
|
DWORD nSignerCertInfo = 0;
|
|
|
|
if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_CERT_INFO_PARAM, 0, nullptr, &nSignerCertInfo))
|
2016-10-28 17:34:55 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed");
|
|
|
|
return false;
|
2016-10-28 17:34:55 +02:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
std::unique_ptr<BYTE[]> pSignerCertInfoBuf(new BYTE[nSignerCertInfo]);
|
|
|
|
if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_CERT_INFO_PARAM, 0, pSignerCertInfoBuf.get(), &nSignerCertInfo))
|
2016-10-28 17:34:55 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed");
|
|
|
|
return false;
|
2016-10-28 17:34:55 +02:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
PCERT_INFO pSignerCertInfo = reinterpret_cast<PCERT_INFO>(pSignerCertInfoBuf.get());
|
2016-10-28 17:34:55 +02:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Open a certificate store in memory using CERT_STORE_PROV_MSG, which
|
|
|
|
// initializes it with the certificates from the message.
|
|
|
|
HCERTSTORE hStoreHandle = CertOpenStore(CERT_STORE_PROV_MSG,
|
|
|
|
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
|
|
|
|
NULL,
|
|
|
|
0,
|
|
|
|
hMsg);
|
|
|
|
if (!hStoreHandle)
|
2016-10-28 17:34:55 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CertOpenStore() failed");
|
|
|
|
return false;
|
2016-10-28 17:34:55 +02:00
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Find the signer's certificate in the store.
|
|
|
|
PCCERT_CONTEXT pSignerCertContext = CertGetSubjectCertificateFromStore(hStoreHandle,
|
|
|
|
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
|
|
|
|
pSignerCertInfo);
|
|
|
|
if (!pSignerCertContext)
|
2016-10-28 17:34:55 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CertGetSubjectCertificateFromStore() failed");
|
|
|
|
return false;
|
2016-10-28 17:34:55 +02:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
else
|
2016-10-28 17:34:55 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
// Write rInformation.ouX509Certificate.
|
|
|
|
uno::Sequence<sal_Int8> aDerCert(pSignerCertContext->cbCertEncoded);
|
|
|
|
for (size_t i = 0; i < pSignerCertContext->cbCertEncoded; ++i)
|
|
|
|
aDerCert[i] = pSignerCertContext->pbCertEncoded[i];
|
|
|
|
OUStringBuffer aBuffer;
|
|
|
|
sax::Converter::encodeBase64(aBuffer, aDerCert);
|
|
|
|
rInformation.ouX509Certificate = aBuffer.makeStringAndClear();
|
2016-10-28 17:34:55 +02:00
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
if (bNonDetached)
|
2016-10-28 17:34:55 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
// Not a detached signature.
|
|
|
|
DWORD nContentParam = 0;
|
|
|
|
if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, nullptr, &nContentParam))
|
2016-11-10 10:33:02 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed");
|
|
|
|
return false;
|
2016-11-10 10:33:02 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
|
|
|
|
std::vector<BYTE> aContentParam(nContentParam);
|
|
|
|
if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, aContentParam.data(), &nContentParam))
|
2016-11-02 11:10:35 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed");
|
|
|
|
return false;
|
2016-11-02 11:10:35 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
|
|
|
|
if (VerifyNonDetachedSignature(rStream, aByteRanges, aContentParam))
|
|
|
|
rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
|
2016-10-28 17:34:55 +02:00
|
|
|
}
|
2016-11-02 11:10:35 +01:00
|
|
|
else
|
2016-10-28 17:34:55 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
// Detached, the usual case.
|
|
|
|
// Use the CERT_INFO from the signer certificate to verify the signature.
|
|
|
|
if (CryptMsgControl(hMsg, 0, CMSG_CTRL_VERIFY_SIGNATURE, pSignerCertContext->pCertInfo))
|
|
|
|
rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
|
2016-10-13 10:37:02 +02:00
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Check if we have a signing certificate attribute.
|
|
|
|
DWORD nSignedAttributes = 0;
|
|
|
|
if (CryptMsgGetParam(hMsg, CMSG_SIGNER_AUTH_ATTR_PARAM, 0, nullptr, &nSignedAttributes))
|
2016-10-13 10:37:02 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
std::unique_ptr<BYTE[]> pSignedAttributesBuf(new BYTE[nSignedAttributes]);
|
|
|
|
if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_AUTH_ATTR_PARAM, 0, pSignedAttributesBuf.get(), &nSignedAttributes))
|
2016-10-13 10:37:02 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: CryptMsgGetParam() failed");
|
|
|
|
return false;
|
2016-10-13 10:37:02 +02:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
auto pSignedAttributes = reinterpret_cast<PCRYPT_ATTRIBUTES>(pSignedAttributesBuf.get());
|
|
|
|
for (size_t nAttr = 0; nAttr < pSignedAttributes->cAttr; ++nAttr)
|
2016-10-13 10:37:02 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
CRYPT_ATTRIBUTE& rAttr = pSignedAttributes->rgAttr[nAttr];
|
|
|
|
/*
|
|
|
|
* id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
|
|
|
|
* { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
|
|
|
|
* smime(16) id-aa(2) 47 }
|
|
|
|
*/
|
|
|
|
OString aOid("1.2.840.113549.1.9.16.2.47");
|
|
|
|
if (aOid == rAttr.pszObjId)
|
|
|
|
{
|
|
|
|
rInformation.bHasSigningCertificate = true;
|
|
|
|
break;
|
|
|
|
}
|
2016-10-13 10:37:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
|
|
|
|
CryptMsgClose(hMsg);
|
2016-10-13 10:37:02 +02:00
|
|
|
return true;
|
2017-03-21 18:03:21 +01:00
|
|
|
#else
|
|
|
|
// Not implemented.
|
|
|
|
(void)rStream;
|
|
|
|
(void)rInformation;
|
2016-10-13 10:37:02 +02:00
|
|
|
|
|
|
|
return false;
|
2017-03-21 18:03:21 +01:00
|
|
|
#endif
|
2016-10-25 11:33:21 +02:00
|
|
|
}
|
2016-10-13 10:37:02 +02:00
|
|
|
|
|
|
|
} // namespace pdfio
|
|
|
|
} // namespace xmlsecurity
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|