home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / crypl200.zip / KEYMGMT / X509_KEY.C < prev   
Text File  |  1996-10-01  |  16KB  |  539 lines

  1. /****************************************************************************
  2. *                                                                            *
  3. *                              X.509 Key Read Routines                        *
  4. *                            Copyright Peter Gutmann 1996                    *
  5. *                                                                            *
  6. ****************************************************************************/
  7.  
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #if defined( INC_ALL ) ||  defined( INC_CHILD )
  12.   #include "asn1.h"
  13.   #include "asn1keys.h"
  14.   #include "asn1oid.h"
  15.   #include "keymgmt.h"
  16. #else
  17.   #include "keymgmt/asn1.h"
  18.   #include "keymgmt/asn1keys.h"
  19.   #include "keymgmt/asn1oid.h"
  20.   #include "keymgmt/keymgmt.h"
  21. #endif /* Compiler-specific includes */
  22.  
  23. /* The minimum and maximum X.509 certificate format we recognise */
  24.  
  25. #define MIN_X509_VERSION        0    /* X.509v1 */
  26. #define MAX_X509_VERSION        2    /* X.509v3 */
  27.  
  28. /* The maximum length of an X.500 commonName */
  29.  
  30. #define MAX_COMMONNAME_LENGTH    64
  31.  
  32. /* Context-specific tags for the X.509 certificate */
  33.  
  34. enum { CTAG_XC_VERSION, CTAG_XC_ISSUERID, CTAG_XC_SUBJECTID,
  35.        CTAG_XC_EXTENSIONS };
  36.  
  37. /****************************************************************************
  38. *                                                                            *
  39. *                        Object Identifier Handling Routines                    *
  40. *                                                                            *
  41. ****************************************************************************/
  42.  
  43. /* Read a SEQUENCE { OBJECT IDENTIFIER, ... } object and compare it with an
  44.    expected value.  This routine doesn't compare the passed-in object ID
  45.    length with the read length since sometimes all we need is a match down
  46.    one arc of the graph and not a complete match.
  47.  
  48.    This function returns information in a complex and screwball manner:
  49.  
  50.     CRYPT_BADDATA if an object identifier in the correct format wasn't
  51.     found
  52.  
  53.     0 and skips the associated attribute data, storing the total number of
  54.     bytes read in remainingData if an object identifier was found by didn't
  55.     match the required one,
  56.  
  57.     The number of bytes read and the associated attribute data ready to read
  58.     if a match was found */
  59.  
  60. #define checkOID( stream, oid, remainingData ) \
  61.         _checkOID( stream, oid, remainingData, TRUE )
  62. #define checkOIDdata( stream, oid, remainingData ) \
  63.         _checkOID( stream, oid, remainingData, FALSE )
  64.  
  65. static int _checkOID( STREAM *stream, const BYTE *oid, int *remainingData,
  66.                       const BOOLEAN readIdent )
  67.     {
  68.     BYTE buffer[ MAX_OID_SIZE ];
  69.     long totalLength;
  70.     const int oidLength = sizeofOID( oid );
  71.     int readDataLength, bufferLength;
  72.  
  73.     /* Perform a quick sanity check */
  74.     if( oidLength > MAX_OID_SIZE )
  75.         return( CRYPT_ERROR );
  76.  
  77.     /* Read the identifier and length fields */
  78.     if( readIdent )
  79.         {
  80.         if( readTag( stream ) != BER_SEQUENCE )
  81.             return( CRYPT_BADDATA );
  82.         }
  83.     readDataLength = readLength( stream, &totalLength ) + 1;
  84.  
  85.     /* Read the raw object identifier data and compare it to the expected
  86.        OID */
  87.     readDataLength += readRawObject( stream, buffer, &bufferLength,
  88.                                      MAX_OID_SIZE, BER_OBJECT_IDENTIFIER );
  89.     if( bufferLength < oidLength )
  90.         return( CRYPT_BADDATA );
  91.     totalLength -= bufferLength + 1;
  92.     if( memcmp( buffer, oid, oidLength ) )
  93.         {
  94.         /* It's not what we want, skip any associated attribute data */
  95.         if( totalLength )
  96.             readDataLength += readUniversal( stream );
  97.         *remainingData = 0;
  98.         return( 0 );
  99.         }
  100.  
  101.     /* Remember the length of any optional attribute fields */
  102.     *remainingData = ( int ) totalLength;
  103.  
  104.     return( readDataLength );
  105.     }
  106.  
  107. /* Check and OID and skip any associated attribute data */
  108.  
  109. static int checkReadOID( STREAM *stream, BYTE *oid )
  110.     {
  111.     int remainingData, status;
  112.  
  113.     status = checkOID( stream, oid, &remainingData );
  114.     if( !cryptStatusError( status ) && remainingData > 0 )
  115.         readUniversal( stream );
  116.  
  117.     return( status );
  118.     }
  119.  
  120. /****************************************************************************
  121. *                                                                            *
  122. *                                Utility Functions                            *
  123. *                                                                            *
  124. ****************************************************************************/
  125.  
  126. /* Find a Common Name (CN) in a Name record */
  127.  
  128. static int readName( STREAM *stream, char *commonName )
  129.     {
  130.     int readDataLength;
  131.     long totalLength;
  132.  
  133.     /* Read the identifier field */
  134.     if( readTag( stream ) != BER_SEQUENCE )
  135.         return( CRYPT_BADDATA );
  136.     readDataLength = readLength( stream, &totalLength ) + 1;
  137.     readDataLength += ( int ) totalLength;
  138.  
  139.     /* Walk through the SEQUENCE OF RelativeDistinguishedNames looking
  140.        for the common name */
  141.     while( totalLength > 0 )
  142.         {
  143.         long setLength;
  144.  
  145.         /* Read the identifier field */
  146.         if( readTag( stream ) != BER_SET )
  147.             return( CRYPT_BADDATA );
  148.         totalLength -= readLength( stream, &setLength ) + 1;
  149.         totalLength -= setLength;
  150.  
  151.         /* Walk through the SET OF AttributeValueAssertions looking for the
  152.            first common name */
  153.         while( setLength > 0 )
  154.             {
  155.             int remainingData, status;
  156.  
  157.             /* Check for a commonName */
  158.             status = checkOID( stream, OID_COMMONNAME, &remainingData );
  159.             if( cryptStatusError( status ) )
  160.                 return( status );
  161.             if( status )
  162.                 {
  163.                 int commonNameLength;
  164.  
  165.                 /* Read in the common name */
  166.                 if( readTag( stream ) != BER_STRING_PRINTABLE )
  167.                     return( CRYPT_BADDATA );
  168.                 status += readStaticOctetStringData( stream, ( BYTE * )
  169.                                 commonName, &commonNameLength,
  170.                                 MAX_COMMONNAME_LENGTH ) + 1;
  171.                 commonName[ commonNameLength ] = '\0';
  172.  
  173.                 /* Subtract the number of bytes read from the set length */
  174.                 setLength -= status;
  175.                 }
  176.             else
  177.                 /* We've read the OID/attribute sequence as part of
  178.                    checkOID(), subtract it from the set length */
  179.                 setLength -= remainingData;
  180.             }
  181.         if( setLength < 0 )
  182.             return( CRYPT_BADDATA );
  183.         }
  184.     if( totalLength < 0 )
  185.         return( CRYPT_BADDATA );
  186.  
  187.     return( readDataLength );
  188.     }
  189.  
  190. /* Get the subtype of the key file */
  191.  
  192. KEYSET_SUBTYPE x509GetKeysetType( FILE *filePtr )
  193.     {
  194.     STREAM stream;
  195.     int status = KEYSET_SUBTYPE_ERROR;
  196.  
  197.     /* Connect the file to an I/O stream */
  198.     sFileConnect( &stream, filePtr );
  199.  
  200.     /* Make sure it's some sort of ASN.1-encapsulated object */
  201.     if( readTag( &stream ) == BER_SEQUENCE )
  202.         {
  203.         BYTE dataType[ 11 ];
  204.         int dataTypeLength;
  205.         long length;
  206.  
  207.         readLength( &stream, &length );
  208.  
  209.         /* Check for a SEQUENCE identifier field */
  210.         if( readTag( &stream ) == BER_SEQUENCE )
  211.             status = KEYSET_SUBTYPE_PUBLIC;
  212.         else
  213.             {
  214.             sungetc( &stream );
  215.  
  216.             /* Check for a Netscape private key file */
  217.             if( !cryptStatusError( readStaticOctetString( &stream, dataType,
  218.                                                 &dataTypeLength, 11 ) ) && \
  219.                 dataTypeLength == 11 && !memcmp( dataType, "private-key", 11 ) )
  220.                 status = KEYSET_SUBTYPE_PRIVATE;
  221.             }
  222.         }
  223.  
  224.     /* Clean up */
  225.     sFileSeek( &stream, 0L );    /* Move back to start of file */
  226.     sFileDisconnect( &stream );
  227.  
  228.     return( status );
  229.     }
  230.  
  231. /****************************************************************************
  232. *                                                                            *
  233. *                            Read an X.509/SET Public Key                    *
  234. *                                                                            *
  235. ****************************************************************************/
  236.  
  237. /* Read an X.509/SET key */
  238.  
  239. static int readX509key( STREAM *stream, PKC_INFO *pkcInfo,
  240.                         const GETKEY_INFO *getkeyInfo, char *name )
  241.     {
  242.     long length, integer;
  243.     time_t time;
  244.     int status;
  245.  
  246.     /* Read the identifier field */
  247.     if( readTag( stream ) != BER_SEQUENCE )
  248.         return( CRYPT_BADDATA );
  249.     readLength( stream, &length );
  250.  
  251.     /* Read the version number and certificate serial number */
  252.     if( checkReadCtag( stream, CTAG_XC_VERSION, TRUE ) )
  253.         {
  254.         readLength( stream, &length );
  255.         readShortInteger( stream, &integer );
  256.         if( integer < MIN_X509_VERSION || integer > MAX_X509_VERSION )
  257.             return( CRYPT_BADDATA );
  258.         }
  259.     if( readTag( stream ) != BER_INTEGER )
  260.         return( CRYPT_BADDATA );
  261.     readUniversalData( stream );
  262.  
  263.     /* Read the signature algorithm type */
  264.     status = checkReadOID( stream, OID_PKCS1 );
  265.     if( status <= 0 )
  266.         return( CRYPT_BADDATA );
  267.  
  268.     /* Read the certificate issuer name */
  269.     if( readTag( stream ) != BER_SEQUENCE )
  270.         return( CRYPT_BADDATA );
  271.     readUniversalData( stream );
  272.  
  273.     /* Read the certificate validity period */
  274.     if( readTag( stream ) != BER_SEQUENCE )
  275.         return( CRYPT_BADDATA );
  276.     readLength( stream, &length );
  277.     readUTCTime( stream, &time );
  278.     readUTCTime( stream, &time );
  279.  
  280.     /* Read the subject name if we're looking for a match by name, otherwise
  281.        skip this field */
  282.     if( !getkeyInfo->isKeyID )
  283.         {
  284.         readName( stream, name );
  285.         if( !matchSubstring( getkeyInfo->keyID, name ) )
  286.             return( CRYPT_KEYSET_NOTFOUND );
  287.         }
  288.     else
  289.         {
  290.         if( readTag( stream ) != BER_SEQUENCE )
  291.             return( CRYPT_BADDATA );
  292.         readUniversalData( stream );
  293.         }
  294.  
  295.     /* Read the SubjectPublicKeyInfo field */
  296.     status = readPublicKey( stream, pkcInfo );
  297.     if( !cryptStatusError( status ) )
  298.         {
  299.         BYTE blem[ CRYPT_MAX_KEYIDSIZE ];
  300.         int blemsize;
  301.  
  302.         status = generateKeyID( CRYPT_ALGO_RSA, blem, &blemsize,
  303.                                 pkcInfo->keyInfo );
  304.         }
  305.  
  306.     return( status );
  307.     }
  308.  
  309. /* Read an X.509/SET public key certificate */
  310.  
  311. static int readX509certificate( STREAM *stream, PKC_INFO *pkcInfo,
  312.                                 const GETKEY_INFO *getkeyInfo, char *name )
  313.     {
  314.     long length;
  315.     int status;
  316.  
  317.     /* Clear the return data in case we don't get anything useful */
  318.     memset( pkcInfo, 0, sizeof( PKC_INFO ) );
  319.  
  320.     /* Read the identifier field */
  321.     if( readTag( stream ) != BER_SEQUENCE )
  322.         {
  323.         sSetError( stream, STREAM_BADDATA );
  324.         return( CRYPT_BADDATA );
  325.         }
  326.     readLength( stream, &length );
  327.  
  328.     status = readX509key( stream, pkcInfo, getkeyInfo, name );
  329.     return( status );
  330.     }
  331.  
  332. /****************************************************************************
  333. *                                                                            *
  334. *                            Read an X.509/SET Private Key                    *
  335. *                                                                            *
  336. ****************************************************************************/
  337.  
  338. /* Read a PKCS #8 private key */
  339.  
  340. static int readPKCS8PrivateKey( BYTE *buffer, int bufferLength,
  341.                                 PKC_INFO *pkcInfo )
  342.     {
  343.     STREAM stream;
  344.     long integer, length;
  345.     int status = CRYPT_WRONGKEY;
  346.  
  347.     /* Connect the memory buffer to an I/O stream */
  348.     sMemConnect( &stream, buffer, bufferLength );
  349.  
  350.     /* Read the start of the private-key encapsulation as a check that the
  351.        correct decryption key was used.  We check that we've got a SEQUENCE,
  352.        that the size of the object is > 128 bytes (which is about the
  353.        minimum a 256-bit key can be encoded in, and also catches any cases
  354.        of the BER short length encoding), that the size of the object is
  355.        < 8192 bytes (which is a suspiciously large key of about 8K+ bits),
  356.        and that the version number is 0 */
  357.     if( readTag( &stream ) != BER_SEQUENCE )
  358.         {
  359.         sMemClose( &stream );
  360.         return( CRYPT_WRONGKEY );
  361.         }
  362.     readLength( &stream, &length );
  363.     readShortInteger( &stream, &integer );
  364.     if( length < 128 || length > 8192 || integer )
  365.         {
  366.         sMemClose( &stream );
  367.         return( CRYPT_WRONGKEY );
  368.         }
  369.     sMemDisconnect( &stream );
  370.  
  371.     /* Now that we're reasonably sure we've used the correct decryption key,
  372.        reconnect the stream and read the private key fields */
  373.     sMemConnect( &stream, buffer, bufferLength );
  374. /*    status = readRSAcomponents( &stream, pkcInfo, FALSE ); */
  375.     if( pkcInfo );    /* Get rid of compiler warning */
  376.     status = CRYPT_ERROR;
  377.     if( !cryptStatusError( status ) )
  378.         status = CRYPT_OK;    /* readXXX() functions return a byte count */
  379.  
  380.     /* Clean up */
  381.     sMemClose( &stream );
  382.     return( status );
  383.     }
  384.  
  385. /* Read a Netscape private key, which contains Netscapes encapsulation of the
  386.    PKCS #8 RSA private key fields.  The format is:
  387.  
  388.     SEQUENCE {
  389.         OCTET STRING 'private-key',
  390.         SEQUENCE {
  391.             SEQUENCE {
  392.                 OBJECT IDENTIFIER '1 2 840 113549 3 4' (rc4),
  393.                 NULL
  394.                 }
  395.             OCTET STRING encrypted-private-key
  396.             }
  397.         }
  398.  
  399.     The OCTET STRING decrypts to a standard PKCS #8 private key object */
  400.  
  401. static int readNetscapeKey( STREAM *stream, PKC_INFO *pkcInfo,
  402.                             const GETKEY_INFO *getkeyInfo )
  403.     {
  404.     CRYPT_CONTEXT cryptContext;
  405.     BYTE *buffer, hashResult[ CRYPT_MAX_HASHSIZE ], dataType[ 11 ];
  406.     int hashInfoSize, hashInputSize, hashOutputSize;
  407.     HASHFUNCTION hashFunction;
  408.     long length;
  409.     int dataTypeLength, status;
  410.  
  411.     /* Clear the return data in case we don't get anything useful */
  412.     memset( pkcInfo, 0, sizeof( PKC_INFO ) );
  413.  
  414.     /* Read the identifier field */
  415.     if( readTag( stream ) != BER_SEQUENCE )
  416.         return( CRYPT_BADDATA );
  417.     readLength( stream, &length );
  418.  
  419.     /* Read the data type field */
  420.     if( cryptStatusError( readStaticOctetString( stream, dataType,
  421.                                                  &dataTypeLength, 11 ) ) || \
  422.         dataTypeLength != 11 || memcmp( dataType, "private-key", 11 ) )
  423.         return( CRYPT_BADDATA );
  424.  
  425.     /* Read the inner SEQUENCE field */
  426.     if( readTag( stream ) != BER_SEQUENCE )
  427.         return( CRYPT_BADDATA );
  428.     readLength( stream, &length );
  429.  
  430.     /* Read the encryption algorithm type */
  431.     status = checkReadOID( stream, OID_RC4 );
  432.     if( status <= 0 )
  433.         return( CRYPT_BADDATA );
  434.  
  435.     /* Read the OCTET STRING containing the encrypted RSA key */
  436.     if( readTag( stream ) != BER_OCTETSTRING )
  437.         return( CRYPT_BADDATA );
  438.     readLength( stream, &length );
  439.     if( length > 8192 )
  440.         return( CRYPT_BADDATA );
  441.  
  442.     /* Read the encrypted data into an in-memory buffer */
  443.     if( ( buffer = ( BYTE * ) malloc( ( size_t ) length ) ) == NULL )
  444.         return( CRYPT_NOMEM );
  445.     sread( stream, buffer, ( int ) length );
  446.     if( ( status = sGetStatus( stream ) ) == STREAM_EMPTY ||
  447.         status == STREAM_READ )
  448.         {
  449.         zeroise( buffer, ( int ) length );
  450.         free( buffer );
  451.         return( CRYPT_BADDATA );
  452.         }
  453.  
  454.     /* Hash the passphrase with MD5 */
  455.     if( !getHashParameters( CRYPT_ALGO_MD5, &hashFunction, &hashInputSize,
  456.                             &hashOutputSize, &hashInfoSize ) )
  457.         {
  458.         zeroise( buffer, ( int ) length );
  459.         free( buffer );
  460.         return( CRYPT_ERROR );    /* API error, should never occur */
  461.         }
  462.     hashFunction( NULL, hashResult, getkeyInfo->password,
  463.                   getkeyInfo->passwordSize, HASH_ALL );
  464.  
  465.     /* Load the hashed passphrase into an encryption context */
  466.     status = cryptCreateContext( &cryptContext, CRYPT_ALGO_RC4,
  467.                                  CRYPT_MODE_STREAM );
  468.     if( !cryptStatusError( status ) )
  469.         status = cryptLoadContext( cryptContext, hashResult, hashOutputSize );
  470.     zeroise( hashResult, hashOutputSize );
  471.     if( cryptStatusError( status ) )
  472.         {
  473.         zeroise( buffer, ( int ) length );
  474.         free( buffer );
  475.         return( status );
  476.         }
  477.  
  478.     /* Decrypt the private key components */
  479.     cryptDecrypt( cryptContext, buffer, ( int ) length );
  480.     cryptDestroyContext( cryptContext );
  481.  
  482.     /* Read the private key fields */
  483.     status = readPKCS8PrivateKey( buffer, ( int ) length, pkcInfo );
  484.  
  485.     /* Clean up (the buffer has already been wiped in readPKCS8PrivateKey() */
  486.     free( buffer );
  487.     return( status );
  488.     }
  489.  
  490. /* Get an X.509 key */
  491.  
  492. int x509GetKey( FILE *filePtr, CRYPT_CONTEXT *cryptContext,
  493.                 const GETKEY_INFO *getkeyInfo )
  494.     {
  495.     PKC_INFO pkcInfo;
  496.     STREAM stream;
  497.     char name[ MAX_COMMONNAME_LENGTH + 1 ];
  498.     int status;
  499.  
  500.     /* Read the public or private key */
  501.     sFileConnect( &stream, filePtr );
  502.     if( getkeyInfo->isPublicKey )
  503.         status = readX509certificate( &stream, &pkcInfo, getkeyInfo, name );
  504.     else
  505.         status = readNetscapeKey( &stream, &pkcInfo, getkeyInfo );
  506.  
  507.     /* Create the encryption context and load the key into it */
  508.     if( cryptStatusOK( status ) )
  509.         {
  510.         status = cryptCreateContext( cryptContext, pkcInfo.algorithm,
  511.                                      CRYPT_MODE_PKC );
  512.         if( cryptStatusOK( status ) )
  513.             status = cryptLoadContext( *cryptContext, pkcInfo.keyInfo,
  514.                                        CRYPT_UNUSED );
  515.         }
  516.     sFileDisconnect( &stream );
  517.  
  518.     /* Store the name in the encryption context as well in case we want to
  519.        later export the key to another type of keyset */
  520.     if( getkeyInfo->isPublicKey && cryptStatusOK( status ) )
  521.         {
  522.         CRYPT_INFO *cryptInfoPtr = CONTEXT_TO_INFO( *cryptContext );
  523.  
  524.         if( ( cryptInfoPtr->userID = ( char * ) \
  525.                     malloc( strlen( name + 1 ) ) ) == NULL )
  526.             {
  527.             cryptDestroyContext( *cryptContext );
  528.             return( CRYPT_NOMEM );
  529.             }
  530.         strcpy( cryptInfoPtr->userID, name );
  531.         }
  532.  
  533.     /* Clean up */
  534.     if( pkcInfo.keyInfo != NULL )
  535.         cleanFree( &pkcInfo.keyInfo, pkcInfo.keyInfoSize );
  536.     zeroise( &pkcInfo, sizeof( PKC_INFO ) );
  537.     return( status );
  538.     }
  539.