Android Boring SSL

前期设置

SSLContext.getInstance("TLS")

首先通过SSLContext.getInstance("TLS")获取SSLContext, 此时的SSLContext中的SSLContextSpiOpenSSLContextImpl, 在OpenSSLContextImpl构造时, 构造了ClientSessionContext并记录在OpenSSLContextImpl.clientSessionContext. 而在ClientSessionContext构造时, 其父类AbstractSessionContext的构造函数中, 调用了底层的NativeCrypto.SSL_CTX_new()创建了SSL_CTX并记录在OpenSSLContextImpl.super.sslCtxNativePointer中(主要步骤):

  • 调用TLS_with_buffers_method()获取SSL_METHOD:

    c 复制代码
      static const SSL_METHOD kMethod = {
          0,
          &kTLSProtocolMethod,
          &ssl_noop_x509_method,
      };
  • 通过SSL_CTX_new()创建SSL_CTX并进行一系列设置, 其中, SSL_CTX.method设置为kMethod:

    • SSL_CTX.method设置为kTLSProtocolMethod(类型为SSL_PROTOCOL_METHOD)

    • SSL_CTX.x509_method设置为ssl_noop_x509_method(类型为SSL_X509_METHOD)

    • 其中: kTLSProtocolMethod的定义:

      c 复制代码
      static const SSL_PROTOCOL_METHOD kTLSProtocolMethod = {
          false /* is_dtls */,
          tls_new, // 初始化 SSL, 创建 SSL_HANDSHAKE
          tls_free,
          tls_get_message, // PEEK 一个 SSLMessage
          tls_next_message, // 读取一个 SSLMessage
          tls_has_unprocessed_handshake_data,
          tls_open_handshake,
          tls_open_change_cipher_spec,
          tls_open_app_data,
          tls_write_app_data, // 加密明文的 app 数据并写到输出
          tls_dispatch_alert,
          tls_init_message, // 初始化一个 SSLMessage
          tls_finish_message,// 销毁一个 SSLMessage
          tls_add_message, // 将 SSLMessage 添加到输出
          tls_add_change_cipher_spec,
          tls_flush_flight, // 冲刷未发送的数据到 BIO
          tls_on_handshake_complete,
          tls_set_read_state, // 设置 SSL.s3->aead_write_ctx
          tls_set_write_state, // 设置 SSL.s3->aead_read_ctx
      };

      该数据结构设计的方法将会经常出现在后文当中

  • 通过SSL_CTX_set_info_callback()设置监听SSL连接状态对应的回调函数为: info_callback, 其调用的上层接口为: ConscryptEngine.onSSLStateChange()

  • 通过SSL_CTX_set_cert_cb()设置证书回调为: cert_cb, 其调用的上层接口为: ConscryptEngine.clientCertificateRequested() -> NativeSsl.chooseClientCertificate()

SSLContext.init()

上层调用SSLContext.init() -> OpenSSLContextImpl.engineInit(), 此时创建了SSLParametersImpl并记录了上层传递的X509KeyManagerX509TrustManager.

创建SSLParametersImpl时, 其clientSessionContext成员(类型是ClientSessionContext)

SSLContext.getSocketFactory()

上层调用SSLContext.getSocketFactory()获取SSLSocketFactory(实际是OpenSSLSocketFactoryImpl), OpenSSLSocketFactoryImpl保存了SSLParametersImpl

SSLSocketFactory.createSocket()

上层调用SSLSocketFactory(实际是OpenSSLSocketFactoryImpl)的createSocket()方法, 经过Platform.createEngineSocket()创建Java8EngineSocket(其父类是ConscryptEngineSocket), 其父类的newEngine方法创建ConscryptEngine, 此时将SSLParametersImpl克隆一份, 并传入. ConscryptEngine在构造时, 将通过newSsl() -> NativeSsl.newInstance()实例化NativeSsl, SSLParametersImpl将保存在NativeSsl.parameters, 其基本引用关系:
ConscryptEngineSocket.engine: Java8EngineSocket
ConscryptEngineSocket.engine.ssl: NativeSsl
ConscryptEngineSocket.engine.ssl.parameters: SSLParametersImpl

NativeSsl.newInstance()

  • 通过parameters.getSessionContext()获取了ClientSessionContext结构体地址
  • 调用ClientSessionContext.newSsl(), 传入ClientSessionContext, 创建SSL结构体, 其底层调用为NativeCrypto.SSL_new(), 此处底层调用到: NativeCrypto_SSL_new():
    • 通过to_SSL_CTX()还原指针ssl_ctx_address为: SSL_CTX
    • SSL_CTX传入SSL_new()以创建SSL结构体
    • 通过SSL_set_custom_verify()设置SSL的回调函数为:cert_verify_callback(), 该方法负责完成对服务端证书的验证, 在cert_verify_callback()中, 回调上层的NativeCrypto.verifyCertificateChain()
  • SSL结构体被传递给NativeCrypto.SSL_new(), 保存到NativeSsl.ssl, 从顶层向下:ConscryptEngineSocket.engine.ssl.ssl

BioWrapper 的创建

这里注意下 ConscryptEngine.networkBio(), 它的类型是BioWrapper, 是很重要的角色, 它负责处理所有 "加密 " 的数据, 通过NativeSsl.newBio()创建, 更底层的方法是:NativeCrypto.SSL_BIO_new():

  • 通过BIO_new_bio_pair()创建一对BIO:internal_bionetwork_bio, 此时创建的BIO.mehtod被设置为methods_biop, 因此BIO.mehtod->bread被设置为bio_read, 而BIO.mehtod->bwrite被设置为: bio_write
  • 通过SSL_set_bio()internal_bio配置给了SSL, 保存在(SSL.wbioSSL.rbio)
  • network_bio引用在BioWrapper.bio

ConscryptEngine.networkBio.bio非常重要, 它将在后文经常被提到.

ConscryptEngineSocket.startHandshake()

上层调用ConscryptEngineSocket.startHandshake()开始执行MTLS协商:

  • 创建 SSLInputStream并保存到ConscryptEngineSocket.in, 在此过程中
    • 通过ByteBuffer.allocateDirect分配与NativeSsl交互的DirectByteBuffer, 此处的 buffer 对于 JNI 来说是没有拷贝成本的. DirectByteBuffer保存到 ConscryptEngineSocket.in.fromEngine
    • 通过 ByteBuffer.allocate()分配与SocketInputStream进行通信的的HeapByteBuffer, HeapByteBuffer保存到 ConscryptEngineSocket.in.fromSocket
  • 创建 SSLOutputStream并保存到ConscryptEngineSocket.out, 在此过程中:
    • 通过 ByteBuffer.allocate()分配与SocketOutputStream进行通信的的HeapByteBuffer, HeapByteBuffer保存到 ConscryptEngineSocket.out.target
  • 调用SSLInputStream.processDataFromSocket()获取数据, 调用readFromSocket()SocketInputStream读取数据, 然后调用ConscryptEngine.unwrap()处理读取到的数据, 细节见解密小节

TLS协商

TLS协商的主循环:

c++ 复制代码
NativeCrypto_ENGINE_SSL_read_direct() [libjavacrypto.so] ->
    ::SSL_read() [libssl.so] ->
        ssl_read_impl() ->
            ::SSL_do_handshake() ->
                bssl::ssl_run_handshake()

该函数是一个循环, 该循环依次调用SSL_HANDSHAKE.ssl->do_handshake方法, 对于客户端, 该方法为ssl_client_handshake(), 在该方法中, 初始状态为state_start_connect()

state_start_connect(TLS)

state_start_connect对应的执行函数为do_start_connect():

  • 初始化SSL.s3结构体
  • 调用ssl_setup_key_shares()初始化 keys_shares
  • 调用ssl_setup_extension_permutation()初始化扩展
  • 调用ssl_encrypt_client_hello()加密 client_hello
  • 调用ssl_add_client_hello()将 client_hello 附加到消息池(SSL.s3->write_buffer中)
  • 返回状态为: ssl_hs_flush
    如果该状态顺利执行结束, 则向上返回给ssl_run_handshake()的结果是ssl_hs_flush, 因此ssl_run_handshake()继续执行SSL_HANDSHAKE.ssl->method->flush_flight方法, 该方法为kTLSProtocolMethod.flush_flighttls_flush_flight(), 进一步调用ssl_write_buffer_flush() -> tls_write_buffer_flush() -> BIO_write(), 将ssl.s3->write_buffer数据写入到SSL.wbioBIO中, 如前文所述, 该BIOinternal_bio, 其关联的对象为network_bio, 至此上层就可以通过NativeCrypto.ENGINE_SSL_read_BIO_direct()读取到数 BoringSSL 需要发送的数据了.

state_enter_early_data(TLS)

执行do_enter_early_data(), 基本没有做太多事情, 判断SSL_HANDSHAKE.early_data_offered为空就直接切换状态到state_read_server_hello

state_read_server_hello(TLS)

执行 do_read_server_hello(), 并读取TLS数据, 调用ssl_parse_server_hello()解析协议头, 获取版本号, 本文的案例显然是MTLSv1.3 , 则切换状态到 state_tls13

state_tls13(TLS)

执行do_tls13() -> tls13_client_handshake(), 该函数也是一个循环, 如果SSL_HANDSHAKE.tls13_state不为state_done就循环执行后续的状态切换, 初始状态为: state_read_hello_retry_request

state_read_hello_retry_request(0, TLSv1.3 start)

调用do_read_hello_retry_request():

  • 调用parse_server_hello_tls13()解析消息头

  • 调用SSL_get_cipher_by_value()获取服务器要求的算法: SSL_CIPHER, 本文的案例是TLS1_3_RFC_AES_128_GCM_SHA256, 协议 id 为 4865, 也就是:

    c 复制代码
    static constexpr SSL_CIPHER kCiphers[] = {
        // Cipher 1301
        {
          TLS1_3_RFC_AES_128_GCM_SHA256,
          "TLS_AES_128_GCM_SHA256",
          TLS1_3_CK_AES_128_GCM_SHA256,
          SSL_kGENERIC,
          SSL_aGENERIC,
          SSL_AES128GCM,
          SSL_AEAD,
          SSL_HANDSHAKE_MAC_SHA256,
        },
        ...
    };
  • SSL_CIPHER保存到SSL_HANDSHAKE.new_cipher

  • 切换状态到state_read_server_hello

state_read_server_hello(2)

调用do_read_server_hello(), 该函数做了很多工作:

  • 复制并保存 server_random
  • SSL_HANDSHAKE.new_cipher保存到SSK_HANDSHAKE.new_session->cipher
  • 调用ssl_ext_key_share_parse_serverhello()server_hello中解析出dhe_secret
  • 调用tls13_advance_key_schedule()派生秘钥并保存到SSL_HANDSHAKE.secret_
  • 调用tls13_derive_handshake_secrets()派生秘钥并保存到SSL_HANDSHAKE.client_handshake_secret_
  • 调用tls13_set_traffic_key()SSL_HANDSHAKE.client_handshake_secret_设置到SSL:
    • 调用ssl_cipher_get_evp_aead()确定EVP_AEAD, 该结构体主要为了确定秘钥长度
    • 调用ssl_session_get_digest()获取EVP_MD用于秘钥派生
    • 以"key"作为label调用hkdf_expand_label()派生秘钥
    • 以"iv"作为label调用hkdf_expand_label()派生初始向量
    • 调用SSLAEADContext::Create()并传入SSL, Cipher, 秘钥, iv, 创建SSLAEADContext
    • 调用SSL.method->set_read_statetls_set_read_state()设置SSLAEADContextSSL_HANDSHAKE.ssl->s3->aead_read_ctx
    • 保存SSL_HANDSHAKE.client_handshake_secret_SSL.s3->read_traffic_secret
    • 调用SSL.method->set_write_statetls_set_write_state()设置SSLAEADContextSSL_HANDSHAKE.ssl->s3->aead_write_ctx
    • 保存SSL_HANDSHAKE.client_handshake_secret_SSL.s3->write_traffic_secret

从此刻开始, 与服务器的数据往来都使用: SSL_HANDSHAKE.client_handshake_secret_ 加密

state_read_encrypted_extensions(3)

state_read_certificate_request(4)

调用do_read_certificate_request()

state_read_server_certificate(5)

调用do_read_server_certificate() -> tls13_process_certificate(), 从服务器返回的消息解析证书信息, 将证书保存到SSL_HANDSHAKE.new_session->certs

state_read_server_certificate_verify(6)

调用do_read_server_certificate_verify() -> ssl_verify_peer_cert() -> SSL_HANDSHAKE.ssl->config->custom_verify_callback, 根据前文, SSL_HANDSHAKE.ssl->config->custom_verify_callback已经被SSL_set_custom_verify()设置为:cert_verify_callback():

  • 调用SSL_get0_peer_certificates()获取上文刚解析到的服务器证书链:SSL_HANDSHAKE.new_session->certs

  • 调用上层: ConscryptEngine.verifyCertificateChain() -> Platform.checkServerTrusted() -> X509Trustmanager.checkServerTrusted(), 其中ConscryptEngine继承了NativeCrypto.SSLHandshakeCallbacks, X509Trustmanager.checkServerTrusted()为用户自定义的服务器证书验证方法.

state_read_server_finished(8)

调用do_read_server_finished():

  • 调用tls13_advance_key_schedule()更新加密算法
  • 调用tls13_derive_application_secrets() -> derive_secret("CLIENT_TRAFFIC_SECRET_0")派生交换凭据的秘钥, 保存到SSL_HANDSHAKE.client_traffic_secret_0_

注意, 此时的SSL_HANDSHAKE.client_traffic_secret_0_并没有作为会话秘钥, 这是因为还没有发送本地的凭据(这里我们只讨论 MTLSv1.3 )

state_send_end_of_early_data(9)

state_send_client_encrypted_extensions(10)

state_send_client_certificate(11)

调用do_send_client_certificate() -> SSL_HANDSHAKE.ssl->cert->cert_cb, 该回调被SSL_CTX_set_cert_cb()设置为external/conscrypt/common/src/jni/main/cpp/conscrypt/native_crypto.cc::cert_cb()将在如下环节被调用:

c++ 复制代码
bssl::ssl_run_handshake() ->
    bssl::ssl_client_handshake() ->
        bssl::tls13_client_handshake() ->
            bssl::do_send_client_certificate() ->
                cert_cb() [libjavacrypto.so]

如上文cert_cb()调用的上层接口为: ConscryptEngine.clientCertificateRequested() -> NativeSsl.chooseClientCertificate(), 其首先调用ConscryptEngineSocket.chooseClientAlias()选取本地的证书别名, 而ConscryptEngineSocket.chooseClientAlias()实际上是调用了X509KeyManager.chooseClientAlias(), 然后通过setCertificate()设置本地的证书&密钥, 参数为证书与秘钥的别名:

  • SSLParametersImpl中获取X509KeyManager

  • X509KeyManager中获取PrivateKey, 这是上层应用自定义的

  • X509KeyManager中获取所有可用的X509Certificate, 即X509Certificate[]

  • PrivateKey转换到OpenSSLKey, 这里调用: OpenSSLKey.fromPrivateKeyForTLSStackOnly() -> fromECPrivateKeyForTLSStackOnly() -> OpenSSLECPrivateKey.wrapJCAPrivateKeyForTLSStackOnly(), 首先调用OpenSSLECGroupContext.getInstance()ECParameterSpec获取OpenSSLECGroupContext. 创建OpenSSLKey, 调用NativeCrypto_getECPrivateKeyWrapper()产生底层的EVP_PKEY结构体, 传递OpenSSLECGroupContext作为参数:

    • 上层传递了秘钥的 Group 信息, 通过fromContextObject<EC_GROUP>()转换参数groupRef转换到EC_GROUP,

      • 通过ensure_engine_globals() -> init_engine_globals()初始化g_rsa_methodg_ecdsa_method:
        • 通过ENGINE_new()初始化g_engine
        • 通过ENGINE_set_RSA_method配置g_rsa_methodg_engine
        • 通过ENGINE_set_ECDSA_method配置g_ecdsa_methodg_engine
    • 通过EC_KEY_new_method()创建 EC_KEY结构体

    • 通过EC_KEY_set_group()配置 EC_KEYGroup信息

    • 构造KeyExData结构体, 将上层传递的javaKeyjobject 对象保存到刚刚创建的KeyExData.private_key字段

    • 通过EC_KEY_set_ex_data()配置KeyExData结构体到 EC_KEY

    • 通过EVP_PKEY_new()创建EVP_PKEY结构体

    • 通过EVP_PKEY_assign_EC_KEY()EC_KEY 结构体分配给 EVP_PKEY 结构体

    • 创建的 EVP_PKEY 结构体保存到OpenSSLKey.ctx中, 所以后续将OpenSSLKey传递给底层时, 获取ctx即可

      可以看到, OpenSSLKey.ctxEVP_PKEY, 其中:

      • EVP_PKEY.ameth被设置为ec_asn1_meth

        c 复制代码
        const EVP_PKEY_ASN1_METHOD ec_asn1_meth = {
            EVP_PKEY_EC,
            // 1.2.840.10045.2.1
            {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01},
            7,
            &ec_pkey_meth,
            eckey_pub_decode, // 从 DER 中解码 EC-PublicKey
            eckey_pub_encode, // 编码 EC-PublicKey 到 DER
            eckey_pub_cmp, // 比较两个公钥是否相同
            eckey_priv_decode, // 从 DER 解码 EC-PrivateKey
            eckey_priv_encode, // 编码 EC-PrivateKey 到 DER
            eckey_set1_tls_encodedpoint,
            eckey_get1_tls_encodedpoint,
            eckey_opaque,
            int_ec_size,
            ec_bits,
            ec_missing_parameters,
            ec_copy_parameters, // 拷贝 ECC 秘钥参数
            ec_cmp_parameters,  // 比较 ECC 秘钥参数
            int_ec_free,
        };

        其中ec_pkey_meth的类型为EVP_PKEY_METHOD, 作为EVP_PKEY的操作方法, 记录在EVP_PKEY_CTX.pmeth

        c 复制代码
        const EVP_PKEY_METHOD ec_pkey_meth = {
            EVP_PKEY_EC,
            pkey_ec_init,
            pkey_ec_copy,
            pkey_ec_cleanup,
            pkey_ec_keygen, // 生成 ECC 秘钥
            pkey_ec_sign, // 使用 EC-PrivateKey 执行签名
            pkey_ec_verify, // 使用 EC-PublicKey 验证签名
            pkey_ec_derive, // 使用 EC-PrivateKey 进行 ECDH
            pkey_ec_paramgen,
            pkey_ec_ctrl,
        };
      • EVP_PKEY.pkey被设置为EC_KEY ,其中:

        • EC_KEY.ecdsa_meth被设置为上文提及的: g_ecdsa_method, 其中:
          • EC_KEY.ecdsa_meth->signEcdsaMethodSign()
          • EC_KEY.ecdsa_meth->flagsECDSA_FLAG_OPAQUE
        • EC_KEY.ex_data.sk被设置为上文体积的: KeyExData结构体
  • 调用NativeCrypto.setLocalCertsAndPrivateKey()设置上文获取的X509Certificate(转换到byte[][])和OpenSSLKey到底层

  • 接下来看NativeCrypto.setLocalCertsAndPrivateKey, 改函数对应: NativeCrypto_setLocalCertsAndPrivateKey():

    • 获取SSL结构体
    • 通过fromContextObject()获取上层的EVP_PKEY结构体(从上文提及的OpenSSLKey.ctx中获取)
    • 依次获取上层传递的证书数据到CRYPTO_BUFFER结构体
    • 通过SSL_set_chain_and_key() -> cert_set_chain_and_key():
      • 通过check_leaf_cert_and_privkey()检查私钥与证书是否配对, 这里需要注意的是, 其中的一个检查步骤为ssl_compare_public_and_private_key(), 该函数中做了如下逻辑:

        c 复制代码
          if (EVP_PKEY_is_opaque(privkey)) {
            // We cannot check an opaque private key and have to trust that it
            // matches.
            return true;
          }

        这里因为EVP_PKEY.ameth->pkey_opaqueec_asn1_meth.pkey_opaque也就是eckey_opaque该函数通过eckey_opaque()判断EVP_PKEY.pkey也就是EC_KEY中的ECDSA_METHOD.flags是否设置了ECDSA_FLAG_OPAQUE, 上文提到EC_KEY.ecdsa_meth.flags已经设置了ECDSA_FLAG_OPAQUE, 所以这里不会报错

        • std::vector<CRYPTO_BUFFER*>的证书信息和EVP_PKEY的私钥信息配置给SSL结构体, 这里:
        • SSL.config->cert->chain保存数个CRYPTO_BUFFER
        • SSL.config->cert->privatekey保存EVP_PKEY
        • SSL.config->cert->key_method设置为空

state_send_client_certificate_verify(12)

执行do_send_client_certificate_verify() -> ssl_private_key_sign()中:

  • 执行setup_ctx() -> EVP_DigestSignInit() -> do_sigver_init() -> EVP_PKEY_CTX_new(), 因为EVP_PKEY.ameth->pkey_methodec_asn1_meth.pkey_method, 即ec_pkey_meth, 因此创建的EVP_PKEY_CTX.pmethec_pkey_meth, 创建的EVP_MD_CTXdo_sigver_init()中设置到EVP_MD_CTX.pctx
  • 执行EVP_DigestSign() -> EVP_DigestSignFinal() -> EVP_PKEY_sign() -> EVP_PKEY.pmeth->sign, 即上一步骤提及的ec_pkey_meth.signpkey_ec_sign(), 该方法先 从EVP_PKEY获取EC_KEY, 然后调用ECDSA_sign(), 传入EC_KEY, 如果设置了EC_KEY.ecdsa_meth->sign方法, 则调用, 如上文, 这里调用EcdsaMethodSign():
    • 通过EcKeyGetKey() -> EC_KEY_get_ex_dataEC_KEY中获取KeyExData结构体中的PrivateKey对象对应的jobject
    • 调用ecSignDigestWithPrivateKey(), 并将上一步骤获取的PrivateKey对象以及签名数据做为参数, 对应的上层调用为: CryptoUpcalls.ecSignDigestWithPrivateKey() -> signDigestWithPrivateKey():
      • 首先通过Signature.getInstance()获取默认签名算法, 然后调用Signature.initSign()设置PrivateKey, 执行 Signature.update()更新签名数据后, 执行Signature.sign()进行签名. 这里的 Signature和上层自定义的PrivateKey是配套的, 这部分内容, 在后续介绍 Security.Provider时再介绍.

state_complete_second_flight(13)

调用do_complete_second_flight():

  • 调用tls13_add_finished()发送协商结束消息
  • 调用tls13_set_traffic_key()设置SSL_HANDSHAKE.client_traffic_secret_0_为新的会话密钥

state_done(14, TLSv1.3 end)

state_finish_client_handshake(TLS)

调用do_finish_client_handshake(), 这里将SSL.s3->established_session设置为SSL.session

加密与解密

SSLOutputStream.write() 时的加密

上层调用: SSLOutputStream.write() -> writeInternal() -> ConscryptEngine.wrap(), ConscryptEngine.wrap()的语义是: 明文到加密数据的: 加密, 分两个步骤:

  • 首先是writePlaintextData() -> writePlaintextDataHeap() -> writePlaintextDataDirect()将数据往NativeSsl写, 更底层的操作是NativeCrypto.ENGINE_SSL_write_direct()
  • 然后是readEncryptedData() -> readEncryptedDataDirect()BioWrapper读取数据, 更底层的操作是NativeCrypto.ENGINE_SSL_read_BIO_direct()

从这里也可以总结出: 对于加密数据, 统一由 BioWrapper传递, 对于明文数据, 统一通过NativeSsl直接传递, 这里的BioWrapper也是后续 MTLS 协商结束后的 AES 加密通道.

c++ 复制代码
NativeCrypto.ENGINE_SSL_write_direct() -> 
    NativeCrypto_ENGINE_SSL_write_direct() [libjavacrypto.so] -> 
        ::SSL_write() [libssl.so]

此处, SSL.method->write_app_datakTLSProtocolMethod中的tls_write_app_data(), 因此

c++ 复制代码
    ::SSL_write() [libssl.so] -> 
        bssl::tls_write_app_data() ->
            bssl::do_tls_write()

此处分两个步骤:

  • 首先处理数据的加密:bssl::do_tls_write() -> bssl::tls_seal_record() -> bssl::tls_seal_scatter_record() -> bssl::do_seal_record(), 此处SSL.s3->aead_write_ctx为: SSLAEADContext, 因此:bssl::SSLAEADContext::SealScatter()被调用, 这里的ctx_EVP_AEAD_CTX, 而EVP_AEAD_CTX.aeadEVP_aead_aes_256_gcm_tls13, 因此EVP_AEAD_CTX.aead->seal_scatteraead_aes_gcm_tls13_seal_scatter() [libcrypto.so], 这里有硬件加速的部分, 简单列出调用的堆栈, 具体细节本文不做讨论:
c 复制代码
    EVP_AEAD_CTX_seal_scatter() [libcrypto.so] ->
        aead_aes_gcm_tls13_seal_scatter() ->
            aead_aes_gcm_seal_scatter() ->
                aead_aes_gcm_seal_scatter_impl() ->
                    CRYPTO_gcm128_encrypt()
  • 然后处理数据的发送: bssl::do_tls_write() -> bssl::tls_seal_record() -> bssl::tls_seal_scatter_record() -> bssl::ssl_write_buffer_flush() -> tls_write_buffer_flush() -> BIO_write(), 将ssl.s3->write_buffer数据写入到SSL.wbioBIO中, 如前文所述, 该BIOinternal_bio, 其关联的对象为network_bio, 至此上层就可以通过NativeCrypto.ENGINE_SSL_read_BIO_direct()读取到数 BoringSSL 需要发送的数据了.
c++ 复制代码
NativeCrypto.ENGINE_SSL_read_BIO_direct() ->
    NativeCrypto_ENGINE_SSL_read_BIO_direct() [libjavacrypto.so] -> 
        ::BIO_read() ->
            bio_read() [external/boringssl/src/crypto/bio/pair.c]

这里从network_bio读取internal_bio的加密数据.

SSLInputStream.read() 时的解密

上层调用: SSLInputStream.read() -> readUntilDataAvailable() -> processDataFromSocket() -> ConscryptEngine.unwrap(), ConscryptEngine.unwrap()的语义是: 加密数据到明文的: 解密 , ,unwrap()分两个步骤进行:

