📄 dumpasn1.c
字号:
/* ASN.1 object dumping code, copyright Peter Gutmann
<pgut001@cs.auckland.ac.nz>, based on ASN.1 dump program by David Kemp
<dpkemp@missi.ncsc.mil>, with contributions from various people including
Matthew Hamrick <hamrick@rsa.com>, Bruno Couillard
<bcouillard@chrysalis-its.com>, Hallvard Furuseth
<h.b.furuseth@usit.uio.no>, Geoff Thorpe <geoff@raas.co.nz>, David Boyce
<d.boyce@isode.com>, John Hughes <john.hughes@entegrity.com>, Life is hard,
and then you die <ronald@trustpoint.com>, Hans-Olof Hermansson
<hans-olof.hermansson@postnet.se>, Tor Rustad <Tor.Rustad@bbs.no>, and
several other people whose names I've misplaced.
Available from http://www.cs.auckland.ac.nz/~pgut001/dumpasn1.c.
Last updated 27 October 2001.
This version of dumpasn1 requires a config file dumpasn1.cfg to be present
in the same location as the program itself or in a standard directory
where binaries live (it will run without it but will display a warning
message, you can configure the path either by hardcoding it in or using an
environment variable as explained further down). The config file is
available from http://www.cs.auckland.ac.nz/~pgut001/dumpasn1.cfg.
This code assumes that the input data is binary, having come from a MIME-
aware mailer or been piped through a decoding utility if the original
format used base64 encoding. Bruno Couillard has created a modified
version which will read raw base64-encoded data (ie without any MIME
encapsulation or other headers) directly, at the expense of being somewhat
non-portable. Alternatively, you can use utilities like uudeview (which
will strip virtually any kind of encoding, MIME, PEM, PGP, whatever) to
recover the binary original.
You can use this code in whatever way you want, as long as you don't try
to claim you wrote it.
Editing notes: Tabs to 4, phasers to stun */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* The update string, printed as part of the help screen */
#define UPDATE_STRING "27 October 2001"
/* Useful defines */
#ifndef TRUE
#define FALSE 0
#define TRUE ( !FALSE )
#endif /* TRUE */
/* Tandem Guardian NonStop Kernel options */
#ifdef __TANDEM
#pragma nolist /* Spare us the source listing, no GUI... */
#pragma nowarn (1506) /* Implicit type conversion: int to char etc */
#endif /* __TANDEM */
/* SunOS 4.x doesn't define seek codes or exit codes or FILENAME_MAX (it does
define _POSIX_MAX_PATH, but in funny locations and to different values
depending on which include file you use). Strictly speaking this code
isn't right since we need to use PATH_MAX, however not all systems define
this, some use _POSIX_PATH_MAX, and then there are all sorts of variations
and other defines you have to check which require about a page of code to
cover each OS, so we just use max( FILENAME_MAX, 512 ) which should work
for everything */
#ifndef SEEK_SET
#define SEEK_SET 0
#define SEEK_CUR 2
#endif /* No fseek() codes defined */
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#define EXIT_SUCCESS ( !EXIT_FAILURE )
#endif /* No exit() codes defined */
#ifndef FILENAME_MAX
#define FILENAME_MAX 512
#else
#if FILENAME_MAX < 128
#undef FILENAME_MAX
#define FILENAME_MAX 512
#endif /* FILENAME_MAX < 128 */
#endif /* FILENAME_MAX */
/* Under Windows we can do special-case handling for things like BMPStrings */
#if ( defined( _WINDOWS ) || defined( WIN32 ) || defined( _WIN32 ) || \
defined( __WIN32__ ) )
#define __WIN32__
#endif /* Win32 */
/* Under Unix we can do special-case handling for paths. Detecting this is
a bit tricky but the following should find most versions */
#if defined( linux ) || defined( __linux__ ) || defined( sun ) || \
defined( __bsdi__ ) || defined( __FreeBSD__ ) || \
defined( __OpenBSD__ ) || defined( __hpux ) || defined( _M_XENIX ) || \
defined( __osf__ ) || defined( _AIX )
#define __UNIX__
#endif /* Every commonly-used Unix */
/* Tandem NSK: Don't use __TANDEM__, which violates the ISO C reserved name
space, also don't mangle with Tandem OSS, which is almost UNIX */
#ifdef __TANDEM
#ifdef _GUARDIAN_TARGET
#define TANDEM_NSK
#else
#define __UNIX__
#endif /* _GUARDIAN_TARGET */
#endif /* __TANDEM */
/* Some OS's don't define the min() macro */
#ifndef min
#define min(a,b) ( ( a ) < ( b ) ? ( a ) : ( b ) )
#endif /* !min */
/* The level of recursion can get scary for deeply-nested structures so we
use a larger-than-normal stack under DOS */
#ifdef __TURBOC__
extern unsigned _stklen = 16384;
#endif /* __TURBOC__ */
/* When we dump a nested data object encapsulated within a larger object, the
length is initially set to a magic value which is adjusted to the actual
length once we start parsing the object */
#define LENGTH_MAGIC 177545L
/* Tag classes */
#define CLASS_MASK 0xC0 /* Bits 8 and 7 */
#define UNIVERSAL 0x00 /* 0 = Universal (defined by ITU X.680) */
#define APPLICATION 0x40 /* 1 = Application */
#define CONTEXT 0x80 /* 2 = Context-specific */
#define PRIVATE 0xC0 /* 3 = Private */
/* Encoding type */
#define FORM_MASK 0x20 /* Bit 6 */
#define PRIMITIVE 0x00 /* 0 = primitive */
#define CONSTRUCTED 0x20 /* 1 = constructed */
/* Universal tags */
#define TAG_MASK 0x1F /* Bits 5 - 1 */
#define EOC 0x00 /* 0: End-of-contents octets */
#define BOOLEAN 0x01 /* 1: Boolean */
#define INTEGER 0x02 /* 2: Integer */
#define BITSTRING 0x03 /* 2: Bit string */
#define OCTETSTRING 0x04 /* 4: Byte string */
#define NULLTAG 0x05 /* 5: NULL */
#define OID 0x06 /* 6: Object Identifier */
#define OBJDESCRIPTOR 0x07 /* 7: Object Descriptor */
#define EXTERNAL 0x08 /* 8: External */
#define REAL 0x09 /* 9: Real */
#define ENUMERATED 0x0A /* 10: Enumerated */
#define EMBEDDED_PDV 0x0B /* 11: Embedded Presentation Data Value */
#define UTF8STRING 0x0C /* 12: UTF8 string */
#define SEQUENCE 0x10 /* 16: Sequence/sequence of */
#define SET 0x11 /* 17: Set/set of */
#define NUMERICSTRING 0x12 /* 18: Numeric string */
#define PRINTABLESTRING 0x13 /* 19: Printable string (ASCII subset) */
#define T61STRING 0x14 /* 20: T61/Teletex string */
#define VIDEOTEXSTRING 0x15 /* 21: Videotex string */
#define IA5STRING 0x16 /* 22: IA5/ASCII string */
#define UTCTIME 0x17 /* 23: UTC time */
#define GENERALIZEDTIME 0x18 /* 24: Generalized time */
#define GRAPHICSTRING 0x19 /* 25: Graphic string */
#define VISIBLESTRING 0x1A /* 26: Visible string (ASCII subset) */
#define GENERALSTRING 0x1B /* 27: General string */
#define UNIVERSALSTRING 0x1C /* 28: Universal string */
#define BMPSTRING 0x1E /* 30: Basic Multilingual Plane/Unicode string */
/* Length encoding */
#define LEN_XTND 0x80 /* Indefinite or long form */
#define LEN_MASK 0x7F /* Bits 7 - 1 */
/* Various special-case operations to perform on strings */
typedef enum {
STR_NONE, /* No special handling */
STR_UTCTIME, /* Check it's UTCTime */
STR_GENERALIZED, /* Check it's GeneralizedTime */
STR_PRINTABLE, /* Check it's a PrintableString */
STR_IA5, /* Check it's an IA5String */
STR_BMP, /* Read and display string as Unicode */
STR_BMP_REVERSED /* STR_BMP with incorrect endianness */
} STR_OPTION;
/* Structure to hold info on an ASN.1 item */
typedef struct {
int id; /* Identifier */
int tag; /* Tag */
long length; /* Data length */
int indefinite; /* Item has indefinite length */
int headerSize; /* Size of tag+length */
unsigned char header[ 8 ]; /* Tag+length data */
} ASN1_ITEM;
/* Config options */
static int printDots = FALSE; /* Whether to print dots to align columns */
static int doPure = FALSE; /* Print data without LHS info column */
static int doDumpHeader = FALSE; /* Dump tag+len in hex (level = 0, 1, 2) */
static int extraOIDinfo = FALSE; /* Print extra information about OIDs */
static int doHexValues = FALSE; /* Display size, offset in hex not dec.*/
static int useStdin = FALSE; /* Take input from stdin */
static int zeroLengthAllowed = FALSE;/* Zero-length items allowed */
static int dumpText = FALSE; /* Dump text alongside hex data */
static int printAllData = FALSE; /* Whether to print all data in long blocks */
static int checkEncaps = TRUE; /* Print encaps.data in BIT/OCTET STRINGs */
static int reverseBitString = TRUE; /* Print BIT STRINGs in natural order */
/* Error and warning information */
static int noErrors = 0; /* Number of errors found */
static int noWarnings = 0; /* Number of warnings */
/* Position in the input stream */
static int fPos = 0; /* Absolute position in data */
/* The output stream */
static FILE *output; /* Output stream */
/* Information on an ASN.1 Object Identifier */
#define MAX_OID_SIZE 32
typedef struct tagOIDINFO {
struct tagOIDINFO *next; /* Next item in list */
char oid[ MAX_OID_SIZE ], *comment, *description;
int oidLength; /* Name, rank, serial number */
int warn; /* Whether to warn if OID encountered */
} OIDINFO;
static OIDINFO *oidList = NULL;
/* If the config file isn't present in the current directory, we search the
following paths (this is needed for Unix with dumpasn1 somewhere in the
path, since this doesn't set up argv[0] to the full path). Anything
beginning with a '$' uses the appropriate environment variable. In
addition under Unix we also walk down $PATH looking for it */
#ifdef TANDEM_NSK
#define CONFIG_NAME "asn1cfg"
#else
#define CONFIG_NAME "dumpasn1.cfg"
#endif /* TANDEM_NSK */
#if defined( TANDEM_NSK )
static const char *configPaths[] = {
"$system.security", "$system.system",
NULL
};
#elif defined( __WIN32__ )
static const char *configPaths[] = {
/* Windoze absolute paths. Usually things are on C:, but older NT setups
are easier to do on D: if the initial copy is done to C: */
"c:\\dos\\", "d:\\dos\\", "c:\\windows\\", "d:\\windows\\",
"c:\\winnt\\", "d:\\winnt\\",
/* It's my program, I'm allowed to hardcode in strange paths which noone
else uses */
"c:\\program files\\bin\\",
/* General environment-based paths */
"$DUMPASN1_PATH/",
NULL
};
#else
static const char *configPaths[] = {
/* Unix absolute paths */
"/bin/", "/usr/bin/", "/usr/local/bin/",
/* It's my program, I'm allowed to hardcode in strange paths which noone
else uses */
"$HOME/BIN/",
/* Unix environment-based paths */
"$HOME/", "$HOME/bin/",
/* General environment-based paths */
"$DUMPASN1_PATH/",
NULL
};
#endif /* OS-specific search paths */
#define isEnvTerminator( c ) \
( ( ( c ) == '/' ) || ( ( c ) == '.' ) || ( ( c ) == '$' ) || \
( ( c ) == '\0' ) || ( ( c ) == '~' ) )
/****************************************************************************
* *
* Object Identification/Description Routines *
* *
****************************************************************************/
/* Return descriptive strings for universal tags */
char *idstr( const int tagID )
{
switch( tagID )
{
case EOC:
return( "End-of-contents octets" );
case BOOLEAN:
return( "BOOLEAN" );
case INTEGER:
return( "INTEGER" );
case BITSTRING:
return( "BIT STRING" );
case OCTETSTRING:
return( "OCTET STRING" );
case NULLTAG:
return( "NULL" );
case OID:
return( "OBJECT IDENTIFIER" );
case OBJDESCRIPTOR:
return( "ObjectDescriptor" );
case EXTERNAL:
return( "EXTERNAL" );
case REAL:
return( "REAL" );
case ENUMERATED:
return( "ENUMERATED" );
case EMBEDDED_PDV:
return( "EMBEDDED PDV" );
case UTF8STRING:
return( "UTF8String" );
case SEQUENCE:
return( "SEQUENCE" );
case SET:
return( "SET" );
case NUMERICSTRING:
return( "NumericString" );
case PRINTABLESTRING:
return( "PrintableString" );
case T61STRING:
return( "TeletexString" );
case VIDEOTEXSTRING:
return( "VideotexString" );
case IA5STRING:
return( "IA5String" );
case UTCTIME:
return( "UTCTime" );
case GENERALIZEDTIME:
return( "GeneralizedTime" );
case GRAPHICSTRING:
return( "GraphicString" );
case VISIBLESTRING:
return( "VisibleString" );
case GENERALSTRING:
return( "GeneralString" );
case UNIVERSALSTRING:
return( "UniversalString" );
case BMPSTRING:
return( "BMPString" );
default:
return( "Unknown (Reserved)" );
}
}
/* Return information on an object identifier */
static OIDINFO *getOIDinfo( char *oid, const int oidLength )
{
OIDINFO *oidPtr;
memset( oid + oidLength, 0, 2 );
for( oidPtr = oidList; oidPtr != NULL; oidPtr = oidPtr->next )
if( oidLength == oidPtr->oidLength - 2 && \
!memcmp( oidPtr->oid + 2, oid, oidLength ) )
return( oidPtr );
return( NULL );
}
/* Add an OID attribute */
static int addAttribute( char **buffer, char *attribute )
{
if( ( *buffer = ( char * ) malloc( strlen( attribute ) + 1 ) ) == NULL )
{
puts( "Out of memory." );
return( FALSE );
}
strcpy( *buffer, attribute );
return( TRUE );
}
/* Table to identify valid string chars (taken from cryptlib) */
#define P 1 /* PrintableString */
#define I 2 /* IA5String */
#define PI 3 /* IA5String and PrintableString */
static 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
};
static int isPrintable( int ch )
{
if( ch >= 128 || !( charFlags[ ch ] & P ) )
return( FALSE );
return( TRUE );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -