批量加密 和 MAC 密钥 派生自 主密钥 ,但可以包含其他源,具体取决于使用的协议和密码套件。
客户端和服务器派生批量加密和 MAC 密钥的过程是相同的:
- 协议引擎对主密钥调用 CryptSetKeyParam 一次或多次,为 CSP 提供生成密钥所需的信息。
- 由于 CryptoAPI 密钥不能直接从其他密钥派生,因此将使用 CryptCreateHash 从主密钥创建哈希对象。 此 哈希 用于创建新密钥。
- 两个批量加密密钥和两个 MAC 密钥是使用对 CryptDeriveKey 的四次调用从“主哈希”对象创建的。
注意
执行 SSL 重新连接时,协议引擎可以使用同一主密钥多次执行上述过程。 这使客户端和服务器可以有多个通常同时连接,每个连接使用不同的 批量加密 和 MAC 密钥,而无需额外的 RSA 或Diffie-Hellman操作。
所有 CSP 都必须使用良好的线程安全做法。 几十个线程计数并不罕见。
下面是协议引擎的典型源代码:
//--------------------------------------------------------------------
// Define and initialize local variables.
BOOL fClient = <TRUE if this is code for the client?>;
CRYPT_DATA_BLOB Data;
HCRYPTHASH hMasterHash;
//--------------------------------------------------------------------
// Finish creating the master_secret.
switch(<protocol being used>)
{
case <PCT 1.0>:
//------------------------------------------------------------
// Specify clear key value.
Data.pbData = pClearKey;
Data.cbData = cbClearKey;
CryptSetKeyParam(
hMasterKey,
KP_CLEAR_KEY,
(PBYTE)&Data,
0);
//------------------------------------------------------------
// Specify the CH_CHALLENGE_DATA.
Data.pbData = pChallenge;
Data.cbData = cbChallenge;
CryptSetKeyParam(
hMasterKey,
KP_CLIENT_RANDOM,
(PBYTE)&Data,
0);
//------------------------------------------------------------
// Specify the SH_CONNECTION_ID_DATA.
Data.pbData = pConnectionID;
Data.cbData = cbConnectionID;
CryptSetKeyParam(
hMasterKey,
KP_SERVER_RANDOM,
(PBYTE)&Data,
0);
//------------------------------------------------------------
// Specify the SH_CERTIFICATE_DATA.
Data.pbData = pbServerCertificate;
Data.cbData = cbServerCertificate;
CryptSetKeyParam(
hMasterKey,
KP_CERTIFICATE,
(PBYTE)&Data,
0);
break;
case <SSL 2.0>:
//------------------------------------------------------------
// Specify clear key value.
Data.pbData = pClearKey;
Data.cbData = cbClearKey;
CryptSetKeyParam(
hMasterKey,
KP_CLEAR_KEY,
(PBYTE)&Data,
0);
//------------------------------------------------------------
// Specify the CH_CHALLENGE_DATA.
Data.pbData = pChallenge;
Data.cbData = cbChallenge;
CryptSetKeyParam(
hMasterKey,
KP_CLIENT_RANDOM,
(BYTE*)&Data,
0);
//------------------------------------------------------------
// Specify the SH_CONNECTION_ID_DATA.
Data.pbData = pConnectionID;
Data.cbData = cbConnectionID;
CryptSetKeyParam(
hMasterKey,
KP_SERVER_RANDOM,
(BYTE*)&Data,
0);
break;
case <SSL 3.0>:
case <TLS 1.0>:
//------------------------------------------------------------
// Specify client_random.
Data.pbData = pClientRandom;
Data.cbData = cbClientRandom;
CryptSetKeyParam(
hMasterKey,
KP_CLIENT_RANDOM,
(PBYTE)&Data,
0);
//------------------------------------------------------------
// Specify server_random.
Data.pbData = pServerRandom;
Data.cbData = cbServerRandom;
CryptSetKeyParam(
hMasterKey,
KP_SERVER_RANDOM,
(PBYTE)&Data,
0);
}
//------------------------------------------------------------
// Create the master hash object from the master key.
CryptCreateHash(
hProv,
CALG_SCHANNEL_MASTER_HASH,
hMasterKey,
0,
&hMasterHash);
//------------------------------------------------------------
// Derive read key from the master hash object.
CryptDeriveKey(hProv,
CALG_SCHANNEL_ENC_KEY,
hMasterHash,
fClient ? CRYPT_SERVER : 0,
&hReadKey);
//------------------------------------------------------------
// Derive write key from the master hash object.
CryptDeriveKey(
hProv,
CALG_SCHANNEL_ENC_KEY,
hMasterHash,
fClient ? 0 : CRYPT_SERVER,
&hWriteKey);
if(<protocol being used> != <SSL 2.0>) // for SSL 2.0, the master
// key is also the MAC.
{
//------------------------------------------------------------
// Derive read MAC from the master hash object.
CryptDeriveKey(
hProv,
CALG_SCHANNEL_MAC_KEY,
hMasterHash,
fClient ? CRYPT_SERVER : 0,
&hReadMAC);
//------------------------------------------------------------
// Derive write MAC from the master hash object.
CryptDeriveKey(
hProv,
CALG_SCHANNEL_MAC_KEY,
hMasterHash,
fClient ? 0 : CRYPT_SERVER,
&hWriteMAC);
}
CryptDestroyHash(hMasterHash);