使用cronet,发起请求的时候,如果是ip的形式,会有报错:ERR_CERT_COMMON_NAME_INVALID,完整信息如下:
txt
System.err java.io.IOException: java.util.concurrent.ExecutionException: org.chromium.net.impl.NetworkExceptionImpl: Exception in CronetUrlRequest: net::ERR_CERT_COMMON_NAME_INVALID, ErrorCode=11, InternalErrorCode=-200, Retryable=false
System.err at com.google.net.cronet.okhttptransport.ResponseConverter.getFutureValue(ResponseConverter.java:216)
System.err at com.google.net.cronet.okhttptransport.ResponseConverter.toResponse(ResponseConverter.java:73)
System.err at com.google.net.cronet.okhttptransport.RequestResponseConverter$1.getResponse(RequestResponseConverter.java:124)
System.err at com.google.net.cronet.okhttptransport.RequestResponseConverter$CronetRequestAndOkHttpResponse.getResponse(RequestResponseConverter.java:156)
System.err at com.google.net.cronet.okhttptransport.CronetCallFactory$CronetCall.execute(CronetCallFactory.java:128)
System.err at org.yeshen.http3.MainActivity.httpGet$lambda$4(MainActivity.kt:140)
System.err at org.yeshen.http3.MainActivity.$r8$lambda$39qdYED_5mxuiTtol8rrKa_0_24(Unknown Source:0)
System.err at org.yeshen.http3.MainActivity$$ExternalSyntheticLambda2.run(D8$$SyntheticClass:0)
System.err at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1156)
System.err at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:651)
System.err at java.lang.Thread.run(Thread.java:1119)
定位出错位置的代码在ssl握手的地方。代码细节如下:
/src/net/socket/ssl_client_socket_impl.cc
c++
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 state
next_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;
}
/cronet/src/net/cert/cert_verify_proc.cc
https://chromium.googlesource.com/chromium/src/+/master/net/cert/cert_verify_proc.cc
c++
int CertVerifyProc::Verify(X509Certificate* cert,
const std::string& hostname,
const std::string& ocsp_response,
const std::string& sct_list,
int flags,
CertVerifyResult* verify_result,
const NetLogWithSource& net_log) {
CHECK(cert);
CHECK(verify_result);
net_log.BeginEvent(NetLogEventType::CERT_VERIFY_PROC, [&] {
return CertVerifyParams(cert, hostname, ocsp_response, sct_list, flags,
crl_set());
});
// CertVerifyProc's contract allows ::VerifyInternal() to wait on File I/O
// (such as the Windows registry or smart cards on all platforms) or may re-
// enter this code via extension hooks (such as smart card UI). To ensure
// threads are not starved or deadlocked, the base::ScopedBlockingCall below
// increments the thread pool capacity when this method takes too much time to
// run.
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
verify_result->Reset();
verify_result->verified_cert = cert;
int rv = VerifyInternal(cert, hostname, ocsp_response, sct_list, flags,
verify_result, net_log);
CHECK(verify_result->verified_cert);
if (rv == OK) {
CHECK_EQ(verify_result->verified_cert->cert_buffers().size(),
verify_result->public_key_hashes.size());
}
// Check for mismatched signature algorithms and unknown signature algorithms
// in the chain. Also fills in the has_* booleans for the digest algorithms
// present in the chain.
if (!InspectSignatureAlgorithmsInChain(verify_result)) {
verify_result->cert_status |= CERT_STATUS_INVALID;
rv = MapCertStatusToNetError(verify_result->cert_status);
}
if (!cert->VerifyNameMatch(hostname)) {
verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
rv = MapCertStatusToNetError(verify_result->cert_status);
}
if (verify_result->ocsp_result.response_status ==
bssl::OCSPVerifyResult::NOT_CHECKED) {
// If VerifyInternal did not record the result of checking stapled OCSP,
// do it now.
BestEffortCheckOCSP(ocsp_response, *verify_result->verified_cert,
&verify_result->ocsp_result);
}
// Check to see if the connection is being intercepted.
for (const auto& hash : verify_result->public_key_hashes) {
if (!crl_set()->IsKnownInterceptionKey(hash)) {
continue;
}
if (verify_result->cert_status & CERT_STATUS_REVOKED) {
// If the chain was revoked, and a known MITM was present, signal that
// with a more meaningful error message.
verify_result->cert_status |= CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED;
rv = MapCertStatusToNetError(verify_result->cert_status);
} else {
// Otherwise, simply signal informatively. Both statuses are not set
// simultaneously.
verify_result->cert_status |= CERT_STATUS_KNOWN_INTERCEPTION_DETECTED;
}
break;
}
std::vector<std::string> dns_names, ip_addrs;
cert->GetSubjectAltName(&dns_names, &ip_addrs);
if (HasNameConstraintsViolation(verify_result->public_key_hashes,
cert->subject().common_name,
dns_names,
ip_addrs)) {
verify_result->cert_status |= CERT_STATUS_NAME_CONSTRAINT_VIOLATION;
rv = MapCertStatusToNetError(verify_result->cert_status);
}
// Check for weak keys in the entire verified chain.
bool weak_key = ExaminePublicKeys(verify_result->verified_cert,
verify_result->is_issued_by_known_root);
if (weak_key) {
verify_result->cert_status |= CERT_STATUS_WEAK_KEY;
// Avoid replacing a more serious error, such as an OS/library failure,
// by ensuring that if verification failed, it failed with a certificate
// error.
if (rv == OK || IsCertificateError(rv))
rv = MapCertStatusToNetError(verify_result->cert_status);
}
if (verify_result->has_sha1)
verify_result->cert_status |= CERT_STATUS_SHA1_SIGNATURE_PRESENT;
// Flag certificates using weak signature algorithms.
bool sha1_allowed = (flags & VERIFY_ENABLE_SHA1_LOCAL_ANCHORS) &&
!verify_result->is_issued_by_known_root;
if (!sha1_allowed && verify_result->has_sha1) {
verify_result->cert_status |= CERT_STATUS_WEAK_SIGNATURE_ALGORITHM;
// Avoid replacing a more serious error, such as an OS/library failure,
// by ensuring that if verification failed, it failed with a certificate
// error.
if (rv == OK || IsCertificateError(rv))
rv = MapCertStatusToNetError(verify_result->cert_status);
}
// Flag certificates using too long validity periods.
if (verify_result->is_issued_by_known_root && HasTooLongValidity(*cert)) {
verify_result->cert_status |= CERT_STATUS_VALIDITY_TOO_LONG;
if (rv == OK)
rv = MapCertStatusToNetError(verify_result->cert_status);
}
// Flag certificates from publicly-trusted CAs that are issued to intranet
// hosts. These are not allowed per the CA/Browser Forum requirements.
//
// Validity period is checked first just for testing convenience; there's not
// a strong security reason to let validity period vs non-unique names take
// precedence.
if (verify_result->is_issued_by_known_root && IsHostnameNonUnique(hostname)) {
verify_result->cert_status |= CERT_STATUS_NON_UNIQUE_NAME;
// On Cronet, CERT_STATUS_NON_UNIQUE_NAME is recorded as a warning but not
// treated as an error, because consumers have tests that use certs with
// non-unique names. See b/337196170 (Google-internal).
#if !BUILDFLAG(CRONET_BUILD)
if (rv == OK) {
rv = MapCertStatusToNetError(verify_result->cert_status);
}
#endif // !BUILDFLAG(CRONET_BUILD)
}
// Record a histogram for per-verification usage of root certs.
if (rv == OK) {
RecordTrustAnchorHistogram(verify_result->public_key_hashes,
verify_result->is_issued_by_known_root);
}
net_log.EndEvent(NetLogEventType::CERT_VERIFY_PROC,
[&] { return verify_result->NetLogParams(rv); });
return rv;
}
注意到 CertVerifyProc::Verify 这里有一堆校验的逻辑,其中的关键就是:
c++
if (!cert->VerifyNameMatch(hostname)) {
verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
rv = MapCertStatusToNetError(verify_result->cert_status);
}
修改的话,比较简单,就是hostname是一个ip类型,就不做证书的校验即可。
c++
const bool host_is_ip_address = HostIsIPAddressNoBrackets(hostname);
if (!host_is_ip_address && !cert->VerifyNameMatch(hostname)) {
verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
rv = MapCertStatusToNetError(verify_result->cert_status);
}
玩~
这个系列其他的文章: