📄 certstr.c
字号:
/****************************************************************************
* *
* Certificate String/DN Routines *
* Copyright Peter Gutmann 1996-2002 *
* *
****************************************************************************/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL ) || defined( INC_CHILD )
#include "asn1.h"
#include "asn1objs.h"
#include "cert.h"
#else
#include "keymgmt/asn1.h"
#include "keymgmt/asn1objs.h"
#include "keymgmt/cert.h"
#endif /* Compiler-specific includes */
/* DN component info flags. Some implementations may place more than one
AVA into a RDN. In this case we set a flag to indicate that the RDN
continues in the next DN component structure. If the RDN/DN was set by
specifying the entire DN at once using a text DN string, it's not a good
idea to allow random changes to it so we mark the components as locked.
If we're reading data from an external source the DN can contain all sorts
of strange stuff, so we set a flag to tell the DN component-handling code
not to perform any validity checking on the components as they're added */
#define DN_FLAG_CONTINUED 0x01 /* RDN continues with another AVA */
#define DN_FLAG_LOCKED 0x02 /* RDN can't be modified */
#define DN_FLAG_NOCHECK 0x04 /* Don't check validity of components */
/* The structure to hold a DN component */
typedef struct DC {
/* The next and previous list element in the linked list of DN
components */
struct DC *next, *prev;
/* DN component type and type information */
CRYPT_ATTRIBUTE_TYPE type; /* cryptlib component type */
ASN1_STRINGTYPE stringType; /* Component string data type */
const void *typeInfo; /* Type info for this component */
/* Various flags for this RDN or AVA: Whether the RDN continues with
another AVA, whether the RDN is locked against modification */
int flags;
/* DN component data. If it's a sensible-length value (virtually all
are) we use a fixed memory block for the value data because it's not
worth the overhead of mallocing each tiny block, if it's a longer
string we store it in a dynamically-allocated buffer */
BYTE smallValue[ CRYPT_MAX_TEXTSIZE ];
void *value; /* Attribute data payload */
int valueLength; /* The value of this component */
/* Encoding information: The overall size of the RDN data (without the
tag and length) if this is the first or only component of an RDN, and
the size of the AVA data */
int encodedRDNdataSize, encodedAVAdataSize;
} DN_COMPONENT;
/****************************************************************************
* *
* Character Set Management Functions *
* *
****************************************************************************/
/* Most systems now include some sort of support for various wide char
functions. We require wchar_t (not supported older DOS compilers and
legacy Unixen), mbstowcs() (usually supported, but with varying degrees of
success), and towlower() (everything from unsupported to token support to
relatively good support). In addition since wchar_t can be anything from
8 bits (Borland C++ under DOS) to 64 bits (RISC Unixen), we define a
bmpchar_t (short for Unicode/BMPString char) which is always 16 bits as
required for BMPStrings. The conversion to and from a BMPString and
wchar_t may require narrowing or widening of characters, and possibly
endianness conversion as well */
typedef unsigned short int bmpchar_t; /* Unicode data type */
#if defined( __MSDOS16__ ) && !defined( __BORLANDC__ )
typedef unsigned short int wchar_t; /* Widechar data type */
#endif /* OS's which don't support wide chars */
#if defined( __BORLANDC__ ) && ( __BORLANDC__ == 0x410 )
#define wchar_t unsigned short int /* BC++ 3.1 has an 8-bit wchar_t */
#endif /* BC++ 3.1 */
#ifdef __UNIX__
#if defined( __hpux ) || defined( _AIX ) || defined( _M_XENIX )
#include <wchar.h>
#define HAS_TOWLOWER
/* The following check tries to include the wcXXX stuff by default,
which should work for most recent Unixen */
#elif !( defined( __linux__ ) || ( defined( sun ) && OSVERSION < 5 ) || \
defined( __bsdi__ ) || defined( __FreeBSD__ ) || \
defined( __OpenBSD__ ) || defined( __SCO_VERSION__ ) )
#include <wctype.h>
#define HAS_TOWLOWER
#endif /* OS's which support towlower() */
#endif /* __UNIX__ */
#if defined( __WIN32__ ) && \
!( defined( __BORLANDC__ ) && ( __BORLANDC__ < 0x500 ) )
/* Win95 doesn't have Unicode support so we can't use CompareString()
since it compares ANSI rather than Unicode strings. However there is
support for towlower() via the C library, so we can use the general
wchar_t comparison routines */
#include <wchar.h>
#define HAS_TOWLOWER
#endif /* __WIN32__ */
#ifdef __IBM4758__
/* Embedded environments rarely have i18n support */
#define NO_WIDECHAR
#endif /* __IBM4758__ */
/* The CSTR_EQUAL define doesn't appear unless the October'97 MSDN stealth
upgrade is installed, so we define it here if it's not already defined */
#if defined( __WIN32__ ) && !defined( CSTR_EQUAL )
#define CSTR_EQUAL 2
#endif /* __WIN32__ && !CSTR_EQUAL */
/* Useful defines to help with scaling offsets and lengths for wchar_t and
bmpchar_t strings */
#define WCSIZE ( sizeof( wchar_t ) )
#define UCSIZE 2
/* Because of the bizarre (and mostly useless) collection of ASN.1 character
types, we need to be very careful about what we allow in a string. The
following table is used to determine whether a character is valid within
certain string types.
Although IA5String and VisibleString/ISO646String are technically
different, the only real difference is that IA5String allows the full
range of control characters, which isn't notably useful. For this reason
we treat both as ISO646String */
#define P 1 /* PrintableString */
#define I 2 /* IA5String/VisibleString/ISO646String */
#define PI ( P | I ) /* PrintableString and IA5String */
static const int charFlags[] = {
/* 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* ! " # $ % & ' ( ) * + , - . / */
PI, I, I, I, I, I, I, PI, PI, PI, I, PI, PI, PI, PI, PI,
/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, I, I, PI, I, PI,
/* @ A B C D E F G H I J K L M N O */
I, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,
/* P Q R S T U V W X Y Z [ \ ] ^ _ */
PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, I, I, I, I, I,
/* ` a b c d e f g h i j k l m n o */
I, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI,
/* p q r s t u v w x y z { | } ~ DL */
PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, PI, I, I, I, I, 0
};
/* Try and guess whether a byte string is a widechar string */
static BOOLEAN isWidecharString( const BYTE *string, const int length )
{
wchar_t *wcString = ( wchar_t * ) string;
int i;
/* Check for a Unicode BOM at the start of the string */
if( *wcString == 0xFFFE || *wcString == 0xFEFF )
return( TRUE ); /* Definitely Unicode with a BOM */
#if !defined( __MSDOS16__ ) && !defined( __WIN16__ )
/* If wchar_t is > 16 bits and the bits above 16 are set or all zero,
it's either definitely not Unicode or Unicode */
if( WCSIZE > 2 )
return( ( *wcString > 0x0FFFF ) ? FALSE : TRUE );
#endif /* > 16-bit machines */
/* wchar_t is 16 bits, check whether it's in the form 00 xx 00 xx. The
code used is safe because to get to this point the string has to be
some multiple of 2 bytes long. Note that if someone passes in a
1-byte string and mistakenly includes the terminator in the length
it'll be identified as a 16-bit widechar string, but this doesn't
really matter since it'll get "converted" into a non-widechar string
later */
for( i = 0; i < length; i += 2 )
{
if( *wcString > 0xFF )
return( FALSE ); /* Probably 8-bit chars */
wcString++;
}
return( TRUE ); /* Probably 16-bit chars */
}
/* Try and figure out the ASN.1 string type for a string. This detects (or
at least tries to detect) not only the basic string type, but also basic
string types encoded as widechar strings, and BMPStrings encoded as basic
string types */
static ASN1_STRINGTYPE getStringType( const BYTE *string, int length,
const BOOLEAN isASN1string )
{
BOOLEAN notPrintable = FALSE, notIA5 = FALSE;
assert( string != NULL );
assert( length > 0 );
/* Try and figure out whether it's a widechar or BMPString. In theory
under NT we could insert:
#ifdef __WIN32__
if( !isWin95 && IsTextUnicode( ( CONST LPVOID ) string, length, NULL ) )
return( STRINGTYPE_UNICODE );
else
#endif
before the following code, however the tests in IsTextUnicode() seem
to be pretty simplistic and aren't very reliable (for example they
won't report a latin1 string encoded as Unicode as being Unicode).
Because of this we don't even try to use it and instead use our own
tests on all platforms. These use all sorts of ad-hockery to try and
guess whether a string is a widechar/BMPString one or not. This
usually works, but probably mainly because we rarely encounter
widechar/BMPString strings */
if( !isASN1string )
{
/* If it a multiple of wchar_t or bmpchar_t in size, check whether it's
a widechar string. If it's a widechar string it may actually be
something else which has been bloated out into widechars, so we
check for this as well */
if( !( length % WCSIZE ) && isWidecharString( string, length ) )
{
wchar_t *wcString = ( wchar_t * ) string;
/* Make sure we don't include the BOM in the test */
if( *wcString == 0xFFFE || *wcString == 0xFEFF )
{ wcString++; length -= WCSIZE; }
while( length > 0 )
{
wchar_t ch = *wcString;
/* If the high bit is set, it's not an ASCII subset */
if( ch >= 128 )
{
notPrintable = notIA5 = TRUE;
if( !charFlags[ ch & 0x7F ] )
/* It's not 8859-1 either */
return( STRINGTYPE_UNICODE );
}
else
/* Check whether it's a PrintableString */
if( !( charFlags[ ch ] & P ) )
notPrintable = TRUE;
wcString++;
length -= WCSIZE;
}
return( notIA5 ? STRINGTYPE_UNICODE_T61 : notPrintable ? \
STRINGTYPE_UNICODE_IA5 : STRINGTYPE_UNICODE_PRINTABLE );
}
}
else
/* If it's a multiple of bmpchar_t in size, check whether it's a
BMPString stuffed into a T61String or an 8-bit string encoded as
a BMPString. The following code assumes that anything claiming
to be a BMPString is always something else, this currently seems
to hold true for all BMPStrings. Hopefully by the time anyone
gets around to using > 8-bit characters everyone will be using
UTF8String's because there's no easy way to distinguish between a
byte string which is a > 8-bit BMPString and a 7/8-bit string */
if( !( length % UCSIZE ) )
{
bmpchar_t *bmpString = ( bmpchar_t * ) string;
int stringLength = length;
/* If the first character is a null, it's an 8-bit string stuffed
into a BMPString */
if( !*string )
{
while( stringLength > 0 )
{
/* BMPString characters are always big-endian, so we need
to convert them if we're on a little-endian system */
#ifdef DATA_LITTLEENDIAN
bmpchar_t ch = ( ( *bmpString & 0xFF ) << 8 ) | \
( *bmpString >> 8 );
#else
bmpchar_t ch = *bmpString;
#endif /* DATA_LITTLEENDIAN */
/* If the high bit is set, it's not an ASCII subset */
if( ch >= 128 )
{
notPrintable = notIA5 = TRUE;
if( !charFlags[ ch & 0x7F ] )
/* It's not 8859-1 either */
return( STRINGTYPE_UNICODE );
}
else
/* Check whether it's a PrintableString */
if( !( charFlags[ ch ] & P ) )
notPrintable = TRUE;
bmpString++;
stringLength -= UCSIZE;
}
return( notIA5 ? STRINGTYPE_UNICODE_T61 : notPrintable ? \
STRINGTYPE_UNICODE_IA5 : STRINGTYPE_UNICODE_PRINTABLE );
}
}
/* Walk down the string checking each character */
while( length-- )
{
BYTE ch = *string;
/* If the high bit is set, it's not an ASCII subset */
if( ch >= 128 )
{
notPrintable = notIA5 = TRUE;
if( !charFlags[ ch & 0x7F ] )
/* It's not 8859-1 either, probably some odd widechar type */
return( STRINGTYPE_NONE );
}
else
{
/* Check whether it's a PrintableString */
if( !( charFlags[ ch ] & P ) )
notPrintable = TRUE;
/* Check whether it's something peculiar */
if( !charFlags[ ch ] )
return( STRINGTYPE_NONE );
}
string++;
}
return( notIA5 ? STRINGTYPE_T61 : notPrintable ? STRINGTYPE_IA5 : \
STRINGTYPE_PRINTABLE );
}
/* The SET string type is much more sensible, we map the ASN.1 string types
to the subset used by SET */
static ASN1_STRINGTYPE getSETStringType( const BYTE *string, const int length,
const BOOLEAN isASN1string )
{
ASN1_STRINGTYPE type = getStringType( string, length, isASN1string );
/* If it can be translated directly to a SETString type, use that */
if( type == STRINGTYPE_IA5 || type == STRINGTYPE_PRINTABLE )
return( STRINGTYPE_VISIBLE );
if( type == STRINGTYPE_UNICODE || type == STRINGTYPE_UNICODE_T61 )
return( STRINGTYPE_UNICODE );
if( type == STRINGTYPE_UNICODE_IA5 || type == STRINGTYPE_UNICODE_PRINTABLE )
return( STRINGTYPE_UNICODE_VISIBLE );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -