windows-nt/Source/XPSP1/NT/ds/security/cryptoapi/common/pkiutil/asn1util.cpp
2020-09-26 16:20:57 +08:00

815 lines
26 KiB
C++

//+-------------------------------------------------------------------------
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1995 - 1999
//
// File: asn1util.cpp
//
// Contents: ASN.1 utility helper functions.
//
// Functions: Asn1UtilDecodeLength
// Asn1UtilExtractContent
// Asn1UtilIsPKCS7WithoutContentType
// Asn1UtilAdjustEncodedLength
// Asn1UtilExtractValues
// Asn1UtilExtractPKCS7SignedDataContent
// Asn1UtilExtractCertificateToBeSignedContent
// Asn1UtilExtractCertificatePublicKeyInfo
// Asn1UtilExtractKeyIdFromCertInfo
//
// History: 04-Dec-96 philh created from kevinr's wincrmsg version
//--------------------------------------------------------------------------
#include "global.hxx"
#include <dbgdef.h>
//+-------------------------------------------------------------------------
// Get the number of contents octets in a BER encoding.
//
// Parameters:
// pcbContent - receives the number of contents octets if definite
// encoding, else CMSG_INDEFINITE_LENGTH
// pbLength - points to the first length octet
// cbDER - number of bytes remaining in the encoding
//
// Returns:
// success - the number of bytes in the length field, >0
// failure - <0
//--------------------------------------------------------------------------
LONG
WINAPI
Asn1UtilDecodeLength(
OUT DWORD *pcbContent,
IN const BYTE *pbLength,
IN DWORD cbEncoded)
{
long i;
BYTE cbLength;
const BYTE *pb;
if (cbEncoded < 1)
goto TooLittleData;
if (0x80 == *pbLength) {
*pcbContent = CMSG_INDEFINITE_LENGTH;
i = 1;
goto CommonReturn;
}
// determine the number of length octets and contents octets
if ((cbLength = *pbLength) & 0x80) {
cbLength &= ~0x80; // low 7 bits have number of bytes
if (cbLength > 4)
goto LengthTooLargeError;
if (cbLength >= cbEncoded)
goto TooLittleData;
*pcbContent = 0;
for (i=cbLength, pb=pbLength+1; i>0; i--, pb++)
*pcbContent = (*pcbContent << 8) + (const DWORD)*pb;
i = cbLength + 1;
} else {
*pcbContent = (DWORD)cbLength;
i = 1;
}
CommonReturn:
return i; // how many bytes there were in the length field
ErrorReturn:
i = -1;
goto CommonReturn;
TooLittleData:
i = ASN1UTIL_INSUFFICIENT_DATA;
goto CommonReturn;
TRACE_ERROR(LengthTooLargeError)
}
//+-------------------------------------------------------------------------
// Point to the content octets in a BER-encoded blob.
//
// Returns:
// success - the number of bytes skipped, >=0
// failure - <0
//
// NB- If the blob is indefinite-length encoded, *pcbContent is set to
// CMSG_INDEFINITE_LENGTH
//--------------------------------------------------------------------------
LONG
WINAPI
Asn1UtilExtractContent(
IN const BYTE *pbEncoded,
IN DWORD cbEncoded,
OUT DWORD *pcbContent,
OUT const BYTE **ppbContent)
{
#define TAG_MASK 0x1f
DWORD cbIdentifier;
DWORD cbContent;
LONG cbLength;
LONG lHeader;
const BYTE *pb = pbEncoded;
if (0 == cbEncoded--)
goto TooLittleData;
// Skip over the identifier octet(s)
if (TAG_MASK == (*pb++ & TAG_MASK)) {
// high-tag-number form
cbIdentifier = 2;
while (TRUE) {
if (0 == cbEncoded--)
goto TooLittleData;
if (0 == (*pb++ & 0x80))
break;
cbIdentifier++;
}
} else {
// low-tag-number form
cbIdentifier = 1;
}
if (0 > (cbLength = Asn1UtilDecodeLength( &cbContent, pb, cbEncoded))) {
lHeader = cbLength;
goto CommonReturn;
}
pb += cbLength;
*pcbContent = cbContent;
*ppbContent = pb;
lHeader = cbLength + cbIdentifier;
CommonReturn:
return lHeader;
TooLittleData:
lHeader = ASN1UTIL_INSUFFICIENT_DATA;
goto CommonReturn;
}
//+-------------------------------------------------------------------------
// Returns TRUE if we believe this is a Bob special that has ommitted the
// PKCS #7 ContentType.
//
// For PKCS #7: an Object Identifier tag (0x06) immediately follows the
// identifier and length octets. For a Bob special: an integer tag (0x02)
// follows the identifier and length octets.
//--------------------------------------------------------------------------
BOOL
WINAPI
Asn1UtilIsPKCS7WithoutContentType(
IN const BYTE *pbDER,
IN DWORD cbDER)
{
DWORD cbContent;
const BYTE *pbContent;
// Handle MappedFile Exceptions
__try {
if (0 < Asn1UtilExtractContent(pbDER, cbDER, &cbContent, &pbContent) &&
(pbContent < pbDER + cbDER) &&
(0x02 == *pbContent))
return TRUE;
else
return FALSE;
} __except(EXCEPTION_EXECUTE_HANDLER) {
SetLastError(GetExceptionCode());
return FALSE;
}
}
//+-------------------------------------------------------------------------
// Decode the Asn1 length bytes to possibly downward adjust the length.
//
// The returned length is always <= cbDER.
//--------------------------------------------------------------------------
DWORD
WINAPI
Asn1UtilAdjustEncodedLength(
IN const BYTE *pbDER,
IN DWORD cbDER
)
{
// Decode the header to get the real length. I've seen files with extra
// stuff.
LONG lLen;
DWORD cbLen;
DWORD cbContent;
const BYTE *pbContent;
lLen = Asn1UtilExtractContent(pbDER, cbDER, &cbContent, &pbContent);
if ((lLen >= 0) && (cbContent != CMSG_INDEFINITE_LENGTH)) {
cbLen = (DWORD)lLen + cbContent;
if (cbLen < cbDER)
cbDER = cbLen;
// else if (cbLen > cbDER)
// DER length exceeds input file
}
// else
// Can't decode DER length
return cbDER;
}
//+-------------------------------------------------------------------------
// Extract one or more tagged values from the ASN.1 encoded byte array.
//
// Either steps into the value's content octets (ASN1UTIL_STEP_INTO_VALUE_OP)
// or steps over the value's tag, length and content octets
// (ASN1UTIL_STEP_OVER_VALUE_OP or ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP).
//
// For tag matching, only supports single byte tags. STEP_OVER values
// must be definite-length encoded.
//
// *pcValue is updated with the number of values successfully extracted.
//
// Returns:
// success - >= 0 => length of all values successfully extracted. For
// STEP_INTO, only the tag and length octets.
// failure - < 0 => negative (offset + 1) of first bad tagged value
// LastError is updated with the error.
//
// A non-NULL rgValueBlob[] is updated with the pointer to and length of the
// tagged value or its content octets. For OPTIONAL_STEP_OVER, if tag isn't
// found, pbData and cbData are set to 0. If a STEP_INTO value is
// indefinite-length encoded, cbData is set to CMSG_INDEFINITE_LENGTH.
// If ASN1UTIL_DEFINITE_LENGTH_FLAG is set, then, all returned lengths
// are definite-length, ie, CMSG_INDEFINITE_LENGTH is never returned.
//
// If ASN1UTIL_RETURN_VALUE_BLOB_FLAG is set, pbData points to
// the tag. cbData includes the tag, length and content octets.
//
// If ASN1UTIL_RETURN_CONTENT_BLOB_FLAG is set, pbData points to the content
// octets. cbData includes only the content octets.
//
// If neither BLOB_FLAG is set, rgValueBlob[] isn't updated.
//--------------------------------------------------------------------------
LONG
WINAPI
Asn1UtilExtractValues(
IN const BYTE *pbEncoded,
IN DWORD cbEncoded,
IN DWORD dwFlags,
IN OUT DWORD *pcValue,
IN const ASN1UTIL_EXTRACT_VALUE_PARA *rgValuePara,
OUT OPTIONAL PCRYPT_DER_BLOB rgValueBlob
)
{
DWORD cValue = *pcValue;
const BYTE *pb = pbEncoded;
DWORD cb = cbEncoded;
DWORD iValue;
LONG lAllValues;
for (iValue = 0; iValue < cValue; iValue++) {
DWORD dwParaFlags = rgValuePara[iValue].dwFlags;
DWORD dwOp = dwParaFlags & ASN1UTIL_MASK_VALUE_OP;
const BYTE *pbParaTag = rgValuePara[iValue].rgbTag;
BOOL fValueBlob = (dwParaFlags & (ASN1UTIL_RETURN_VALUE_BLOB_FLAG |
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG)) && rgValueBlob;
LONG lTagLength;
DWORD cbContent;
const BYTE *pbContent;
DWORD cbValue;
if (0 == cb) {
if (ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP != dwOp)
goto TooLittleData;
if (fValueBlob) {
rgValueBlob[iValue].pbData = NULL;
rgValueBlob[iValue].cbData = 0;
}
continue;
}
// Assumption: single byte tag for doing comparison
if (pbParaTag) {
// Check if the encoded tag matches one of the expected tags
BYTE bEncodedTag;
BYTE bParaTag;
bEncodedTag = *pb;
while ((bParaTag = *pbParaTag) && bParaTag != bEncodedTag)
pbParaTag++;
if (0 == bParaTag) {
if (ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP != dwOp)
goto InvalidTag;
if (fValueBlob) {
rgValueBlob[iValue].pbData = NULL;
rgValueBlob[iValue].cbData = 0;
}
continue;
}
}
lTagLength = Asn1UtilExtractContent(
pb,
cb,
&cbContent,
&pbContent
);
if (0 >= lTagLength || (DWORD) lTagLength > cb)
goto InvalidTagOrLength;
if (CMSG_INDEFINITE_LENGTH == cbContent) {
if (ASN1UTIL_STEP_INTO_VALUE_OP != dwOp)
goto UnsupportedIndefiniteLength;
else if (fValueBlob && (dwFlags & ASN1UTIL_DEFINITE_LENGTH_FLAG))
goto NotAllowedIndefiniteLength;
cbValue = CMSG_INDEFINITE_LENGTH;
} else {
cbValue = cbContent + lTagLength;
if (cbValue > cb)
goto TooLittleData;
}
if (fValueBlob) {
if (dwParaFlags & ASN1UTIL_RETURN_CONTENT_BLOB_FLAG) {
rgValueBlob[iValue].pbData = (BYTE *) pbContent;
rgValueBlob[iValue].cbData = cbContent;
} else if (dwParaFlags & ASN1UTIL_RETURN_VALUE_BLOB_FLAG) {
rgValueBlob[iValue].pbData = (BYTE *) pb;
rgValueBlob[iValue].cbData = cbValue;
}
}
switch (dwOp) {
case ASN1UTIL_STEP_INTO_VALUE_OP:
pb += lTagLength;
cb -= lTagLength;
break;
case ASN1UTIL_STEP_OVER_VALUE_OP:
case ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP:
pb += cbValue;
cb -= cbValue;
break;
default:
goto InvalidArg;
}
}
lAllValues = (LONG)(pb - pbEncoded);
assert((DWORD) lAllValues <= cbEncoded);
CommonReturn:
*pcValue = iValue;
return lAllValues;
ErrorReturn:
lAllValues = -((LONG)(pb - pbEncoded)) - 1;
goto CommonReturn;
SET_ERROR(TooLittleData, ERROR_INVALID_DATA)
SET_ERROR(InvalidTag, ERROR_INVALID_DATA)
SET_ERROR(InvalidTagOrLength, ERROR_INVALID_DATA)
SET_ERROR(InvalidArg, E_INVALIDARG)
SET_ERROR(UnsupportedIndefiniteLength, ERROR_INVALID_DATA)
SET_ERROR(NotAllowedIndefiniteLength, ERROR_INVALID_DATA)
}
static const BYTE rgbSeqTag[] = {ASN1UTIL_TAG_SEQ, 0};
static const BYTE rgbSetTag[] = {ASN1UTIL_TAG_SET, 0};
static const BYTE rgbOIDTag[] = {ASN1UTIL_TAG_OID, 0};
static const BYTE rgbIntegerTag[] = {ASN1UTIL_TAG_INTEGER, 0};
static const BYTE rgbBitStringTag[] = {ASN1UTIL_TAG_BITSTRING, 0};
static const BYTE rgbConstructedContext0Tag[] =
{ASN1UTIL_TAG_CONSTRUCTED_CONTEXT_0, 0};
static const ASN1UTIL_EXTRACT_VALUE_PARA rgExtractSignedDataContentPara[] = {
// 0 - ContentInfo ::= SEQUENCE {
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
// 1 - contentType ContentType,
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG |
ASN1UTIL_STEP_OVER_VALUE_OP, rgbOIDTag,
// 2 - content [0] EXPLICIT ANY -- OPTIONAL
ASN1UTIL_STEP_INTO_VALUE_OP, rgbConstructedContext0Tag,
// 3 - SignedData ::= SEQUENCE {
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
// 4 - version INTEGER,
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG |
ASN1UTIL_STEP_OVER_VALUE_OP, rgbIntegerTag,
// 5 - digestAlgorithms DigestAlgorithmIdentifiers,
ASN1UTIL_STEP_OVER_VALUE_OP, rgbSetTag,
// 6 - ContentInfo ::= SEQUENCE {
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG |
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
// 7 - contentType ContentType,
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG |
ASN1UTIL_STEP_OVER_VALUE_OP, rgbOIDTag,
// 8 - content [0] EXPLICIT ANY -- OPTIONAL
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG |
ASN1UTIL_STEP_INTO_VALUE_OP, rgbConstructedContext0Tag,
};
#define SIGNED_DATA_CONTENT_OUTER_OID_VALUE_INDEX 1
#define SIGNED_DATA_CONTENT_VERSION_VALUE_INDEX 4
#define SIGNED_DATA_CONTENT_INNER_OID_VALUE_INDEX 7
#define SIGNED_DATA_CONTENT_INFO_SEQ_VALUE_INDEX 6
#define SIGNED_DATA_CONTENT_CONTEXT_0_VALUE_INDEX 8
#define SIGNED_DATA_CONTENT_VALUE_COUNT \
(sizeof(rgExtractSignedDataContentPara) / \
sizeof(rgExtractSignedDataContentPara[0]))
static const ASN1UTIL_EXTRACT_VALUE_PARA rgExtractCertSignedContent[] = {
// 0 - SignedContent ::= SEQUENCE {
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
// 1 - toBeSigned NOCOPYANY,
ASN1UTIL_RETURN_VALUE_BLOB_FLAG |
ASN1UTIL_STEP_OVER_VALUE_OP, NULL,
// 2 - algorithm AlgorithmIdentifier,
ASN1UTIL_STEP_OVER_VALUE_OP, rgbSeqTag,
// 3 - signature BITSTRING
ASN1UTIL_STEP_OVER_VALUE_OP, rgbBitStringTag,
};
#define CERT_TO_BE_SIGNED_VALUE_INDEX 1
#define CERT_SIGNED_CONTENT_VALUE_COUNT \
(sizeof(rgExtractCertSignedContent) / \
sizeof(rgExtractCertSignedContent[0]))
static const ASN1UTIL_EXTRACT_VALUE_PARA rgExtractCertPublicKeyInfo[] = {
// 0 - SignedContent ::= SEQUENCE {
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
// 1 - CertificateToBeSigned ::= SEQUENCE {
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
// 2 - version [0] CertificateVersion DEFAULT v1,
ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP, rgbConstructedContext0Tag,
// 3 - serialNumber CertificateSerialNumber,
ASN1UTIL_STEP_OVER_VALUE_OP, rgbIntegerTag,
// 4 - signature AlgorithmIdentifier,
ASN1UTIL_STEP_OVER_VALUE_OP, rgbSeqTag,
// 5 - issuer NOCOPYANY, -- really Name
ASN1UTIL_STEP_OVER_VALUE_OP, rgbSeqTag,
// 6 - validity Validity,
ASN1UTIL_STEP_OVER_VALUE_OP, rgbSeqTag,
// 7 - subject NOCOPYANY, -- really Name
ASN1UTIL_STEP_OVER_VALUE_OP, rgbSeqTag,
// 8 - subjectPublicKeyInfo SubjectPublicKeyInfo,
ASN1UTIL_RETURN_VALUE_BLOB_FLAG |
ASN1UTIL_STEP_OVER_VALUE_OP, rgbSeqTag
};
#define CERT_PUBLIC_KEY_INFO_VALUE_INDEX 8
#define CERT_PUBLIC_KEY_INFO_VALUE_COUNT \
(sizeof(rgExtractCertPublicKeyInfo) / \
sizeof(rgExtractCertPublicKeyInfo[0]))
// #define szOID_RSA_signedData "1.2.840.113549.1.7.2"
static const BYTE rgbOIDSignedData[] =
{0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02};
static const CRYPT_DER_BLOB EncodedOIDSignedData = {
sizeof(rgbOIDSignedData), (BYTE *) rgbOIDSignedData
};
#ifdef CMS_PKCS7
// #define szOID_RSA_Data "1.2.840.113549.1.7.1"
static const BYTE rgbOIDData[] =
{0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01};
static const CRYPT_DER_BLOB EncodedOIDData = {
sizeof(rgbOIDData), (BYTE *) rgbOIDData
};
#endif // CMS_PKCS7
// The encoded OID only includes the content octets. Excludes the tag and
// length octets.
static BOOL CompareEncodedOID(
IN const CRYPT_DER_BLOB *pEncodedOID1,
IN const CRYPT_DER_BLOB *pEncodedOID2
)
{
if (pEncodedOID1->cbData == pEncodedOID2->cbData &&
0 == memcmp(pEncodedOID1->pbData, pEncodedOID2->pbData,
pEncodedOID1->cbData))
return TRUE;
else
return FALSE;
}
//+-------------------------------------------------------------------------
// Skips past PKCS7 ASN.1 encoded values to get to the SignedData content.
//
// Checks that the outer ContentType has the SignedData OID and optionally
// checks the inner SignedData content's ContentType.
//
// Returns:
// success - the number of bytes skipped, >=0
// failure - <0
//
// If the SignedData content is indefinite-length encoded,
// *pcbContent is set to CMSG_INDEFINITE_LENGTH
//--------------------------------------------------------------------------
LONG
WINAPI
Asn1UtilExtractPKCS7SignedDataContent(
IN const BYTE *pbEncoded,
IN DWORD cbEncoded,
IN OPTIONAL const CRYPT_DER_BLOB *pEncodedInnerOID,
OUT DWORD *pcbContent,
OUT const BYTE **ppbContent
)
{
LONG lSkipped;
DWORD cValue;
CRYPT_DER_BLOB rgValueBlob[SIGNED_DATA_CONTENT_VALUE_COUNT];
DWORD cbContent;
const BYTE *pbContent;
DWORD cbSeq;
const BYTE *pbSeq;
cValue = SIGNED_DATA_CONTENT_VALUE_COUNT;
if (0 >= (lSkipped = Asn1UtilExtractValues(
pbEncoded,
cbEncoded,
0, // dwFlags
&cValue,
rgExtractSignedDataContentPara,
rgValueBlob
)))
goto ExtractValuesError;
pbContent = rgValueBlob[SIGNED_DATA_CONTENT_CONTEXT_0_VALUE_INDEX].pbData;
cbContent = rgValueBlob[SIGNED_DATA_CONTENT_CONTEXT_0_VALUE_INDEX].cbData;
// For definite-length encoded, check that the content wasn't
// omitted.
//
// Note, for indefinite-length encoding, if the content was omitted,
// we would have had a 0 tag instead of the CONTEXT_0 tag.
cbSeq = rgValueBlob[SIGNED_DATA_CONTENT_INFO_SEQ_VALUE_INDEX].cbData;
pbSeq = rgValueBlob[SIGNED_DATA_CONTENT_INFO_SEQ_VALUE_INDEX].pbData;
if (CMSG_INDEFINITE_LENGTH != cbSeq && pbContent >= (pbSeq + cbSeq))
goto NoSignedDataError;
#ifdef CMS_PKCS7
// For V3 SignedData, non Data types are wrapped, encapsulated with a
// OCTET string
if (1 == rgValueBlob[SIGNED_DATA_CONTENT_VERSION_VALUE_INDEX].cbData &&
CMSG_SIGNED_DATA_V3 <=
*(rgValueBlob[SIGNED_DATA_CONTENT_VERSION_VALUE_INDEX].pbData)
&&
!CompareEncodedOID(
&rgValueBlob[SIGNED_DATA_CONTENT_INNER_OID_VALUE_INDEX],
&EncodedOIDData)
&&
0 != cbContent && ASN1UTIL_TAG_OCTETSTRING == *pbContent
) {
LONG lTagLength;
const BYTE *pbInner;
DWORD cbInner;
// Advance past the outer OCTET wrapper
lTagLength = Asn1UtilExtractContent(
pbContent,
cbEncoded - lSkipped,
&cbInner,
&pbInner
);
if (0 < lTagLength) {
lSkipped += lTagLength;
cbContent = cbInner;
pbContent = pbInner;
}
}
#endif
if (CMSG_INDEFINITE_LENGTH == cbContent) {
// Extract the pbContent and attempt to get its definite length
LONG lTagLength;
const BYTE *pbInner;
DWORD cbInner;
lTagLength = Asn1UtilExtractContent(
pbContent,
cbEncoded - lSkipped,
&cbInner,
&pbInner
);
if (0 < lTagLength && CMSG_INDEFINITE_LENGTH != cbInner)
cbContent = cbInner + lTagLength;
}
// Verify that the outer ContentType is SignedData and the inner
// ContentType is the specified type
if (!CompareEncodedOID(
&rgValueBlob[SIGNED_DATA_CONTENT_OUTER_OID_VALUE_INDEX],
&EncodedOIDSignedData
))
goto NotSignedDataContentType;
if (pEncodedInnerOID && !CompareEncodedOID(
&rgValueBlob[SIGNED_DATA_CONTENT_INNER_OID_VALUE_INDEX],
pEncodedInnerOID
))
goto UnexpectedInnerContentTypeError;
*pcbContent = cbContent;
*ppbContent = pbContent;
CommonReturn:
return lSkipped;
ErrorReturn:
if (0 <= lSkipped)
lSkipped = -lSkipped - 1;
*pcbContent = 0;
*ppbContent = NULL;
goto CommonReturn;
TRACE_ERROR(ExtractValuesError)
SET_ERROR(NoSignedDataError, ERROR_INVALID_DATA)
SET_ERROR(NotSignedDataContentType, ERROR_INVALID_DATA)
SET_ERROR(UnexpectedInnerContentTypeError, ERROR_INVALID_DATA)
}
//+-------------------------------------------------------------------------
// Verifies this is a certificate ASN.1 encoded signed content.
// Returns the pointer to and length of the ToBeSigned content.
//
// Returns an error if the ToBeSigned content isn't definite length
// encoded.
//--------------------------------------------------------------------------
BOOL
WINAPI
Asn1UtilExtractCertificateToBeSignedContent(
IN const BYTE *pbEncoded,
IN DWORD cbEncoded,
OUT DWORD *pcbContent,
OUT const BYTE **ppbContent
)
{
BOOL fResult;
DWORD cValue;
CRYPT_DER_BLOB rgValueBlob[CERT_SIGNED_CONTENT_VALUE_COUNT];
cValue = CERT_SIGNED_CONTENT_VALUE_COUNT;
if (0 >= Asn1UtilExtractValues(
pbEncoded,
cbEncoded,
ASN1UTIL_DEFINITE_LENGTH_FLAG,
&cValue,
rgExtractCertSignedContent,
rgValueBlob
))
goto ExtractValuesError;
*ppbContent = rgValueBlob[CERT_TO_BE_SIGNED_VALUE_INDEX].pbData;
*pcbContent = rgValueBlob[CERT_TO_BE_SIGNED_VALUE_INDEX].cbData;
fResult = TRUE;
CommonReturn:
return fResult;
ErrorReturn:
fResult = FALSE;
*ppbContent = NULL;
*pcbContent = 0;
goto CommonReturn;
TRACE_ERROR(ExtractValuesError)
}
//+-------------------------------------------------------------------------
// Returns the pointer to and length of the SubjectPublicKeyInfo value in
// a signed and encoded X.509 certificate.
//
// Returns an error if the value isn't definite length encoded.
//--------------------------------------------------------------------------
BOOL
WINAPI
Asn1UtilExtractCertificatePublicKeyInfo(
IN const BYTE *pbEncoded,
IN DWORD cbEncoded,
OUT DWORD *pcbPublicKeyInfo,
OUT const BYTE **ppbPublicKeyInfo
)
{
BOOL fResult;
DWORD cValue;
CRYPT_DER_BLOB rgValueBlob[CERT_PUBLIC_KEY_INFO_VALUE_COUNT];
cValue = CERT_PUBLIC_KEY_INFO_VALUE_COUNT;
if (0 >= Asn1UtilExtractValues(
pbEncoded,
cbEncoded,
ASN1UTIL_DEFINITE_LENGTH_FLAG,
&cValue,
rgExtractCertPublicKeyInfo,
rgValueBlob
))
goto ExtractValuesError;
*ppbPublicKeyInfo = rgValueBlob[CERT_PUBLIC_KEY_INFO_VALUE_INDEX].pbData;
*pcbPublicKeyInfo = rgValueBlob[CERT_PUBLIC_KEY_INFO_VALUE_INDEX].cbData;
fResult = TRUE;
CommonReturn:
return fResult;
ErrorReturn:
fResult = FALSE;
*ppbPublicKeyInfo = NULL;
*pcbPublicKeyInfo = 0;
goto CommonReturn;
TRACE_ERROR(ExtractValuesError)
}
// Special RDN containing the KEY_ID. Its value type is CERT_RDN_OCTET_STRING.
//#define szOID_KEYID_RDN "1.3.6.1.4.1.311.10.7.1"
static const BYTE rgbOIDKeyIdRDN[] =
{0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0A, 0x07, 0x01};
static const CRYPT_DER_BLOB EncodedOIDKeyIdRDN = {
sizeof(rgbOIDKeyIdRDN), (BYTE *) rgbOIDKeyIdRDN
};
static const BYTE rgbOctetStringTag[] = {ASN1UTIL_TAG_OCTETSTRING, 0};
static const ASN1UTIL_EXTRACT_VALUE_PARA rgExtractNameKeyIdRDNPara[] = {
// 0 - Name ::= SEQUENCE OF RelativeDistinguishedName
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
// 1 - RelativeDistinguishedName ::= SET OF AttributeTypeValue
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSetTag,
// 2 - AttributeTypeValue ::= SEQUENCE
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
// 3 - type EncodedObjectID,
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG |
ASN1UTIL_STEP_OVER_VALUE_OP, rgbOIDTag,
// 4 - value NOCOPYANY
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG |
ASN1UTIL_STEP_OVER_VALUE_OP, rgbOctetStringTag,
};
#define NAME_KEYID_RDN_OID_VALUE_INDEX 3
#define NAME_KEYID_RDN_OCTET_VALUE_INDEX 4
#define NAME_KEYID_RDN_VALUE_COUNT \
(sizeof(rgExtractNameKeyIdRDNPara) / \
sizeof(rgExtractNameKeyIdRDNPara[0]))
//+-------------------------------------------------------------------------
// If the Issuer and SerialNumber in the CERT_INFO contains a special
// KeyID RDN attribute returns TRUE with pKeyId's cbData and pbData updated
// with the RDN attribute's OCTET_STRING value. Otherwise, returns FALSE.
//--------------------------------------------------------------------------
BOOL
WINAPI
Asn1UtilExtractKeyIdFromCertInfo(
IN PCERT_INFO pCertInfo,
OUT PCRYPT_HASH_BLOB pKeyId
)
{
DWORD cValue;
CRYPT_DER_BLOB rgValueBlob[NAME_KEYID_RDN_VALUE_COUNT];
if (0 == pCertInfo->SerialNumber.cbData ||
0 != *pCertInfo->SerialNumber.pbData)
goto NoKeyId;
cValue = NAME_KEYID_RDN_VALUE_COUNT;
if (0 > Asn1UtilExtractValues(
pCertInfo->Issuer.pbData,
pCertInfo->Issuer.cbData,
0, // dwFlags
&cValue,
rgExtractNameKeyIdRDNPara,
rgValueBlob
))
goto NoKeyId;
if (!CompareEncodedOID(
&rgValueBlob[NAME_KEYID_RDN_OID_VALUE_INDEX],
&EncodedOIDKeyIdRDN))
goto NoKeyId;
if (CMSG_INDEFINITE_LENGTH ==
rgValueBlob[NAME_KEYID_RDN_OCTET_VALUE_INDEX].cbData)
goto NoKeyId;
pKeyId->pbData = rgValueBlob[NAME_KEYID_RDN_OCTET_VALUE_INDEX].pbData;
pKeyId->cbData = rgValueBlob[NAME_KEYID_RDN_OCTET_VALUE_INDEX].cbData;
return TRUE;
NoKeyId:
pKeyId->pbData = NULL;
pKeyId->cbData = 0;
return FALSE;
}