📄 asn1keys.c
字号:
/****************************************************************************
* *
* ASN.1 Key Encode/Decode Routines *
* Copyright Peter Gutmann 1992-2002 *
* *
****************************************************************************/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL ) || defined( INC_CHILD )
#include "../cryptctx.h"
#include "asn1.h"
#include "asn1objs.h"
#include "asn1oid.h"
#else
#include "cryptctx.h"
#include "keymgmt/asn1.h"
#include "keymgmt/asn1objs.h"
#include "keymgmt/asn1oid.h"
#endif /* Compiler-specific includes */
/****************************************************************************
* *
* Key Component Read/Write Routines *
* *
****************************************************************************/
/* The DLP algorithms split the key components over the information in the
AlgorithmIdentifier and the actual public/private key components, with the
(p, q, g) set classed as domain parameters and included in the
AlgorithmIdentifier and y being the actual key.
params = SEQ {
p INTEGER,
q INTEGER, -- g for DSA
g INTEGER, -- q for DSA
j INTEGER OPTIONAL, -- X9.42 only
validationParams [...] -- X9.42 only
}
key = y INTEGER -- g^x mod p
For peculiar historical reasons X9.42 reverses the second two parameters
from FIPS 186 (so it uses p, g, q instead of p, q, g), so when we read/
write the parameter information we have to switch the order in which we
read the values if the algorithm isn't DSA */
#define hasReversedParams( cryptAlgo ) \
( ( cryptAlgo ) == CRYPT_ALGO_DH || \
( cryptAlgo ) == CRYPT_ALGO_ELGAMAL )
/* The format in which to write the key data */
typedef enum { KEYFORMAT_PUBLIC, KEYFORMAT_PRIVATE, KEYFORMAT_CERT } KEYFORMAT_TYPE;
/* When we're writing bignums we can't use the standard ASN.1 sizeof()
routines, the following macro works out the encoded size */
#define sizeofEncodedBignum( value ) \
( ( int ) sizeofObject( bitsToBytes( BN_num_bits( value ) ) + \
BN_high_bit( value ) ) )
/* Read/write a bignum */
static int writeBignum( STREAM *stream, const BIGNUM *value, const int tag )
{
BYTE buffer[ CRYPT_MAX_PKCSIZE ];
int length, status;
length = BN_bn2bin( ( BIGNUM * ) value, buffer );
status = writeInteger( stream, buffer, length, tag );
zeroise( buffer, CRYPT_MAX_PKCSIZE );
return( status );
}
static int readBignum( STREAM *stream, BIGNUM *value, const int tag )
{
BYTE buffer[ CRYPT_MAX_PKCSIZE ];
int length, status;
/* Read the value into a fixed buffer */
status = readIntegerTag( stream, buffer, &length, CRYPT_MAX_PKCSIZE,
( tag == DEFAULT_TAG ) ? \
DEFAULT_TAG : MAKE_CTAG_PRIMITIVE( tag ) );
if( !cryptStatusError( status ) )
{
BN_bin2bn( buffer, length, value );
zeroise( buffer, CRYPT_MAX_PKCSIZE );
}
return( status );
}
/* Read and write DLP key info with handling for reversed parameters */
static int readDLPparameters( STREAM *stream, PKC_INFO *dlpKey,
const BOOLEAN hasReversedParameters )
{
/* Read the header and key parameters */
readSequence( stream, NULL );
readBignum( stream, dlpKey->dlpParam_p, DEFAULT_TAG );
if( hasReversedParameters )
{
readBignum( stream, dlpKey->dlpParam_g, DEFAULT_TAG );
return( readBignum( stream, dlpKey->dlpParam_q, DEFAULT_TAG ) );
}
readBignum( stream, dlpKey->dlpParam_q, DEFAULT_TAG );
return( readBignum( stream, dlpKey->dlpParam_g, DEFAULT_TAG ) );
}
static void writeDLPparameters( STREAM *stream, const PKC_INFO *dlpKey,
const BOOLEAN hasReversedParameters )
{
/* Write the identifier and length fields */
writeSequence( stream, sizeofEncodedBignum( dlpKey->dlpParam_p ) +
sizeofEncodedBignum( dlpKey->dlpParam_q ) +
sizeofEncodedBignum( dlpKey->dlpParam_g ) );
/* Write the parameter fields */
writeBignum( stream, dlpKey->dlpParam_p, DEFAULT_TAG );
if( hasReversedParameters )
{
writeBignum( stream, dlpKey->dlpParam_g, DEFAULT_TAG );
writeBignum( stream, dlpKey->dlpParam_q, DEFAULT_TAG );
}
else
{
writeBignum( stream, dlpKey->dlpParam_q, DEFAULT_TAG );
writeBignum( stream, dlpKey->dlpParam_g, DEFAULT_TAG );
}
}
static int readDLPcomponents( STREAM *stream, PKC_INFO *dlpKey,
const KEYFORMAT_TYPE formatType )
{
/* Set up the general information fields */
dlpKey->isPublicKey = ( formatType == KEYFORMAT_PUBLIC || \
formatType == KEYFORMAT_CERT ) ? TRUE : FALSE;
/* If it's a cert there's a single INTEGER component */
if( formatType == KEYFORMAT_CERT )
return( readBignum( stream, dlpKey->dlpParam_y, DEFAULT_TAG ) );
/* Read the header and key components */
readSequence( stream, NULL );
if( formatType == KEYFORMAT_PUBLIC )
return( readBignum( stream, dlpKey->dlpParam_y, 0 ) );
return( readBignum( stream, dlpKey->dlpParam_x, 0 ) );
}
static void writeDLPcomponents( STREAM *stream, const PKC_INFO *dlpKey,
const KEYFORMAT_TYPE formatType )
{
/* When we're generating a DH key ID, only p, q, and g are initialised,
so we write a special-case zero y value. This is a somewhat ugly
side-effect of the odd way in which DH "public keys" work */
if( BN_is_zero( dlpKey->dlpParam_y ) )
{
swrite( stream, "\x02\x00", 2 );
return;
}
/* If it's a cert there's a single INTEGER component */
if( formatType == KEYFORMAT_CERT )
{
writeBignum( stream, dlpKey->dlpParam_y, DEFAULT_TAG );
return;
}
/* Write the header and key components */
writeSequence( stream, ( formatType == KEYFORMAT_PUBLIC ) ? \
sizeofEncodedBignum( dlpKey->dlpParam_y ) : \
sizeofEncodedBignum( dlpKey->dlpParam_x ) );
if( formatType == KEYFORMAT_PUBLIC )
writeBignum( stream, dlpKey->dlpParam_y, 0 );
else
writeBignum( stream, dlpKey->dlpParam_x, 0 );
}
/* Read and write RSA key info */
static int readRSAcomponents( STREAM *stream, PKC_INFO *rsaKey,
const KEYFORMAT_TYPE formatType )
{
long value;
int status;
/* Set up the general information fields */
rsaKey->isPublicKey = ( formatType == KEYFORMAT_PUBLIC || \
formatType == KEYFORMAT_CERT ) ? TRUE : FALSE;
/* Read the header and key components */
readSequence( stream, NULL );
if( formatType == KEYFORMAT_CERT )
{
readBignum( stream, rsaKey->rsaParam_n, DEFAULT_TAG );
return( readBignum( stream, rsaKey->rsaParam_e, DEFAULT_TAG ) );
}
readConstructed( stream, NULL, 0 );
if( formatType == KEYFORMAT_PUBLIC )
{
readBignum( stream, rsaKey->rsaParam_n, DEFAULT_TAG );
readBignum( stream, rsaKey->rsaParam_e, DEFAULT_TAG );
}
else
{
readBignum( stream, rsaKey->rsaParam_n, 0 );
readBignum( stream, rsaKey->rsaParam_e, 1 );
readBignum( stream, rsaKey->rsaParam_d, 2 );
readBignum( stream, rsaKey->rsaParam_p, 3 );
readBignum( stream, rsaKey->rsaParam_q, 4 );
readBignum( stream, rsaKey->rsaParam_exponent1, 5 );
readBignum( stream, rsaKey->rsaParam_exponent2, 6 );
readBignum( stream, rsaKey->rsaParam_u, 7 );
}
status = readShortInteger( stream, &value );
if( cryptStatusOK( status ) && \
value != BN_num_bits( rsaKey->rsaParam_n ) )
/* We compare the last piece of encrypted data with a known-good
value to help detect an attempt to alter u by flipping bits in
the last encrypted block (although the RSA validity check should
catch this as well) */
status = CRYPT_ERROR_BADDATA;
return( status );
}
static void writeRSAcomponents( STREAM *stream, const PKC_INFO *rsaKey,
const KEYFORMAT_TYPE formatType )
{
const int modulusLength = BN_num_bits( rsaKey->rsaParam_n );
int length;
/* Determine the size of the public and private fields */
length = sizeofEncodedBignum( rsaKey->rsaParam_n ) +
sizeofEncodedBignum( rsaKey->rsaParam_e );
if( formatType == KEYFORMAT_PRIVATE )
length += sizeofEncodedBignum( rsaKey->rsaParam_d ) +
sizeofEncodedBignum( rsaKey->rsaParam_p ) +
sizeofEncodedBignum( rsaKey->rsaParam_q ) +
sizeofEncodedBignum( rsaKey->rsaParam_exponent1 ) +
sizeofEncodedBignum( rsaKey->rsaParam_exponent2 ) +
sizeofEncodedBignum( rsaKey->rsaParam_u );
/* Write the the PKC fields */
if( formatType == KEYFORMAT_CERT )
{
writeSequence( stream, length );
writeBignum( stream, rsaKey->rsaParam_n, DEFAULT_TAG );
writeBignum( stream, rsaKey->rsaParam_e, DEFAULT_TAG );
return;
}
writeSequence( stream, ( int ) sizeofObject( length ) + \
sizeofShortInteger( modulusLength ) );
writeConstructed( stream, length, 0 );
if( formatType == KEYFORMAT_PUBLIC )
{
writeBignum( stream, rsaKey->rsaParam_n, DEFAULT_TAG );
writeBignum( stream, rsaKey->rsaParam_e, DEFAULT_TAG );
}
else
{
writeBignum( stream, rsaKey->rsaParam_n, 0 );
writeBignum( stream, rsaKey->rsaParam_e, 1 );
writeBignum( stream, rsaKey->rsaParam_d, 2 );
writeBignum( stream, rsaKey->rsaParam_p, 3 );
writeBignum( stream, rsaKey->rsaParam_q, 4 );
writeBignum( stream, rsaKey->rsaParam_exponent1, 5 );
writeBignum( stream, rsaKey->rsaParam_exponent2, 6 );
writeBignum( stream, rsaKey->rsaParam_u, 7 );
}
writeShortInteger( stream, modulusLength, DEFAULT_TAG );
}
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Generate a key ID, which is the SHA-1 hash of the SubjectPublicKeyInfo.
There are about half a dozen incompatible ways of generating X.509
keyIdentifiers, the following is conformant with the PKIX specification
("use whatever you like as long as it's unique"), but differs slightly
from one common method which hashes the SubjectPublicKey without the
BIT STRING encapsulation. The problem with this is that a number of DLP-
based algorithms use a single integer as the SubjectPublicKey, leading to
key ID clashes */
static void calculateFlatKeyID( const void *keyInfo, const int keyInfoSize,
BYTE *keyID )
{
HASHFUNCTION hashFunction;
int hashSize;
/* Hash the key info to get the key ID */
getHashParameters( CRYPT_ALGO_SHA, &hashFunction, &hashSize );
hashFunction( NULL, keyID, ( BYTE * ) keyInfo, keyInfoSize, HASH_ALL );
}
int calculateKeyID( CRYPT_INFO *cryptInfo )
{
int writePublicKey( STREAM *stream, const CRYPT_INFO *cryptInfo );
STREAM stream;
BYTE buffer[ ( CRYPT_MAX_PKCSIZE * 4 ) + 50 ];
const CRYPT_ALGO cryptAlgo = cryptInfo->capabilityInfo->cryptAlgo;
int status;
/* If it's an RSA key, we need to calculate the PGP key ID as well as the
cryptlib one */
if( cryptAlgo == CRYPT_ALGO_RSA )
{
const PKC_INFO *pkcInfo = &cryptInfo->ctxPKC;
const int length = BN_bn2bin( pkcInfo->rsaParam_n, buffer );
memcpy( cryptInfo->ctxPKC.pgpKeyID, buffer + length - PGP_KEYID_SIZE,
PGP_KEYID_SIZE );
}
/* If the public key info is present in pre-encoded form, calculate the
key ID directly from that */
if( cryptInfo->ctxPKC.publicKeyInfo != NULL )
{
STREAM stream;
int length;
calculateFlatKeyID( cryptInfo->ctxPKC.publicKeyInfo,
cryptInfo->ctxPKC.publicKeyInfoSize,
cryptInfo->ctxPKC.keyID );
if( cryptAlgo != CRYPT_ALGO_KEA )
return( CRYPT_OK );
/* If it's a KEA context, we also need to remember the start and
length of the domain parameters and key agreement public value in
the encoded key data */
sMemConnect( &stream, cryptInfo->ctxPKC.publicKeyInfo,
cryptInfo->ctxPKC.publicKeyInfoSize );
readSequence( &stream, NULL );
readSequence( &stream, NULL );
readUniversal( &stream );
readTag( &stream );
length = readShortLength( &stream );
cryptInfo->ctxPKC.domainParamPtr = sMemBufPtr( &stream );
cryptInfo->ctxPKC.domainParamSize = ( int ) length;
sSkip( &stream, length );
readTag( &stream );
length = readShortLength( &stream );
sgetc( &stream ); /* Skip extra bit count in bitfield */
cryptInfo->ctxPKC.publicValuePtr = sMemBufPtr( &stream );
cryptInfo->ctxPKC.publicValueSize = ( int ) length - 1;
assert( sGetStatus( &stream ) == CRYPT_OK );
sMemDisconnect( &stream );
return( CRYPT_OK );
}
/* Write the public key fields to a buffer and hash them to get the key
ID */
sMemOpen( &stream, buffer, ( CRYPT_MAX_PKCSIZE * 4 ) + 50 );
status = writePublicKey( &stream, cryptInfo );
calculateFlatKeyID( buffer, ( int ) stell( &stream ),
cryptInfo->ctxPKC.keyID );
sMemClose( &stream );
/* Finally, set the OpenPGP key ID to the first 64 bits of the key ID if
it hasn't been explicitly set. Since calculation of the OpenPGP ID
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -