📄 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>,
Kjetil Barvik <kjetil.barvik@bbs.no>, James Sweeny <jsweeny@us.ibm.com>,
Chris Ridd <chris.ridd@isode.com>, and several other people whose names
I've misplaced (a number of those email addresses probably no longer
work, since this code has been around for awhile).
Available from http://www.cs.auckland.ac.nz/~pgut001/dumpasn1.c.
Last updated 14 April 2008 (version 20080414, if you prefer it that
way). To build under Windows, use 'cl /MD dumpasn1.c'. To build on OS390
or z/OS, use '/bin/c89 -D OS390 -o dumpasn1 dumpasn1.c'.
This code grew slowly over time without much design or planning, and with
extra features being tacked on as required. It's not representative of
my normal coding style.
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. If you need to decode it, it's recommended
that you use a utility 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 (and in case anyone wants to
complain about that, see "Program Indentation and Comprehensiblity",
Richard Miara, Joyce Musselman, Juan Navarro, and Ben Shneiderman,
Communications of the ACM, Vol.26, No.11 (November 1983), p.861) */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef OS390
#include <unistd.h>
#endif /* OS390 */
/* The update string, printed as part of the help screen */
#define UPDATE_STRING "14 April 2008"
/* 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 that 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 paths and Unicode
strings (although in practice it can't really handle much except
latin-1) */
#if ( defined( _WINDOWS ) || defined( WIN32 ) || defined( _WIN32 ) || \
defined( __WIN32__ ) )
#define __WIN32__
#endif /* Win32 */
/* Under Unix we can do special-case handling for paths and Unicode strings.
Detecting Unix systems is a bit tricky but the following should find most
versions. This define implicitly assumes that the system has wchar_t
support, but this is almost always the case except for very old systems,
so it's best to default to allow-all rather than deny-all */
#if defined( linux ) || defined( __linux__ ) || defined( sun ) || \
defined( __bsdi__ ) || defined( __FreeBSD__ ) || defined( __NetBSD__ ) || \
defined( __OpenBSD__ ) || defined( __hpux ) || defined( _M_XENIX ) || \
defined( __osf__ ) || defined( _AIX ) || defined( __MACH__ )
#define __UNIX__
#endif /* Every commonly-used Unix */
#if defined( linux ) || defined( __linux__ )
#define __USE_ISOC99
#include <wchar.h>
#endif /* Linux */
/* For IBM mainframe OSes we use the Posix environment, so it looks like
Unix */
#ifdef OS390
#define __OS390__
#define __UNIX__
#endif /* OS390 / z/OS */
/* Tandem NSK: Don't tangle 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_LATIN1, /* Read and display string as latin-1 */
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; /* Tag class + primitive/constructed */
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 checkCharset = TRUE; /* Check val.of char strs.hidden in OCTET STRs */
#ifndef __OS390__
static int reverseBitString = TRUE; /* Print BIT STRINGs in natural order */
#else
static int reverseBitString = FALSE;/* Natural order on OS390 is the same as ASN.1 */
#endif /* __OS390__ */
static int rawTimeString = FALSE; /* Print raw time strings */
static int shallowIndent = FALSE; /* Perform shallow indenting */
static int outputWidth = 80; /* 80-column display */
/* The indent size and fixed indent string to the left of the data */
#if 0
#define INDENT_SIZE 14
#define INDENT_STRING " : "
#else
#define INDENT_SIZE 11
#define INDENT_STRING " : "
#endif /* 0 */
/* 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 that no-one
else uses */
"c:\\program files\\bin\\",
/* This one seems to be popular as well */
"c:\\program files\\utilities\\",
/* General environment-based paths */
"$DUMPASN1_PATH/",
NULL
};
#elif defined( __OS390__ )
static const char *configPaths[] = {
/* General environment-based paths */
"$DUMPASN1_PATH/",
NULL
};
#else
static const char *configPaths[] = {
#ifndef DEBIAN
/* Unix absolute paths */
"/usr/bin/", "/usr/local/bin/", "/etc/dumpasn1/",
/* Unix environment-based paths */
"$HOME/", "$HOME/bin/",
/* It's my program, I'm allowed to hardcode in strange paths that no-one
else uses */
"$HOME/BIN/",
#else
/* Debian has specific places where you're supposed to dump things */
"$HOME/", "/etc/dumpasn1/",
#endif /* DEBIAN-specific paths */
/* 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 */
static 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)" );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -