ssl.c
来自「提供了很多种加密算法和CA认证及相关服务如CMP、OCSP等的开发」· C语言 代码 · 共 1,778 行 · 第 1/5 页
C
1,778 行
int status;
status = krnlSendMessage( handshakeInfo->clientMD5context,
RESOURCE_IMESSAGE_CTX_HASH,
( void * ) data, dataLength );
if( cryptStatusOK( status ) )
status = krnlSendMessage( handshakeInfo->clientSHA1context,
RESOURCE_IMESSAGE_CTX_HASH,
( void * ) data, dataLength );
if( cryptStatusOK( status ) )
status = krnlSendMessage( handshakeInfo->serverMD5context,
RESOURCE_IMESSAGE_CTX_HASH,
( void * ) data, dataLength );
if( cryptStatusOK( status ) )
status = krnlSendMessage( handshakeInfo->serverSHA1context,
RESOURCE_IMESSAGE_CTX_HASH,
( void * ) data, dataLength );
return( status );
}
/* Write an SSL cert chain:
byte ID = 0x0B
uint24 len
uint24 certListLen
uint24 certLen | 1...n certs ordered
byte[] cert | leaf -> root */
static int writeSSLCertChain( SESSION_INFO *sessionInfoPtr, BYTE *buffer )
{
BYTE *bufPtr = buffer, *lengthPtr;
int length = 0, status;
/* Write the packet header and leave room for the packet length and
cert list length */
*bufPtr++ = SSL_HAND_SERVER_CERT;
lengthPtr = bufPtr;
bufPtr += LENGTH_SIZE + LENGTH_SIZE; /* len + certListLen */
/* Lock the cert chain for our exclusive use and select the leaf cert,
export each cert in turn until we reach the root, and unlock it to
allow others access */
krnlSendMessage( sessionInfoPtr->privateKey,
RESOURCE_IMESSAGE_SETATTRIBUTE,
MESSAGE_VALUE_CURSORFIRST,
CRYPT_CERTINFO_CURRENT_CERTIFICATE );
status = krnlSendNotifier( sessionInfoPtr->privateKey,
RESOURCE_IMESSAGE_LOCK );
if( cryptStatusError( status ) )
return( status );
do
{
RESOURCE_DATA msgData;
setResourceData( &msgData, bufPtr + LENGTH_SIZE,
sessionInfoPtr->sendBufSize - \
( bufPtr + LENGTH_SIZE - sessionInfoPtr->sendBuffer ) );
status = krnlSendMessage( sessionInfoPtr->privateKey,
RESOURCE_IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_ENC_CERT );
*bufPtr++ = 0;
mputBWord( bufPtr, msgData.length );
bufPtr += msgData.length;
length += msgData.length + LENGTH_SIZE;
}
while( cryptStatusOK( status ) && \
krnlSendMessage( sessionInfoPtr->privateKey,
RESOURCE_IMESSAGE_SETATTRIBUTE,
MESSAGE_VALUE_CURSORNEXT,
CRYPT_CERTINFO_CURRENT_CERTIFICATE ) == CRYPT_OK );
krnlSendNotifier( sessionInfoPtr->privateKey, RESOURCE_IMESSAGE_UNLOCK );
if( cryptStatusError( status ) )
return( status );
/* Go back and add the overall packet length and cert chain length at the
start of the packet */
*lengthPtr++ = 0; /* len */
mputBWord( lengthPtr, length + LENGTH_SIZE );
*lengthPtr++ = 0; /* certListLen */
mputBWord( lengthPtr, length );
return( ID_SIZE + LENGTH_SIZE + LENGTH_SIZE + length );
}
/* Read/write an SSL certificate verify message:
byte ID = 0x0F
uint24 len
byte[] signature
SSLv3/TLS use a weird signature format which dual-MACs (SSLv3) or hashes
(TLS) the handshake messages exchanged to date, then signs them using raw,
non-PKCS #1 RSA (that is, it uses the private key to encrypt the
concatenated SHA-1 and MD5 MAC or hash of the handshake messages), unless
we're using DSA in which case it drops the MD5 MAC/hash and uses only the
SHA-1 one. This is an incredible pain to support because it requires
further hashing/MAC'ing of messages and the use of weird nonstandard data
formats and signature mechanisms which aren't normally supported by
anything. For example if the signing is to be done via a smart card then
we can't use the standard PKCS #1 sig, we can't even use raw RSA and
kludge the format together ourselves because some PKCS #11 implementations
don't support the _X509 mechanism, what we have to do is tunnel the
nonstandard sig.format info down through several cryptlib layers and then
hope that the PKCS #11 implementation we're using (a) supports this format
and (b) gets it right. Another problem (which only occurs for SSLv3) is
that the MAC requires the use of the master secret, which isn't available
for several hundred more lines of code, so we have to delay producing any
more data packets until the master secret is available, which severely
screws up the handshake processing flow.
The chances of all of this working correctly are fairly low, and in any
case there's no advantage to the weird mechanism and format used in
SSL/TLS, all we actually need to do is sign the client and server nonces
to ensure signature freshness. Because of this what we actually do is
just this, after which we create a standard PKCS #1 signature via the
normal cryptlib mechanisms, which guarantees it'll work with native
cryptlib as well as any crypto hardware implementation. Since client
certs are hardly ever used and when they are it's in a closed environment,
it's extremely unlikely that anyone will ever notice. There'll be far
more problems in trying to use the nonstandard SSL/TLS signature
mechanism than there are with using a standard (but not-in-the-spec)
one */
static int processCertVerify( const SESSION_INFO *sessionInfoPtr,
const SSL_HANDSHAKE_INFO *handshakeInfo,
void *signature, const int signatureLength,
const BOOLEAN createVerifyMessage )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
BYTE nonceBuffer[ 64 + SSL_NONCE_SIZE + SSL_NONCE_SIZE ];
int length, status;
/* Hash the client and server nonces */
setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_SHA );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
RESOURCE_IMESSAGE_DEV_CREATEOBJECT,
&createInfo, OBJECT_TYPE_CONTEXT );
if( cryptStatusError( status ) )
return( status );
memcpy( nonceBuffer, "certificate verify", 18 );
memcpy( nonceBuffer + 18, handshakeInfo->clientNonce, SSL_NONCE_SIZE );
memcpy( nonceBuffer + 18 + SSL_NONCE_SIZE, handshakeInfo->serverNonce,
SSL_NONCE_SIZE );
krnlSendMessage( createInfo.cryptHandle, RESOURCE_IMESSAGE_CTX_HASH,
nonceBuffer, 18 + SSL_NONCE_SIZE + SSL_NONCE_SIZE );
krnlSendMessage( createInfo.cryptHandle, RESOURCE_IMESSAGE_CTX_HASH,
nonceBuffer, 0 );
/* Create or verify the signature as appropriate */
if( createVerifyMessage )
status = iCryptCreateSignatureEx( signature, &length,
CRYPT_FORMAT_CRYPTLIB,
sessionInfoPtr->privateKey,
createInfo.cryptHandle,
CRYPT_UNUSED, CRYPT_UNUSED );
else
status = iCryptCheckSignatureEx( signature, signatureLength,
sessionInfoPtr->iKeyexAuthContext,
createInfo.cryptHandle, NULL );
krnlSendNotifier( createInfo.cryptHandle, RESOURCE_IMESSAGE_DECREFCOUNT );
return( ( cryptStatusOK( status ) && createVerifyMessage ) ? \
length : status );
}
/* Wrap a handshake packet, taking as input a data packet with a 5-byte gap
at the start for the header and wraps it up as appropriate in the SSL/TLS
packet encapsulation
byte type = 22 (handshake)
byte[2] version = { 0x03, 0x0n }
uint16 len */
static void wrapHandshakePacket( void *data, const int length,
const int protocolVersion )
{
BYTE *dataPtr = data;
/* Add the length and type at the start */
*dataPtr++ = SSL_MSG_HANDSHAKE;
*dataPtr++ = SSL_MAJOR_VERSION;
*dataPtr++ = ( protocolVersion == CRYPT_PROTOCOLVERSION_SSL ) ? \
SSL_MINOR_VERSION : TLS_MINOR_VERSION;
mputBWord( dataPtr, length );
}
/* Send a close alert, with appropriate protection if necessary */
static void sendCloseAlert( SESSION_INFO *sessionInfoPtr )
{
const BYTE data[] = { SSL_ALERTLEVEL_WARNING, SSL_ALERT_CLOSE_NOTIFY };
BYTE buffer[ 256 ];
int length;
/* If we haven't entered the secure state yet, just send a plain alert
and exit */
if( !( sessionInfoPtr->flags & SESSION_ISSECURE ) )
{
swrite( &sessionInfoPtr->stream,
( sessionInfoPtr->version == CRYPT_PROTOCOLVERSION_SSL ) ? \
closeAlertSSLTemplate : closeAlertTLSTemplate,
CLOSEALERT_TEMPLATE_SIZE );
return;
}
/* Send a closure alert to tell the other side we're going away */
memcpy( buffer + SSL_HEADER_SIZE, data, 2 );
if( sessionInfoPtr->version == CRYPT_PROTOCOLVERSION_SSL )
length = macDataSSL( sessionInfoPtr, buffer + SSL_HEADER_SIZE, 2,
SSL_MSG_ALERT, FALSE );
else
length = macDataTLS( sessionInfoPtr, buffer + SSL_HEADER_SIZE, 2,
SSL_MSG_ALERT, FALSE );
if( !cryptStatusError( length ) )
length = encryptData( sessionInfoPtr, buffer + SSL_HEADER_SIZE,
length );
if( !cryptStatusError( length ) )
{
BYTE *bufPtr = buffer;
*bufPtr++ = SSL_MSG_ALERT;
*bufPtr++ = SSL_MAJOR_VERSION;
*bufPtr++ = ( sessionInfoPtr->version == CRYPT_PROTOCOLVERSION_SSL ) ? \
SSL_MINOR_VERSION : TLS_MINOR_VERSION;
mputBWord( bufPtr, length );
swrite( &sessionInfoPtr->stream, buffer, SSL_HEADER_SIZE + length );
}
}
/* Send a handshake failure alert. This doesn't need any protection since
it's always sent during the handshake phase */
static void sendHandshakeFailAlert( SESSION_INFO *sessionInfoPtr )
{
swrite( &sessionInfoPtr->stream,
( sessionInfoPtr->version == CRYPT_PROTOCOLVERSION_SSL ) ? \
handshakeFailAlertSSLTemplate : handshakeFailAlertTLSTemplate,
HANDSHAKEFAILALERT_TEMPLATE_SIZE );
}
/* Process an alert packet. MS products tend to just drop the connection
rather than sending an alert when they encounter a problem, so when
communicating with MS software the only error indication will be a
"Connection closed by remote host" rather than an SSL-level error message */
static int processAlert( SESSION_INFO *sessionInfoPtr, const int length )
{
const static struct {
const int type;
const char *message;
const int cryptlibError;
} alertInfo[] = {
{ SSL_ALERT_CLOSE_NOTIFY, "Close notify", CRYPT_ERROR_COMPLETE },
{ SSL_ALERT_UNEXPECTED_MESSAGE, "Unexpected message", CRYPT_ERROR_FAILED },
{ SSL_ALERT_BAD_RECORD_MAC, "Bad record MAC", CRYPT_ERROR_SIGNATURE },
{ TLS_ALERT_DECRYPTION_FAILED, "Decryption failed", CRYPT_ERROR_WRONGKEY },
{ TLS_ALERT_RECORD_OVERFLOW, "Record overflow", CRYPT_ERROR_OVERFLOW },
{ SSL_ALERT_DECOMPRESSION_FAILURE, "Decompression failure", CRYPT_ERROR_FAILED },
{ SSL_ALERT_HANDSHAKE_FAILURE, "Handshake failure", CRYPT_ERROR_FAILED },
{ SSL_ALERT_NO_CERTIFICATE, "No certificate", CRYPT_ERROR_PERMISSION },
{ SSL_ALERT_BAD_CERTIFICATE, "Bad certificate", CRYPT_ERROR_INVALID },
{ SSL_ALERT_UNSUPPORTED_CERTIFICATE, "Unsupported certificate", CRYPT_ERROR_INVALID },
{ SSL_ALERT_CERTIFICATE_REVOKED, "Certificate revoked", CRYPT_ERROR_INVALID },
{ SSL_ALERT_CERTIFICATE_EXPIRED, "Certificate expired", CRYPT_ERROR_INVALID },
{ SSL_ALERT_CERTIFICATE_UNKNOWN, "Certificate unknown", CRYPT_ERROR_INVALID },
{ SSL_ALERT_ILLEGAL_PARAMETER, "Illegal parameter", CRYPT_ERROR_FAILED },
{ TLS_ALERT_UNKNOWN_CA, "Unknown CA", CRYPT_ERROR_INVALID },
{ TLS_ALERT_ACCESS_DENIED, "Access denied", CRYPT_ERROR_PERMISSION },
{ TLS_ALERT_DECODE_ERROR, "Decode error", CRYPT_ERROR_FAILED },
{ TLS_ALERT_DECRYPT_ERROR, "Decrypt error", CRYPT_ERROR_WRONGKEY },
{ TLS_ALERT_EXPORT_RESTRICTION, "Export restriction", CRYPT_ERROR_FAILED },
{ TLS_ALERT_PROTOCOL_VERSION, "Protocol version", CRYPT_ERROR_NOTAVAIL },
{ TLS_ALERT_INSUFFICIENT_SECURITY, "Insufficient security", CRYPT_ERROR_NOSECURE },
{ TLS_ALERT_INTERNAL_ERROR, "Internal error", CRYPT_ERROR_FAILED },
{ TLS_ALERT_USER_CANCELLED, "User cancelled", CRYPT_ERROR_FAILED },
{ TLS_ALERT_NO_RENEGOTIATION, "No renegotiation", CRYPT_ERROR_FAILED },
{ CRYPT_ERROR, NULL }
};
BYTE buffer[ 128 ];
int type, i, status;
assert( length < 128 );
/* Get the alert packet and tell the server we're going away */
status = sread( &sessionInfoPtr->stream, buffer, length );
if( cryptStatusError( status ) )
{
sNetGetErrorInfo( &sessionInfoPtr->stream,
sessionInfoPtr->errorMessage,
&sessionInfoPtr->errorCode );
return( status );
}
if( sessionInfoPtr->flags & SESSION_ISSECURE && length > ALERTINFO_SIZE )
{
/* We only try and decrypt if the alert info is big enough to be
encrypted, this situation can occur if there's an error moving
from the unencrypted to the encrypted state */
status = decryptData( sessionInfoPtr, buffer, length );
if( cryptStatusError( status ) )
return( status );
}
sendCloseAlert( sessionInfoPtr );
/* Process the alert info */
if( buffer[ 0 ] != SSL_ALERTLEVEL_WARNING && \
buffer[ 0 ] != SSL_ALERTLEVEL_FATAL )
return( CRYPT_ERROR_BADDATA );
sessionInfoPtr->errorCode = type = buffer[ 1 ];
for( i = 0; alertInfo[ i ].type != CRYPT_ERROR && \
alertInfo[ i ].type != type; i++ );
if( alertInfo[ i ].type == CRYPT_ERROR )
{
sprintf( sessionInfoPtr->errorMessage,
"Received unknown alert message type %d", type );
return( CRYPT_ERROR_BADDATA );
}
strcpy( sessionInfoPtr->errorMessage,
( sessionInfoPtr->version == CRYPT_PROTOCOLVERSION_SSL ) ? \
"Received SSL alert message: " : "Received TLS alert message: " );
strcat( sessionInfoPtr->errorMessage, alertInfo[ i ].message );
return( alertInfo[ i ].cryptlibError );
}
/* Read an SSL packet */
static int readPacketHeader( SESSION_INFO *sessionInfoPtr, BYTE *buffer )
{
int status;
/* Read the SSL packet header data */
status = sread( &sessionInfoPtr->stream, buffer,
SSL_HEADER_SIZE );
if( status <= 0 )
{
if( cryptStatusError( status ) )
sNetGetErrorInfo( &sessionInfoPtr->stream,
sessionInfoPtr->errorMessage,
&sessionInfoPtr->errorCode );
return( status );
}
/* Check for an SSL alert message */
if( buffer[ 0 ] == SSL_MSG_ALERT )
{
BYTE *bufPtr = buffer + ID_SIZE;
int length, ch;
if( *bufPtr++ != SSL_MAJOR_VERSION )
return( CRYPT_ERROR_BADDATA );
ch = *bufPtr++;
if( ch != SSL_MINOR_VERSION && ch != TLS_MINOR_VERSION )
return( CRYPT_ERROR_BADDATA );
length = mgetBWord( bufPtr );
if( sessionInfoPtr->flags & SESSION_ISSECURE )
{
if( length < ALERTINFO_SIZE || length > 32 )
return( CRYPT_ERROR_BADDATA );
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?