|
|
//+-------------------------------------------------------------------------
// 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; }
|