Skip to content

feature: add option for pinning root ca or pinning leaf ca #93#94

Open
weixuefeng wants to merge 8 commits into
diefferson:masterfrom
weixuefeng:master
Open

feature: add option for pinning root ca or pinning leaf ca #93#94
weixuefeng wants to merge 8 commits into
diefferson:masterfrom
weixuefeng:master

Conversation

@weixuefeng

Copy link
Copy Markdown

No description provided.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request renames the package to http_certificate_pinning_plus and introduces key features such as leaf or root certificate fingerprint validation, in-memory caching for successful checks, and coalescing of concurrent checks. The feedback suggests defensive checks on Android to handle empty validated certificate chains and catch CertificateException specifically for better error reporting. Additionally, it recommends forwarding HTTP headers in SecureHttpClient to prevent handshake failures on servers requiring specific headers.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +150 to +158
private fun getValidatedCertificateChain(host: String, serverCertificates: Array<Certificate>): List<X509Certificate> {
val certificateChain = serverCertificates.filterIsInstance<X509Certificate>().toTypedArray()
if (certificateChain.isEmpty()) {
throw CertificateException("No X509 certificates found in connection")
}
val trustManagerExtensions = this.getTrustManagerExtensions()
val authType = certificateChain.first().publicKey.algorithm
return trustManagerExtensions.checkServerTrusted(certificateChain, authType, host)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

trustManagerExtensions.checkServerTrusted can theoretically return null or an empty list depending on the platform's trust manager implementation or custom configurations. To prevent potential NoSuchElementException or NullPointerException when calling .first() or .last() on the returned chain, we should defensively verify that the validated chain is not null or empty.

  private fun getValidatedCertificateChain(host: String, serverCertificates: Array<Certificate>): List<X509Certificate> {
      val certificateChain = serverCertificates.filterIsInstance<X509Certificate>().toTypedArray()
      if (certificateChain.isEmpty()) {
          throw CertificateException("No X509 certificates found in connection")
      }
      val trustManagerExtensions = this.getTrustManagerExtensions()
      val authType = certificateChain.first().publicKey.algorithm
      val validatedChain = trustManagerExtensions.checkServerTrusted(certificateChain, authType, host)
      if (validatedChain.isNullOrEmpty()) {
          throw CertificateException("Validated certificate chain is empty")
      }
      return validatedChain
  }

Comment on lines +95 to +111
} catch (e: UnknownHostException) {
handler?.post {
result.error("NO_INTERNET", "No Internet Connection", e.localizedMessage)
}
} catch (e: SocketTimeoutException) {
handler?.post {
result.error("TIMEOUT", "Connection Timeout", e.localizedMessage)
}
} catch (e: IOException) {
handler?.post {
result.error("NETWORK_ERROR", "Network Error", e.localizedMessage)
}
} catch (e: Exception) {
handler?.post {
result.error("UNKNOWN_ERROR", "An Unknown Error Occurred", e.localizedMessage)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If checkServerTrusted throws a CertificateException (e.g., due to an expired certificate, untrusted CA, or hostname mismatch), it will currently be caught by the generic catch (e: Exception) block and return UNKNOWN_ERROR. Since this is a certificate validation failure, it should be caught specifically and return CONNECTION_NOT_SECURE to help developers easily diagnose certificate issues.

    } catch (e: CertificateException) {
      handler?.post {
        result.error("CONNECTION_NOT_SECURE", "Certificate validation failed", e.localizedMessage)
      }
    } catch (e: UnknownHostException) {
      handler?.post {
        result.error("NO_INTERNET", "No Internet Connection", e.localizedMessage)
      }
    } catch (e: SocketTimeoutException) {
      handler?.post {
        result.error("TIMEOUT", "Connection Timeout", e.localizedMessage)
      }
    } catch (e: IOException) {
      handler?.post {
        result.error("NETWORK_ERROR", "Network Error", e.localizedMessage)
      }
    } catch (e: Exception) {
      handler?.post {
        result.error("UNKNOWN_ERROR", "An Unknown Error Occurred", e.localizedMessage)
      }
    }

Comment on lines +109 to 111
final secureString = await HttpCertificatePinning.check(
serverURL: url.toString(),
headerHttp: {},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In _sendUnstreamed, the HTTP headers passed to the client request are not forwarded to HttpCertificatePinning.check. If the target server or API gateway requires specific headers (such as User-Agent, Authorization, or custom routing headers) to establish a connection or complete the handshake, the certificate check might fail. Forwarding the headers parameter will make the client more robust.

Suggested change
final secureString = await HttpCertificatePinning.check(
serverURL: url.toString(),
headerHttp: {},
final secureString = await HttpCertificatePinning.check(
serverURL: url.toString(),
headerHttp: headers,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant