📄 ssl_cli.c
字号:
retExt( sessionInfoPtr, status,
"Server provided a broken/invalid certificate, try again "
"with a reduced level of certificate compliance "
"checking" );
}
else
status = krnlSendMessage( createInfo.cryptHandle,
IMESSAGE_GETATTRIBUTE, &algorithm,
CRYPT_CTXINFO_ALGO );
if( cryptStatusOK( status ) )
{
setMessageData( &msgData, certFingerprint, CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( createInfo.cryptHandle,
IMESSAGE_GETATTRIBUTE_S, &msgData,
( sessionInfoPtr->keyFingerprintSize == 16 ) ? \
CRYPT_CERTINFO_FINGERPRINT_MD5 : \
CRYPT_CERTINFO_FINGERPRINT_SHA );
}
if( cryptStatusError( status ) )
return( status );
bufPtr += chainLength;
sessionInfoPtr->iKeyexCryptContext = createInfo.cryptHandle;
/* Either compare the cert fingerprint to a supplied one or save it for
the caller to examine */
if( sessionInfoPtr->keyFingerprintSize > 0 )
{
/* The caller has supplied a cert fingerprint, compare it to the
received cert's fingerprint to make sure that we're talking to
the right server */
if( sessionInfoPtr->keyFingerprintSize != msgData.length || \
memcmp( sessionInfoPtr->keyFingerprint, certFingerprint,
msgData.length ) )
retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
"Server key didn't match fingerprint" );
}
else
{
/* Remember the cert fingerprint in case the caller wants to check
it */
memcpy( sessionInfoPtr->keyFingerprint, certFingerprint,
msgData.length );
sessionInfoPtr->keyFingerprintSize = msgData.length;
}
/* Make sure that we can perform the required operation using the key
we've been given. This performs a variety of checks alongside the
obvious one, so it's a good general health check before we go any
further. If this fails, we convert the result to a wrong key error
rather than a check failure */
status = krnlSendMessage( createInfo.cryptHandle,
IMESSAGE_CHECK, NULL,
isKeyxAlgo( algorithm ) ? \
MESSAGE_CHECK_PKC_KA_IMPORT : \
MESSAGE_CHECK_PKC_ENCRYPT );
if( cryptStatusError( status ) )
retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
"Server returned a key incapable of being used for %s",
isKeyxAlgo( algorithm ) ? "key agreement" : "key transport" );
/* Process optional server cert request and server hello done:
byte ID = 0x0E
uint24 len = 0 */
if( sessionInfoPtr->receiveBufPos >= sessionInfoPtr->receiveBufEnd )
{
status = readPacketSSL( sessionInfoPtr, handshakeInfo,
SSL_MSG_HANDSHAKE );
if( cryptStatusError( status ) )
return( status );
bufPtr = sessionInfoPtr->receiveBuffer;
}
if( *bufPtr == SSL_HAND_SERVER_CERTREQUEST )
{
int certInfoLen;
/* The server wants a client cert:
byte ID = 0x0D
uint24 len
byte certTypeLen
byte[] certType
uint16 caNameListLen
uint16 caNameLen
byte[] caName
We don't really care what's in the cert request packet since the
contents are irrelevant, and in many cases servers send out
superfluous cert requests without the admins even knowning that
they're doing it. All we do here is perform a basic sanity check
and remember that we may need to submit a cert later on.
Although the spec says that at least one CA name entry must be
present, some implementations send a zero-length list, so we allow
this as well. The spec was changed in late TLS 1.1 drafts to
reflect this practice */
length = checkPacketHeader( sessionInfoPtr, &bufPtr,
SSL_HAND_SERVER_CERTREQUEST, 4,
CRYPT_UNUSED );
if( cryptStatusError( length ) )
return( length );
certInfoLen = *bufPtr++; /* certTypeLen */
if( certInfoLen < 1 || certInfoLen > length - 1 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid cert request cert type length %d",
certInfoLen );
bufPtr += certInfoLen; /* Skip cert types */
length -= 1 + certInfoLen;
certInfoLen = mgetWord( bufPtr );
if( certInfoLen < 0 || certInfoLen != length - UINT16_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid cert request CA name list length %d",
certInfoLen );
bufPtr += certInfoLen;
needClientCert = TRUE;
if( sessionInfoPtr->receiveBufPos >= sessionInfoPtr->receiveBufEnd )
{
status = readPacketSSL( sessionInfoPtr, handshakeInfo,
SSL_MSG_HANDSHAKE );
if( cryptStatusError( status ) )
return( status );
bufPtr = sessionInfoPtr->receiveBuffer;
}
}
if( sessionInfoPtr->receiveBufPos + SERVERHELLODONE_TEMPLATE_SIZE > \
sessionInfoPtr->receiveBufEnd || \
memcmp( bufPtr, serverHelloDoneTemplate,
SERVERHELLODONE_TEMPLATE_SIZE ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid server hello packet" );
sessionInfoPtr->receiveBufPos += SERVERHELLODONE_TEMPLATE_SIZE;
/* If we need a client cert, build the client cert packet */
bufPtr = sessionInfoPtr->sendBuffer + sessionInfoPtr->sendBufStartOfs;
if( needClientCert )
{
/* If we haven't got a cert available, tell the server. SSL and TLS
differ here, SSL sends a no-certificate alert while TLS sends an
empty client cert packet */
if( sessionInfoPtr->privateKey == CRYPT_ERROR )
{
setErrorInfo( sessionInfoPtr, CRYPT_SESSINFO_PRIVATEKEY,
CRYPT_ERRTYPE_ATTR_ABSENT );
if( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL )
swrite( &sessionInfoPtr->stream, noCertAlertSSLTemplate,
NOCERTALERT_TEMPLATE_SIZE );
else
{
memcpy( bufPtr, noCertTLSTemplate, NOCERT_TEMPLATE_SIZE );
length = NOCERT_TEMPLATE_SIZE;
bufPtr += NOCERT_TEMPLATE_SIZE;
}
/* The reaction to the lack of a cert is up to the server (some
just request one anyway even though they can't do anything
with it), so from here on we just continue as if nothing had
happened */
needClientCert = FALSE;
}
else
{
/* Write the client cert chain */
status = length = writeSSLCertChain( sessionInfoPtr, bufPtr );
if( cryptStatusError( status ) )
return( status );
bufPtr += status;
}
}
else
/* No client cert packet */
length = 0;
/* Build the client key exchange packet:
byte ID = 0x10
uint24 len
RSA:
[ uint16 encKeyLen - TLS only ]
byte[] rsaPKCS1( byte[2] { 0x03, 0x0n } || byte[46] random )
DH:
uint16 yLen
byte[] y */
*bufPtr++ = SSL_HAND_CLIENT_KEYEXCHANGE;
*bufPtr++ = 0;
lengthPtr = bufPtr;
bufPtr += UINT16_SIZE;
if( !isKeyxAlgo( algorithm ) )
{
if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS )
bufPtr += UINT16_SIZE; /* See comment below */
/* Create the premaster secret and wrap it using the server's public
key. Note that the version that we advertise at this point is the
version originally offered by the client in its hello message, not
the version eventually negotiated for the connection. This is
designed to prevent rollback attacks */
handshakeInfo->premasterSecret[ 0 ] = SSL_MAJOR_VERSION;
handshakeInfo->premasterSecret[ 1 ] = handshakeInfo->clientOfferedVersion;
setMessageData( &msgData,
handshakeInfo->premasterSecret + VERSIONINFO_SIZE,
SSL_SECRET_SIZE - VERSIONINFO_SIZE );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_RANDOM );
if( cryptStatusError( status ) )
return( status );
setMechanismWrapInfo( &mechanismInfo, bufPtr, CRYPT_MAX_PKCSIZE,
handshakeInfo->premasterSecret, SSL_SECRET_SIZE,
CRYPT_UNUSED, sessionInfoPtr->iKeyexCryptContext,
CRYPT_UNUSED );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_EXPORT,
&mechanismInfo, MECHANISM_PKCS1_RAW );
if( cryptStatusError( status ) )
return( status );
bufPtr += mechanismInfo.wrappedDataLength;
length += ID_SIZE + LENGTH_SIZE + mechanismInfo.wrappedDataLength;
if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS )
{
/* The original Netscape SSL implementation didn't provide a
length for the encrypted key and everyone copied that so it
became the de facto standard way to do it (Sic faciunt omnes.
The spec itself is ambiguous on the topic). This was fixed
in TLS (although the spec is still ambiguous) so the encoding
differs slightly between SSL and TLS */
mputWord( lengthPtr, UINT16_SIZE + mechanismInfo.wrappedDataLength );
length += UINT16_SIZE;
}
mputWord( lengthPtr, mechanismInfo.wrappedDataLength );
}
else
{
KEYAGREE_PARAMS keyAgreeParams;
/* Perform phase 2 of the DH key agreement. This is in fact extra-
ordinarily complex since SSL allows for DH parameters to be
exchanged in every imaginable manner, including raw DH parameters,
a DH key signed by the server, a DH server cert, and just to top
it all off as DH client info. Since nothing actively uses DH,
it's not even possible to determine which of the various options
are likely to occur. Because of this we go through the motions of
handling DH up to this point but leave the public value zeroed, if
anyone ever reports a live deployment that uses DH we can fetch
the data from the appropriate location and complete the key
agreement process */
memset( &keyAgreeParams, 0, sizeof( KEYAGREE_PARAMS ) );
#if 0
memcpy( keyAgreeParams.publicValue, y, yLength );
keyAgreeParams.publicValueLen = yLength;
#endif /* 0 */
status = krnlSendMessage( sessionInfoPtr->iKeyexCryptContext,
IMESSAGE_CTX_DECRYPT, &keyAgreeParams,
sizeof( KEYAGREE_PARAMS ) );
if( cryptStatusError( status ) )
{
zeroise( &keyAgreeParams, sizeof( KEYAGREE_PARAMS ) );
return( status );
}
memcpy( handshakeInfo->premasterSecret, keyAgreeParams.wrappedKey,
SSL_SECRET_SIZE );
zeroise( &keyAgreeParams, sizeof( KEYAGREE_PARAMS ) );
}
/* If we need to supply a client cert, send the signature generated with
the cert to prove possession of the private key */
if( needClientCert )
{
int verifyInfoLength;
/* Write the packet header and drop in the signature data */
*bufPtr++ = SSL_HAND_CLIENT_CERTVERIFY;
status = verifyInfoLength = \
processCertVerify( sessionInfoPtr, handshakeInfo,
bufPtr + LENGTH_SIZE, 0,
min( sessionInfoPtr->sendBufSize - ( length + 256 ),
MAX_PACKET_SIZE ) );
if( cryptStatusError( status ) )
return( status );
*bufPtr++ = 0;
mputWord( bufPtr, verifyInfoLength );
length += ID_SIZE + LENGTH_SIZE + verifyInfoLength;
}
/* Send the client information to the server */
wrapHandshakePacket( sessionInfoPtr->sendBuffer, length,
sessionInfoPtr->version );
status = swrite( &sessionInfoPtr->stream, sessionInfoPtr->sendBuffer,
sessionInfoPtr->sendBufStartOfs + length );
if( cryptStatusError( status ) )
{
sNetGetErrorInfo( &sessionInfoPtr->stream,
sessionInfoPtr->errorMessage,
&sessionInfoPtr->errorCode );
return( status );
}
dualMacData( handshakeInfo, sessionInfoPtr->sendBuffer + \
sessionInfoPtr->sendBufStartOfs, length );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Session Access Routines *
* *
****************************************************************************/
void initSSLclientProcessing( SSL_HANDSHAKE_INFO *handshakeInfo )
{
handshakeInfo->beginHandshake = beginClientHandshake;
handshakeInfo->exchangeKeys = exchangeClientKeys;
}
#endif /* USE_SSL */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -