背景,荣耀9x,混淆才会出这个问题。
[ERROR:ssl_client_socket_impl.cc(981)] handshake failed; returned -1, SSL error code 1, net_error -2
NetError.java
int SSLClientSocketImpl::DoHandshake() {crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);int rv = SSL_do_handshake(ssl_.get());int net_error = OK;if (rv <= 0) {int ssl_error = SSL_get_error(ssl_.get(), rv);if (ssl_error == SSL_ERROR_WANT_X509_LOOKUP && !send_client_cert_) {return ERR_SSL_CLIENT_AUTH_CERT_NEEDED;}if (ssl_error == SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) {DCHECK(client_private_key_);DCHECK_NE(kSSLClientSocketNoPendingResult, signature_result_);next_handshake_state_ = STATE_HANDSHAKE;return ERR_IO_PENDING;}if (ssl_error == SSL_ERROR_WANT_CERTIFICATE_VERIFY) {DCHECK(cert_verifier_request_);next_handshake_state_ = STATE_HANDSHAKE;return ERR_IO_PENDING;}OpenSSLErrorInfo error_info;net_error = MapLastOpenSSLError(ssl_error, err_tracer, &error_info);if (net_error == ERR_IO_PENDING) {// If not done, stay in this statenext_handshake_state_ = STATE_HANDSHAKE;return ERR_IO_PENDING;}LOG(ERROR) << "handshake failed; returned " << rv << ", SSL error code "<< ssl_error << ", net_error " << net_error;NetLogOpenSSLError(net_log_, NetLogEventType::SSL_HANDSHAKE_ERROR,net_error, ssl_error, error_info);}next_handshake_state_ = STATE_HANDSHAKE_COMPLETE;return net_error;
}
int SSLClientSocketImpl::MapLastOpenSSLError(int ssl_error,const crypto::OpenSSLErrStackTracer& tracer,OpenSSLErrorInfo* info) {int net_error = MapOpenSSLErrorWithDetails(ssl_error, tracer, info);if (ssl_error == SSL_ERROR_SSL &&ERR_GET_LIB(info->error_code) == ERR_LIB_SSL) {// TLS does not provide an alert for missing client certificates, so most// servers send a generic handshake_failure alert. Detect this case by// checking if we have received a CertificateRequest but sent no// certificate. See https://crbug.com/646567.if (ERR_GET_REASON(info->error_code) ==SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE &&certificate_requested_ && send_client_cert_ && !client_cert_) {net_error = ERR_BAD_SSL_CLIENT_AUTH_CERT;}// Per spec, access_denied is only for client-certificate-based access// control, but some buggy firewalls use it when blocking a page. To avoid a// confusing error, map it to a generic protocol error if no// CertificateRequest was sent. See https://crbug.com/630883.if (ERR_GET_REASON(info->error_code) == SSL_R_TLSV1_ALERT_ACCESS_DENIED &&!certificate_requested_) {net_error = ERR_SSL_PROTOCOL_ERROR;}// This error is specific to the client, so map it here.if (ERR_GET_REASON(info->error_code) ==SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS) {net_error = ERR_SSL_CLIENT_AUTH_NO_COMMON_ALGORITHMS;}}return net_error;
}
int MapOpenSSLErrorWithDetails(int err,const crypto::OpenSSLErrStackTracer& tracer,OpenSSLErrorInfo* out_error_info) {*out_error_info = OpenSSLErrorInfo();switch (err) {case SSL_ERROR_WANT_READ:case SSL_ERROR_WANT_WRITE:return ERR_IO_PENDING;case SSL_ERROR_EARLY_DATA_REJECTED:return ERR_EARLY_DATA_REJECTED;case SSL_ERROR_SYSCALL:PLOG(ERROR) << "OpenSSL SYSCALL error, earliest error code in ""error queue: "<< ERR_peek_error();return ERR_FAILED;case SSL_ERROR_SSL:// Walk down the error stack to find an SSL or net error.while (true) {OpenSSLErrorInfo error_info;error_info.error_code =ERR_get_error_line(&error_info.file, &error_info.line);if (error_info.error_code == 0) {// Map errors to ERR_SSL_PROTOCOL_ERROR by default, reporting the most// recent error in |*out_error_info|.return ERR_SSL_PROTOCOL_ERROR;}*out_error_info = error_info;if (ERR_GET_LIB(error_info.error_code) == ERR_LIB_SSL) {return MapOpenSSLErrorSSL(error_info.error_code);}if (ERR_GET_LIB(error_info.error_code) == OpenSSLNetErrorLib()) {// Net error codes are negative but encoded in OpenSSL as positive// numbers.return -ERR_GET_REASON(error_info.error_code);}}default:// TODO(joth): Implement full mapping.LOG(WARNING) << "Unknown OpenSSL error " << err;return ERR_SSL_PROTOCOL_ERROR;}
}
只有MapOpenSSLErrorWithDetails能返回-2,所以是openssl返回的错误。
抓包对比报文的差异:
似乎是客户端不识别证书。
没有思路,使用二分试错法。
结果:keep bouncycastle库可以解决问题,之前没有keep这个库却移除了系统实现。
问题代码:
//set up the security provider
private fun setupBouncyCastle() {// Web3j will set up a provider when it's used for the first time.val provider = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) ?: returnif (provider.javaClass == BouncyCastleProvider::class.java) {return}//There is a possibility the bouncy castle registered by android may not have all ciphers//so we substitute with the one bundled in the app.Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME)Security.insertProviderAt(BouncyCastleProvider(), 1)
}