* 首先是writeEncryptedData() -> writeEncryptedDataHeap() -> writeEncryptedDataDirect()将数据往BioWrapper写, 更底层的操作是NativeCrypto.ENGINE_SSL_write_BIO_direct()

* 然后是readPlaintextData() -> readPlaintextDataDirect()NativeSsl读取数据, 更底层的操作是NativeCrypto.ENGINE_SSL_read_direct()

c++ 复制代码
NativeCrypto.ENGINE_SSL_write_BIO_direct() ->
    NativeCrypto_ENGINE_SSL_write_BIO_direct() [libjavacrypto.so] -> 
        ::BIO_write() ->
            bio_write() [external/boringssl/src/crypto/bio/pair.c]

这里通过network_bio写入加密数据到internal_bio

c++ 复制代码
NativeCrypto.ENGINE_SSL_read_direct() ->
    NativeCrypto_ENGINE_SSL_read_direct() [libjavacrypto.so] -> 
        ::SSL_read() ->
            ::SSL_peek() ->
                ssl_read_impl()

这里开始分两部分:

  • 首先处理数据的读取, 从BIO中读取上层写入的加密数据
c 复制代码
ssl_read_impl() ->
    bssl::ssl_handle_open_record() ->
        bssl::ssl_read_buffer_extend_to() ->
            tls_read_buffer_extend_to() ->
                BIO_read() [libcrypto.so] ->
                    bio_read()
  • 然后处理数据的解密, ssl_read_impl() -> tls_open_app_data() -> tls_open_record(), 此处SSL.s3->aead_read_ctx为: SSLAEADContext, 因此:bssl::SSLAEADContext::Open() -> EVP_AEAD_CTX_open() -> EVP_AEAD_CTX_open_gather()被调用, 这里的ctx_EVP_AEAD_CTX, 而EVP_AEAD_CTX.aeadEVP_aead_aes_256_gcm_tls13, 因此EVP_AEAD_CTX.aead->open_gatheraead_aes_gcm_open_gather() [libcrypto.so], 这里有硬件加速的部分, 简单列出调用的堆栈, 具体细节本文不做讨论:
c++ 复制代码
bssl::tls_open_record() ->
    bssl::SSLAEADContext::Open() ->
        EVP_AEAD_CTX_open() ->
            EVP_AEAD_CTX_open_gather() ->
                aead_aes_gcm_open_gather_impl() ->
                    CRYPTO_gcm128_decrypt_ctr32()
相关推荐
Amy187021118234 分钟前
基站光储能源系统 实现绿色基站 让每一度电都“光”芒四射
网络·能源
BioRunYiXue9 分钟前
甘油不够了,能用植物油保存菌种吗?
java·linux·运维·服务器·网络·人工智能·eclipse
好多渔鱼好多11 分钟前
【网络协议】P2P技术
网络·网络协议·p2p
小江的记录本16 分钟前
【JWT】JWT(JSON Web Token)结构化知识体系(完整版)
前端·网络·web安全·http·网络安全·json·安全架构
没有了遇见20 分钟前
Android 关于注入Js处理Android和H5 Js 交互问题
android
加密狗复制模拟21 分钟前
软件加密狗中时间限制机制的破解
开发语言·网络·安全·php·软件工程·个人开发
阿拉斯攀登31 分钟前
第 12 篇 RK 平台安卓驱动实战 5:SPI 设备驱动开发,以 SPI 屏 / Flash 为例
android·驱动开发·rk3568·瑞芯微·嵌入式驱动·安卓驱动·spi 设备驱动
Predestination王瀞潞31 分钟前
CentOS7虚拟机安装过程中没有打开网卡,ip addr无法查看es33这个情况下的解决方法
服务器·网络·tcp/ip
Predestination王瀞潞38 分钟前
Mysql忘记密码重置的方法
android·mysql·adb
闻哥40 分钟前
MySQL三大日志深度解析:redo log、undo log、binlog 原理与实战
android·java·jvm·数据库·mysql·adb·面试