ECDH密鑰協商
ECDH密鑰協商算法基於橢圓曲線密碼系統(ECC),使用較短的密鑰長度可提供與RSA或DH算法同等的安全等級,密鑰長度位160 ~ 256比特的橢圓曲線算法與密鑰長度位1024 ~ 3072比特的非ECC算法安全強度相同。
常用有限域上的橢圓曲線
橢圓曲線由以下參數組成
T=(p,a,b,G,n,h)
- p 有限域中的大素數,長度一般224比特、256比特、384比特
- a 整數,橢圓方程係數
- b整數,橢圓方程係數
- G,橢圓曲線上某點,生成元
- n,爲一個素數,表示橢圓曲線的階
- h,餘因數
其中G包含Gx和Gy 2個參數,非壓縮模式以04開始,壓縮模式以03開始,實際以非壓縮模式
通過模數p和係數a,b構成橢圓曲線方程
y^2=x^3+ax+b mod p
TLS支持很多橢圓曲線,常用的有2個secp256r1和secp284r1
secp256r1
大素數p長度256比特(32字節)
p=FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF
a=FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC
b=5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B
生成源G(Gx,Gy)
Gx=6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296
Gy=4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5
橢圓曲線的階n
n=FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551
secp384r1
大素數p長度384比特(48字節)
省略。。。
ECDH密鑰協商步驟
其實和DH思想一樣,只不過這是基於橢圓曲線離散對數上實現的。
-
ECDH共享參數
Alice和Bob進行ECDH密鑰協商之前雙方要有共同的ECDH共享參數即必須選擇相同的橢圓曲線方程、大素數p、生成源G,實際中這些橢圓曲線已經被相關組織標準,比如上邊的secp256r1、secp384r1,通過這個雙方就確定了這些共享參數 -
ECHD密鑰協商
1.Alice選擇一個比橢圓曲線階小的隨機數dA
作爲私密參數,計算QA=dA G=(xA,yA)
發送給Bob
2.Bob選擇一個比橢圓曲線階小的隨機數dB
作爲私密參數,計算QB=dB G=(xB,yB)
發送給Alice
3.Bob收到QA並計算得到共享密鑰參數KB=dB QA=dB(dA G)
4.Alice收到QB並計算得到共享密鑰參數KA=dA QB=dA(dB G)
根據橢圓曲線結合律dB(dA G) = dA(dB G) = (xQ,yQ)
,即獲得共享密鑰KA=KB
,KA和KB是一個座標點,所以共享密鑰可以是xQ,yQ兩部分也可以是xQ單一部分,若共享曲線是secp256r1
,則xQ,yQ長度均256比特,如共享曲線是secp384r1
,則xQ,yQ長度均384比特
如下圖
mbedtls橢圓曲線模塊
ecp.c/ecp.h
橢圓曲線基本操作ecp_curves.c
橢圓曲線定義ecdh.c/ecdh.h
橢圓曲線密鑰協商ecdsa.c/ecdsa.h
橢圓曲線數據簽名
mbedtls所支持的橢圓曲線可以通過config.h
中下面這些宏使能或者關閉,這些宏是ecp_curves.c
中條件的編譯
/**
* \def MBEDTLS_ECP_DP_SECP192R1_ENABLED
*
* MBEDTLS_ECP_XXXX_ENABLED: Enables specific curves within the Elliptic Curve
* module. By default all supported curves are enabled.
*
* Comment macros to disable the curve and functions for it
*/
#define MBEDTLS_ECP_DP_SECP192R1_ENABLED
#define MBEDTLS_ECP_DP_SECP224R1_ENABLED
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
#define MBEDTLS_ECP_DP_SECP384R1_ENABLED
#define MBEDTLS_ECP_DP_SECP521R1_ENABLED
#define MBEDTLS_ECP_DP_SECP192K1_ENABLED
#define MBEDTLS_ECP_DP_SECP224K1_ENABLED
#define MBEDTLS_ECP_DP_SECP256K1_ENABLED
#define MBEDTLS_ECP_DP_BP256R1_ENABLED
#define MBEDTLS_ECP_DP_BP384R1_ENABLED
#define MBEDTLS_ECP_DP_BP512R1_ENABLED
#define MBEDTLS_ECP_DP_CURVE25519_ENABLED
mbedtls ECHDH例子
先要打開這些宏,這個例子還是想DH協商那樣通過共享內存來模擬網絡交換共享參數。
#define MBEDTLS_AES_C
#define MBEDTLS_SHA256_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_ECP_C 開啓橢圓曲線基礎運算
#define MBEDTLS_ECDH_C 開啓橢圓曲線密鑰協商
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED 選擇secp256r1曲線參數
#define MBEDTLS_AES_ROM_TABLES
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "mbedtls/ecdh.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/platform.h"
#define assert_exit(cond, ret) \
do { if (!(cond)) { \
printf(" !. assert: failed [line: %d, error: -0x%04X]\n", __LINE__, -ret); \
goto cleanup; \
} } while (0)
/*
static int entropy_source(void *data, uint8_t *output, size_t len, size_t *olen)
{
uint32_t seed;
seed = sys_rand32_get();
if (len > sizeof(seed)) {
len = sizeof(seed);
}
memcpy(output, &seed, len);
*olen = len;
return 0;
}*/
static void dump_buf(char *info, uint8_t *buf, uint32_t len)
{
mbedtls_printf("%s", info);
for (int i = 0; i < len; i++) {
mbedtls_printf("%s%02X%s", i % 16 == 0 ? "\n ":" ",
buf[i], i == len - 1 ? "\n":"");
}
}
int main(void)
{
int ret = 0;
size_t olen;
char buf[65];
mbedtls_ecp_group grp;
mbedtls_mpi cli_secret, srv_secret;
mbedtls_mpi cli_pri, srv_pri;
mbedtls_ecp_point cli_pub, srv_pub;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
uint8_t *pers = "simple_ecdh";
mbedtls_mpi_init(&cli_pri); //
mbedtls_mpi_init(&srv_pri);
mbedtls_mpi_init(&cli_secret);
mbedtls_mpi_init(&srv_secret);
mbedtls_ecp_group_init(&grp); //初始化橢圓曲線羣結構體
mbedtls_ecp_point_init(&cli_pub); //初始化橢圓曲線點結構體 cli
mbedtls_ecp_point_init(&srv_pub);//初始化橢圓曲線點結構體 srv
mbedtls_entropy_init(&entropy); //初始化熵結構體
mbedtls_ctr_drbg_init(&ctr_drbg);//初始化隨機數結構體
/*
mbedtls_entropy_add_source(&entropy, entropy_source, NULL,
MBEDTLS_ENTROPY_MAX_GATHER, MBEDTLS_ENTROPY_SOURCE_STRONG);*/
mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
(const uint8_t *) pers, strlen(pers));
mbedtls_printf("\n . setup rng ... ok\n");
//加載橢圓曲線,選擇SECP256R1
ret = mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1);
mbedtls_printf("\n . select ecp group SECP256R1 ... ok\n");
//cli生成公開參數
ret = mbedtls_ecdh_gen_public(&grp, //橢圓曲線結構體
&cli_pri,//輸出cli私密參數d
&cli_pub,//輸出cli公開參數Q
mbedtls_ctr_drbg_random, &ctr_drbg);
assert_exit(ret == 0, ret);
mbedtls_ecp_point_write_binary(&grp, &cli_pub, //把cli的公開參數到處到buf中
MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, buf, sizeof(buf));
dump_buf(" 1. ecdh client generate public parameter:", buf, olen);
//srv生成公開參數
ret = mbedtls_ecdh_gen_public(&grp, //橢圓曲線結構體
&srv_pri,//輸出srv私密參數d
&srv_pub,//輸出srv公開參數Q
mbedtls_ctr_drbg_random, &ctr_drbg);
assert_exit(ret == 0, ret);
mbedtls_ecp_point_write_binary(&grp, &srv_pub, //把srv的公開參數導出到buf中
MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, buf, sizeof(buf));
dump_buf(" 2. ecdh server generate public parameter:", buf, olen);
//cli計算共享密鑰
ret = mbedtls_ecdh_compute_shared(&grp, //橢圓曲線結構體
&cli_secret, //cli計算出的共享密鑰
&srv_pub, //輸入srv公開參數Q
&cli_pri, //輸入cli本身的私密參數d
mbedtls_ctr_drbg_random, &ctr_drbg);
assert_exit(ret == 0, ret);
//把cli計算出的共享密鑰導出buf中
mbedtls_mpi_write_binary(&cli_secret, buf, mbedtls_mpi_size(&cli_secret));
dump_buf(" 3. ecdh client generate secret:", buf, mbedtls_mpi_size(&cli_secret));
//srv計算共享密鑰
ret = mbedtls_ecdh_compute_shared(&grp, //橢圓曲線結構體
&srv_secret, //srv計算出的共享密鑰
&cli_pub, //輸入cli公開參數Q
&srv_pri, //輸入srv本身的私密參數d
mbedtls_ctr_drbg_random, &ctr_drbg);
assert_exit(ret == 0, ret);
//把srv計算出的共享密鑰導出buf中
mbedtls_mpi_write_binary(&srv_secret, buf, mbedtls_mpi_size(&srv_secret));
dump_buf(" 4. ecdh server generate secret:", buf, mbedtls_mpi_size(&srv_secret));
//比較2個大數是否相等
ret = mbedtls_mpi_cmp_mpi(&cli_secret, &srv_secret);
assert_exit(ret == 0, ret);
mbedtls_printf(" 5. ecdh checking secrets ... ok\n");
cleanup:
mbedtls_mpi_free(&cli_pri);
mbedtls_mpi_free(&srv_pri);
mbedtls_mpi_free(&cli_secret);
mbedtls_mpi_free(&srv_secret);
mbedtls_ecp_group_free(&grp);
mbedtls_ecp_point_free(&cli_pub);
mbedtls_ecp_point_free(&srv_pub);
mbedtls_entropy_free(&entropy);
mbedtls_ctr_drbg_free(&ctr_drbg);
return 0;
}
運行結果
. setup rng ... ok
. select ecp group SECP256R1 ... ok
1. ecdh client generate public parameter:
04 D8 CC C3 43 FF 96 03 2A AB DD 1D 42 D3 D2 6E
ED 31 08 EB 6D 6E D3 1E F0 E8 5A 8B 8E E3 2B D6
02 B9 B3 AA 8F CF 99 EA 6E F5 3A 0B 04 34 4F 00
6D B4 28 29 0E DF 1B 2B 38 E7 34 A7 E5 AD B4 6B
1D
2. ecdh server generate public parameter:
04 5C C6 24 F1 61 E5 B2 B8 F1 45 73 3E 99 F8 1B
DB 95 13 5E 9D 59 BA DA 7F 78 01 AC C8 3C FD 7E
2F 27 04 18 B0 29 7F 9E C0 BF 86 78 CF 49 1C 98
0B 03 75 B0 43 D0 36 F7 90 CF 54 49 8C F6 97 6D
AF
3. ecdh client generate secret:
04 92 C4 4B 51 39 17 18 FB 6C 92 15 16 10 E6 D6
B5 E4 CF 45 97 36 6C 12 22 E7 6E 83 02 C7 11 D9
4. ecdh server generate secret:
04 92 C4 4B 51 39 17 18 FB 6C 92 15 16 10 E6 D6
B5 E4 CF 45 97 36 6C 12 22 E7 6E 83 02 C7 11 D9
5. ecdh checking secrets ... ok
從運行結果看出
- 模擬服務器和客戶端均產生了不同的橢圓曲線公鑰
- 服務器和客戶端公鑰均採用非壓縮模式,因爲均以04開頭
- 雙方採用secp256r1,所以生成公鑰長度都爲65字節,包括1字節壓縮提示,32字節x座標,32字節y座標
- 最終雙方獲得相同的共享密鑰,共享密鑰僅包括x座標,所以長度是32字節