Android Boring SSL

2023-10-18 19:16
文章标签 android ssl boring

本文主要是介绍Android Boring SSL,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • 前期设置
    • SSLContext.getInstance(“TLS”)
    • SSLContext.init()
    • SSLContext.getSocketFactory()
    • SSLSocketFactory.createSocket()
    • NativeSsl.newInstance()
    • BioWrapper 的创建
    • ConscryptEngineSocket.startHandshake()
  • TLS协商
    • state_start_connect(TLS)
    • state_enter_early_data(TLS)
    • state_read_server_hello(TLS)
    • state_tls13(TLS)
    • state_read_hello_retry_request(0, TLSv1.3 start)
    • state_read_server_hello(2)
    • state_read_encrypted_extensions(3)
    • state_read_certificate_request(4)
    • state_read_server_certificate(5)
    • state_read_server_certificate_verify(6)
    • state_read_server_finished(8)
    • state_send_end_of_early_data(9)
    • state_send_client_encrypted_extensions(10)
    • state_send_client_certificate(11)
    • state_send_client_certificate_verify(12)
    • state_complete_second_flight(13)
    • state_done(14, TLSv1.3 end)
    • state_finish_client_handshake(TLS)
  • 加密与解密
    • SSLOutputStream.write() 时的加密
    • SSLInputStream.read() 时的解密

前期设置

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:

      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的定义:

      static const SSL_PROTOCOL_METHOD kTLSProtocolMethod = {false /* is_dtls */,tls_new, // 初始化 SSL, 创建 SSL_HANDSHAKEtls_free,tls_get_message, // PEEK 一个 SSLMessagetls_next_message, // 读取一个 SSLMessagetls_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, // 初始化一个 SSLMessagetls_finish_message,// 销毁一个 SSLMessagetls_add_message, // 将 SSLMessage 添加到输出tls_add_change_cipher_spec,tls_flush_flight, // 冲刷未发送的数据到 BIOtls_on_handshake_complete,tls_set_read_state, // 设置 SSL.s3->aead_write_ctxtls_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协商的主循环:

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, 也就是:

    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()将在如下环节被调用:

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

        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-PublicKeyeckey_pub_encode, // 编码 EC-PublicKey 到 DEReckey_pub_cmp, // 比较两个公钥是否相同eckey_priv_decode, // 从 DER 解码 EC-PrivateKeyeckey_priv_encode, // 编码 EC-PrivateKey 到 DEReckey_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

        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 进行 ECDHpkey_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(), 该函数中做了如下逻辑:
          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 加密通道.

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(), 因此

    ::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], 这里有硬件加速的部分, 简单列出调用的堆栈, 具体细节本文不做讨论:
    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 需要发送的数据了.
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()

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

NativeCrypto.ENGINE_SSL_read_direct() ->NativeCrypto_ENGINE_SSL_read_direct() [libjavacrypto.so] -> ::SSL_read() ->::SSL_peek() ->ssl_read_impl()

这里开始分两部分:

  • 首先处理数据的读取, 从BIO中读取上层写入的加密数据
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], 这里有硬件加速的部分, 简单列出调用的堆栈, 具体细节本文不做讨论:
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()

这篇关于Android Boring SSL的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/234613

相关文章

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

android应用中res目录说明

Android应用的res目录是一个特殊的项目,该项目里存放了Android应用所用的全部资源,包括图片、字符串、颜色、尺寸、样式等,类似于web开发中的public目录,js、css、image、style。。。。 Android按照约定,将不同的资源放在不同的文件夹中,这样可以方便的让AAPT(即Android Asset Packaging Tool , 在SDK的build-tools目

Android fill_parent、match_parent、wrap_content三者的作用及区别

这三个属性都是用来适应视图的水平或者垂直大小,以视图的内容或尺寸为基础的布局,比精确的指定视图的范围更加方便。 1、fill_parent 设置一个视图的布局为fill_parent将强制性的使视图扩展至它父元素的大小 2、match_parent 和fill_parent一样,从字面上的意思match_parent更贴切一些,于是从2.2开始,两个属性都可以使用,但2.3版本以后的建议使

Android Environment 获取的路径问题

1. 以获取 /System 路径为例 /*** Return root of the "system" partition holding the core Android OS.* Always present and mounted read-only.*/public static @NonNull File getRootDirectory() {return DIR_ANDR

消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法

消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法   消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法 [转载]原地址:http://blog.csdn.net/x605940745/article/details/17911115 消除SDK更新时的“

Android逆向(反调,脱壳,过ssl证书脚本)

文章目录 总结 基础Android基础工具 定位关键代码页面activity定位数据包参数定位堆栈追踪 编写反调脱壳好用的脚本过ssl证书校验抓包反调的脚本打印堆栈bilibili反调的脚本 总结 暑假做了两个月的Android逆向,记录一下自己学到的东西。对于app渗透有了一些思路。 这两个月主要做的是代码分析,对于分析完后的持久化等没有学习。主要是如何反编译源码,如何找到