TLS协议分析 (五) handshake协议 证书与密钥交换

转自:http://chuansong.me/n/1281554552959

5.4. handshake — Server Certificate

当服务器确定了CipherSuite后,根据CipherSuite里面的认证算法,如果需要发送证书给客户端,那么就发送 Server Certificate消息给客户端。Server Certificate总是在ServerHello之后立即发送,所以在同一个RTT里。

Server Certificate里面包含了服务器的证书链。

消息结构:

opaque ASN.1Cert<1..2^24-1>;struct {
    ASN.1Cert certificate_list<0..2^24-1>;
} Certificate;

certificate_list
:      证书列表,发送者的证书必须是第一个,后续的每一个证书都必须是前一个的签署证书。根证书可以省略

证书申请的时候,一般会收到好几个证书,有的需要自己按照这个格式来拼接成证书链。

如果服务器要认证客户端的身份,那么服务器会发送Certificate Request消息,客户端应该也以 这条Server Certificate消息的格式回复。

服务器发送的证书必须:

  • 证书类型必须是 X.509v3。除非明确地协商成别的了(比较少见,rfc里提到了例如 [OpenPGP格式]  链接 https://tools.ietf.org/html/rfc5081 )。

  • 服务器证书的公钥,必须和选择的密钥交换算法配套。

密钥交换+认证算法 配套的证书中公钥类型
RSA  /  RSA_PSK RSA 公钥;证书中必须允许私钥用于加密 (即如果使用了X509V3规定的key usage扩展, keyEncipherment比特位必须置位) 这种用法没有前向安全性,因此在 TLS 1.3中被废弃了


DHE_RSA /  ECDHE_RSA RSA 公钥;证书中必须允许私钥用于签名(即如果使用了X509V3规定的key usage扩展, digitalSignature比特位必须置位),并且允许server key exchange消息将要使用的签名模式(例如 PKCS1_V1.5 ,OAEP等)和hash算法(例如sha1, sha256等)


DHE_DSS DSA 公钥; 历史遗留产物,从来没有被大规模用过,安全性差,废弃状态。证书必须允许私钥用于签名,必须允许server key exchange消息中使用的hash算法。
DH_DSS   / DH_RSA Diffie-Hellman 公钥; 要求key usage里面的keyAgreement比特位必须置位。 这种用法没有前向安全性,因此在 TLS 1.3中被废弃了


ECDH_ECDSA   / ECDH_RSA 能做 ECDH 用途的公钥;公钥必须使用 客户端支持的ec曲线和点格式。这种用法没有前向安全性,因此在 TLS 1.3中被废弃了


ECDHE_ECDSA ECDSA用途的公钥;证书必须运输私钥用作签名,必须允许server key exchange消息里面要用到的hash算法。公钥必须使用客户端支持的ec曲线和点格式。
  • “server_name” 和 “trusted_ca_keys” 扩展用于引导证书选择。

其中有5种是ECC密钥交换算法:ECDH_ECDSA, ECDHE_ECDSA, ECDH_RSA, ECDHE_RSA, ECDH_anon。
ECC(椭圆曲线)体制相比RSA,由于公钥更小,性能更高,所以在移动互联网环境下越发重要。
以上ECC的5种算法都用ECDH来计算premaster  secret, 仅仅是ECDH密钥的生命周期和认证算法不同。
其中只有 ECDHE_ECDSA 和 ECDHE_RSA 是前向安全的。

如果客户端在ClientHello里提供了 “signature_algorithms” 扩展,那么服务器提供的所有证书必须用 “signature_algoritms”中提供的 hash/signature算法对 之一签署。要注意的是,这意味着,一个包含某种签名算法密钥的证书,可能被另一种签名算法签署(例如,一个RSA公钥可能被一个ECDSA公钥签署)。(这在TLS1.2和TLS1.1中是不一样的,TLS1.1要求所有的算法都相同。)注意这也意味着DH_DSS,DH_RSA,ECDH_ECDSA,和ECDH_RSA 密钥交换不限制签署证书的算法。固定DH证书可能使用”signature_algorithms”扩展列表中的 hash/签名算法对 中的某一个签署。名字 DH_DSS, DH_RSA, ECDH_ECDSA, 和 ECDH_RSA 只是历史原因,这几个名字的后半部分中指定的算法,并不会被使用,即DH_DSS中的DSS并不会被使用,DH_RSA中并不会使用RSA做签名,ECDH_ECDSA并不会使用ECDSA算法。。。
如果服务器有多个证书,就必须从中选择一个,一般根据服务器的外网ip地址,SNI中指定的hostname,服务器配置来做选择。如果服务器只有一个证书,那么要确保这一个证书符合这些条件。
要注意的是,存在一些证书使用了TLS目前不支持的 算法组合。例如,使用 RSASSA-PSS签名公钥的证书(即证书的SubjectPublicKeyInfo字段是id-RSASSA-PSS)。由于TLS没有给这些算法定义对应的签名算法,这些证书不能在TLS中使用。
如果一个CipherSuite指定了新的TLS密钥交换算法,也会指定证书格式和要求的密钥编码方法。

5.5. handshake  — Server Key Exchange

服务器会在 server Certificate 消息之后,立即发送 Server Key Exchange消息。
(如果协商出的CipherSuite不需要做认证,即anonymous negotiation,会在ServerHello之后立即发送Server Key Exchange消息)

只有在server Certificate 消息没有足够的信息,不能让客户端完成premaster的密钥交换时,服务器才发送 server Key Exchange, 主要是对前向安全的几种密钥协商算法,列表如下:

  1. DHE_DSS

  2. DHE_RSA

  3. DH_anon

  4. ECDHE_ECDSA

  5. ECDHE_RSA

  6. ECDH_anon

对下面几种密钥交换方法,发送ServerKeyExchange消息是非法的:

  1. RSA

  2. DH_DSS

  3. DH_RSA

  4. ECDH_ECDSA

  5. ECDH_RSA

需要注意的是,ECDH和ECDSA公钥的数据结构是一样的。所以,CA在签署一个证书的时候,可能要使用 X.509 v3 的 keyUsage 和 extendedKeyUsage 扩展来限定ECC公钥的使用方式。

ServerKeyExchange传递足够的信息给客户端,来让客户端交换premaster secret。一般要传递的是:一个 Diffie-Hellman 公钥,或者一个其他算法(例如RSA)的公钥。

在TLS实际部署中,我们一般只使用这4种:ECDHE_RSA, DHE_RSA,  ECDHE_ECDSA,RSA

其中RSA密钥协商(也可以叫密钥传输)算法,由于没有前向安全性,在TLS 1.3里面已经被废除了。参见: [Confirming Consensus on removing RSA key Transport from TLS    1.3 ] 链接 http://www.ietf.org/mail-archive/web/tls/current/msg12266.html

消息格式:

enum { dhe_dss, dhe_rsa, dh_anon, rsa, dh_dss, dh_rsa,    ec_diffie_hellman
     } KeyExchangeAlgorithm;

struct {    opaque dh_p<1..2^16-1>;    opaque dh_g<1..2^16-1>;    opaque dh_Ys<1..2^16-1>; } ServerDHParams;     /* Ephemeral DH parameters */


dh_p   Diffie-Hellman密钥协商计算的大质数模数。 dh_g   Diffie-Hellman 的生成元, dh_Ys   服务器的Diffie-Hellman公钥 (g^X mod p).  

struct {      opaque point <1..2^8-1>;  } ECPoint;  

enum { explicit_prime (1), explicit_char2 (2),         named_curve (3), reserved(248..255) } ECCurveType;  

struct {      ECCurveType    curve_type;      select (curve_type) {
         case named_curve:              NamedCurve namedcurve;      };  } ECParameters;  
         
struct {      ECParameters    curve_params;      ECPoint         public; //ECDH的公钥  } ServerECDHParams;

struct {    select (KeyExchangeAlgorithm) {
       case dh_anon:            ServerDHParams params;
       case dhe_dss:
       case dhe_rsa:            ServerDHParams params;            digitally-signed struct {                opaque client_random[32];                opaque server_random[32];                ServerDHParams params;            } signed_params;
       case ec_diffie_hellman:            ServerECDHParams    params;            Signature           signed_params;
       case rsa:
       case dh_dss:
       case dh_rsa:
            struct {} ;
       /* message is omitted for rsa, dh_dss, and dh_rsa */        /* may be extended, e.g., for ECDH -- see [TLSECC] */    }; } ServerKeyExchange; params   服务器的密钥交换参数。 signed_params   对需要认证的(即非anonymous的)密钥交换,对服务器的密钥交换参数的数字签名。

ECParameters 结构比较麻烦,其中ECCurveType是支持3种曲线类型的,可以自行指定椭圆曲线的多项式系数,基点等参数。但是,我们基本不会用到这种功能,因为一般部署都是使用 NamedCurve,即参数已经预先选定,各种密码学库普遍都支持的一组曲线,其中目前用的最广的是 secp256r1 (还被称为 P256,或 prime256v1)

NamedCurve 列表中比较重要的曲线(在TLS1.3中,只保留了这几条曲线。),定义如下:

enum {
    ...
    secp256r1 (23), secp384r1 (24), secp521r1 (25),
    reserved (0xFE00..0xFEFF),
    (0xFFFF)
} NamedCurve;

ECDHE_RSA 密钥交换算法的 SignatureAlgorithm 是 rsa 。
ECDHE_RSA 密钥交换算法的 SignatureAlgorithm 是 ecdsa。

如果客户端提供了 “signature_algorithms” 扩展, 则签名算法和hash算法必须是列在扩展中的算法。
要注意的是,这个地方可能有不一致,例如客户端可能提供了 DHE_DSS 密钥交换,但是 “signature_algorithms”扩展中没有DSA算法,在这类情况下,为了正确地协商,服务器必须确保满足自己选择的CipherSuite满足 “signature_algorithms” 的限制。这不优雅,但是是为了把对原来的CipherSuite协商的设计的改动减到最小,而做的妥协。

并且,hash和签名算法,必须和服务器的证书里面的公钥兼容。

5.6. handshake  — Certificate Request

TLS规定了一个可选功能:服务器可以认证客户端的身份,这通过服务器要求客户端发送一个证书实现,服务器应该在ServerKeyExchange之后立即发送CertificateRequest消息。

消息结构:

enum {
    rsa_sign(1), dss_sign(2), rsa_fixed_dh(3),dss_fixed_dh(4),
    rsa_ephemeral_dh_RESERVED(5),dss_ephemeral_dh_RESERVED(6),
    fortezza_dms_RESERVED(20),
    ecdsa_sign(64), rsa_fixed_ecdh(65),
    ecdsa_fixed_ecdh(66), 
    (255)
} ClientCertificateType;

opaque DistinguishedName<1..2^16-1>;struct {
    ClientCertificateType certificate_types<1..2^8-1>;
    SignatureAndHashAlgorithm
      supported_signature_algorithms<2^16-1>;
    DistinguishedName certificate_authorities<0..2^16-1>;
} CertificateRequest;

certificate_types
:    客户端可以提供的证书类型。

  • rsa_sign        包含RSA公钥的证书。

  • dss_sign        包含DSA公钥的证书。

  • rsa_fixed_dh    包含静态DH公钥的证书。

  • dss_fixed_dh    包含静态DH公钥的证书。

    supported_signature_algorithms
    :      服务器支持的  hash/signature 算法的列表。

    certificate_authorities
    :      服务器可以接受的CA(certificate_authorities)的 distinguished names 的列表 DER编码格式.

这些 distinguished names 可能为root CA或者次级CA指定了想要的 distinguished name ,因此,这个消息可以用来描述已知的root,或者希望的授权空间。
如果 certificate_authorities 列表是空的,那么客户端可以发送任何适当的 ClientCertificateType 类型的证书,如果没有别的限制的话。

certificate_types 和 supported_signature_algorithms 字段的交叉选择很复杂。 certificate_types 这个字段从SSLv3时代就定义了,但是一直都没有详细定义,其大多数功能都被 supported_signature_algorithms 代替了。
  有如下规则:

  • 客户端提供的任何证书,必须用一个supported_signature_algorithms 中出现过的  hash/signature 算法对 签名.

  • 客户端提供的末端证书必须提供一个和 certificate_types 兼容的key。 如果这个key是一个签名key,那必须能和 supported_signature_algorithms 中提供的某个 hash/signature 算法对配合使用。

  • 由于历史原因,某些客户端证书类型的名字,包含了证书的签名算法,例如,早期版本的TLS中, rsa_fixed_dh 意思是一个被RSA算法签署,并且包含一个固定DH密钥的证书。在TLS1.2中,这个功能被 supported_signature_algorithms 淘汰,并且证书类型不再限制用来签署证书的算法。例如,如果服务器发送了 dss_fixed_dh 证书类型,和 { {sha1, dsa}, {sha1,rsa} } 签名类型,客户端可以回复一个 包含静态DH密钥,用RSA-sha1签署的证书。

  • 如果协商出来的是匿名CipherSuite,服务器不能要求客户端认证。

5.7. handshake  — Server Hello Done

在 ServerHello和相关消息已经处理结束后,服务器发送ServerHelloDone。在发送ServerHelloDone后,服务器开始等待客户端的响应。

ServerHelloDone消息表示,服务器已经发送完了密钥协商需要的消息,并且客户端可以开始客户端的密钥协商处理了。

收到ServerHelloDone后,客户端应该确认服务器提供了合法的证书,并且确认服务器的ServerHello消息里面的参数是可以接受的。

消息格式:

  struct { } ServerHelloDone;

5.8. handshake  — Client Certificate

ClientCertificate消息是客户端收到ServerHelloDone后,可以发送的第一条消息。仅当服务器要求了一个证书的情况下,客户端才发送ClientCertificate消息,如果没有可用的合适证书,客户端必须发送一条不包含任何证书的ClientCertificate消息(即 certificate_list 结构长度为0)。

如果客户端没有发送任何证书,服务器自行决定,可以放弃要求客户端认证,继续握手;或者发送一条 fatal handshake_failure的alert消息,断开连接。并且,如果证书链的某些方面是不能接受的(比如证书没有被可信任的CA签署),服务器可以自行决定,是继续握手(放弃要求客户端认证),或者发送一条fatal的alert。

客户端证书使用和ServerCertificate相同的结构发送。

ClientCertificate把客户端的证书链发送给服务器。服务器会使用证书链来验证CertificateVerify 消息(如果使用基于签名的客户端认证),或者来计算premaster secret(对于非短暂的 DH)。证书必须和协商出来的CipherSuite的密钥交换算法配套,并和任何协商的扩展配套。

尤其是:

  • 证书必须是X.509v3 类型的。

  • 客户端的末级证书的公钥必须和CertificateRequest里列出的证书类型兼容。

客户端证书类型 证书公钥类型
rsa_sign RSA公钥;证书必须允许公钥用于certificateVerify消息中的数字签名和hash算法
dss_sign DSA 公钥;证书必须允许密钥使用CertificateVerify中的hahs函数做签名;
ecdsa_sign 可以用作 ECDSA 的公钥;证书必须允许 公钥用 CertificateVerify中的hash函数做签名;公钥必须使用服务器支持的曲线,和点格式;
rsa_fixed_dh / dss_fixed_dh Diffie-Hellman 公钥; 必须使用和服务器key相同的参数。
rsa_fixed_ecdh   /  ecdsa_fixed_ecdh 可以用作 ECDH 的公钥。必须和服务器的公钥使用同样的曲线,同样的点格式
  • 如果 certificate_authorities 列表不是空的,客户端证书链中的某一个证书必须是CA中的某一个签署的。

  • 证书必须使用 服务器可以接受的 hash/signature 算法对。

类似于Server Certificate,有一些证书目前无法在TLS中使用。

5.9. handshake — Client Key Exchange

客户端必须在客户端的Certificate消息之后,立即发送ClientKeyExchange消息。
或者必须在ServerHelloDone后立即发送ClientKeyExchange消息。

ClientKeyExchange消息中,会设置premaster secret,通过发送 RSA公钥加密premaster secret的密文,或者发送允许双方得出相同的premaster secret的Diffie-Hellman参数。

当客户端使用短暂的 Diffie-Hellman 密钥对时,ClientKeyExchange包含客户端的 Diffie-Hellman 公钥。如果客户端发送一个包含静态 Diffie-Hellman 指数的证书(比如,在使用固定DH的客户端认证),那么这条消息必须被发送,并且必须为空。

消息结构:
消息的选择取决于选择的密钥交换算法。

struct {
    select (KeyExchangeAlgorithm) {
       case rsa:            EncryptedPreMasterSecret;
       case dhe_dss:
       case dhe_rsa:
       case dh_dss:
       case dh_rsa:
       case dh_anon:            ClientDiffieHellmanPublic;
       case ec_diffie_hellman:            ClientECDiffieHellmanPublic;    } exchange_keys; } ClientKeyExchange;

5.9.(1).  RSA 加密的 Premaster Secret 消息

如果用RSA做密钥协商和认证,客户端生成 48字节的 premaster secret,使用服务器证书里面的公钥加密,然后把密文EncryptedPreMasterSecret发送给服务器,结构定义如下:

struct {
    ProtocolVersion client_version;
    opaque random[46];
} PreMasterSecret;

client_version
   客户端支持的最新协议版本号,这个字段用来检测中间人版本回退攻击。T
random   46 字节的,安全生成的随机值。

struct {    
   public-key-encrypted PreMasterSecret pre_master_secret; } EncryptedPreMasterSecret; pre_master_secret   这个随机值由客户端生成,用于生成master secret。

注:PreMasterSecret里面的 client_version 是 ClientHello.client_version,而不是协商的到的版本号,这个特性用来阻止版本回退攻击。不幸的是,有些不正确的老的代码使用了协商得到的版本号,导致检查client_version字段的时候,和正确的实现无法互通。

客户端实现必须在PreMasterSecret中发送正确的版本号。如果 ClientHello.client_version 的版本号是 TLS 1.1 或者更高,服务器实现必须如下检查版本号。如果版本号是 TLS 1.0 或者更早,服务器必须检查版本号,但是可以通过配置项关闭检查。

要注意的是,如果版本号检查失败了,PreMasterSecret 应该像下面描述的那样填充成随机数。

TLS中的RSA使用的是 PKCS1-V1.5 填充( PKCS1-V1.5也是openssl库RSA的默认填充方式)。Bleichenbacher 在1998年发表了一种针对 PKCS1-V1.5 的选择密文攻击, Klima在2003年发现  PKCS1-V1.5 中 PreMasterSecret 版本号检查的一个侧通道攻击。只要TLS 服务器暴露一条特定的消息是否符合PKCS1-V1.5格式,或暴露PreMasterSecret解密后结构是否合法,或版本号是否合法,就可以用上面2种方法攻击。

Klima 还提出了完全避免这类攻击的方法:对格式不正确的消息,版本号不符的情况,要做出和完全正确的RSA块一样的响应,要让客户端区分不出这3种情况。
具体地说,要如下:

  1. 生成 46 字节的密码学安全随机值 R

  2. 解密消息,获得明文 M

  3. 如果 PKCS#1 填充不正确,或者 PreMasterSecret 消息的长度不是48字节,则
    pre_master_secret = ClientHello.client_version || R
    或者如果 ClientHello.client_version <= TLS 1.0,并且明确禁止了版本号检查,则
    pre_master_secret = ClientHello.client_version || M[2..47]

注意:明确地用 ClientHello.client_version 构造 pre_master_secret 时,当客户端在原来的 pre_master_secret 中发送了错误的 客户端版本值时,会产生一个不合法的 master_secret 。

另一种解决问题的方法是,把版本号不符,当成 PKCS-1 格式错误来对待,并且完全随机填充 premaster secret。

  1. 生成 48 字节的密码学安全随机值 R

  2. 解密 PreMasterSecret 恢复出明文 M

  3. 如果 PKCS#1 填充不正确,或者消息的长度不是48字节,则
    pre_master_secret = R
    或者如果 ClientHello.client_version <= TLS 1.0,并且 明确禁止了版本号检查,则
    pre_master_secret = M
    或者如果 M[0..1] != CleintHello.client_version
    pre_master_secret = R
    或者
    pre_master_secret = M

尽管实践中,还没有发现针对这种结构的攻击,Klima 在论文中描述了几种理论上的攻击方式,因此推荐上述的第一种结构。

在任何情况下,一个 TLS 服务器绝对不能在:1. 处理 RSA 加密的 premaster 消息失败, 2.或者版本号检查失败 时产生alert消息。当遇到这两种情况时,服务器必须用随机生成的 premaster 值继续握手。服务器可以把造成失败的真实原因log下来,用于调查问题,但是必须小心确保不能把这种信息泄漏给攻击者(比如通过时间侧通道,log文件,或者其它通道等泄漏)。

RSAES-OAEP 加密体制,更能抵抗 Bleichenbacher 发表的攻击,然而,为了和早期的TLS版本最大程度保持兼容,TLS 仍然规定使用  RSAES-PKCS1-v1_5 体制。只要遵守了上面列出的建议,目前还没有 Bleichenbacher 的变化形式能攻破 TLS 。

实现的时候要注意:公钥加密的数据用 字节数组 <0..2^16-1> 的形式表示。因此,ClientKeyExchange中的 RSA加密的PreMasterSecret 前面有2个字节用来表示长度。这2个字节在使用RSA做密钥协商时,是冗余的,因为此时 EncryptedPreMasterSecret 是 ClientKeyExchange 中的唯一字段,因此可以无歧义地得出 EncryptedPreMasterSecret 的长度。因此更早的 SSLv3 规范没有明确规定 public-key-encrypted 数据的编码格式,因此有一些SSLv3的实现没有包含 长度字段,这些实现直接把 RSA 加密的数据放入了 ClientKeyExchange消息里面。
TLS规范要求 EncryptedPreMasterSecret 字段包含长度字段。因此得出的结果会和一些 SSLv3 的实现不兼容。实现者从 SSLv3 升级到 TLS 时,必须修改自己的实现,以接受并且生成带长度的格式。如果一个实现要同时兼容 SSLv3 和 TLS,那就应该根据协议版本确定自己的行为。

注意:根据 Boneh 等在2003年USENIX Security Symposium上发表的论文 “Remote timing attacks are practical”,针对 TLS RSA密钥交换的远程时间侧通道攻击,是实际可行的,起码当客户端和服务器在同一个LAN里时是可行的。因此,使用静态 RSA 密钥的实现,必须使用 RSA blinding,或者Boneh论文中提到的,其他抵抗时间侧通道攻击的技术。

openssl中的RSA blinding,参见:http://linux.die.net/man/3/rsa_blinding_on

5.9.(2).  客户端 Diffie-Hellman 公钥

这条消息把客户端的 Diffie-Hellman 公钥 ( Yc ) 发送给服务器。

Yc的编码方式由 PublicValueEncoding 决定。

消息的结构:

enum { implicit, explicit } PublicValueEncoding;

implicit
   如果客户端已经发送了一个包含合适的 DH 公钥的证书(即 fixed_dh 客户端认证方式),那么Yc已经隐式包含了,不需要再发送。这种情况下,ClientKeyExchange消息必须发送,并且必须是空的。

explicit   表示Yc需要发送。


struct {    select (PublicValueEncoding) {
       case implicit:
          struct { };
        case explicit:
           opaque dh_Yc<1..2^16-1>;    } dh_public; } ClientDiffieHellmanPublic; dh_Yc   客户端的 Diffie-Hellman 公钥 Yc.

5.9.(3).  客户端 EC Diffie-Hellman 公钥

struct {
    select (PublicValueEncoding) {
       case implicit:
         struct { };
       case explicit:
           ECPoint ecdh_Yc;    } ecdh_public; } ClientECDiffieHellmanPublic;

Diffie-Hellman 推广到椭圆曲线群上,就是 EC Diffie-Hellman ,简称 ECDH,其它的计算,和一般的 DH 计算类似。

ECDH 是目前最重要的密钥协商算法 http://vincent.bernat.im/en/blog/2011-ssl-perfect-forward-secrecy.html

5.10. handshake  — Cerificate Verify

当需要做客户端认证时,客户端发送CertificateVerify消息,来证明自己确实拥有客户端证书的私钥。这条消息仅仅在客户端证书有签名能力的情况下发送(就是说,除了含有固定 Diffie-Hellman 参数的证书以外的证书)。CertificateVerify必须紧跟在ClientKeyExchange之后发送。

消息结构:
  Structure of this message:

struct {
     digitally-signed struct {
         opaque handshake_messages[handshake_messages_length];
     }
} CertificateVerify;

此处, handshake_messages 表示所有发送或者接收的握手消息,从client hello开始,一直到CertificateVerify之前的所有消息,包括handshake消息的type和length字段,这是之前所有握手结构体的拼接。要注意,这要求双方在握手过程中,都得缓存所有消息,或者在握手过程中,用每一种可能的hash算法计算到CeritificateVerify为止的hash值。

signature中用的hash和签名算法必须是 CertificateRequest 的 supported_signature_algorithms 中的某一种。另外,hash和签名算法必须和客户端的证书的算法兼容。
RSA公钥可能被用于任何允许的hash函数,只要遵循证书中的限制。

你可能感兴趣的:(SSL/TLS)