Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.amazonq.lsp

import com.intellij.util.io.DigestUtil
import com.intellij.util.net.JdkProxyProvider
import com.intellij.util.net.ssl.CertificateManager
import org.apache.http.client.methods.RequestBuilder
import org.apache.http.conn.ssl.DefaultHostnameVerifier
import org.apache.http.impl.client.HttpClientBuilder
import org.apache.http.impl.client.SystemDefaultCredentialsProvider
import org.apache.http.impl.conn.SystemDefaultRoutePlanner
import software.aws.toolkits.core.utils.getLogger
import software.aws.toolkits.core.utils.warn
import java.net.URI
import java.security.KeyStore
import java.security.cert.CertPathBuilder
import java.security.cert.CertStore
import java.security.cert.Certificate
import java.security.cert.CollectionCertStoreParameters
import java.security.cert.PKIXBuilderParameters
import java.security.cert.PKIXCertPathBuilderResult
import java.security.cert.X509CertSelector
import java.security.cert.X509Certificate
import kotlin.collections.ifEmpty

object TrustChainUtil {

Check warning on line 28 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unused symbol

Object "TrustChainUtil" is never used
private val LOG = getLogger<TrustChainUtil>()

Check warning on line 29 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L29

Added line #L29 was not covered by tests

/**
* Build and validate the complete certificate chain
* @param certs The end-entity certificate
* @param trustAnchors The truststore containing trusted CA certificates
* @return The complete certificate chain
*/
fun resolveTrustChain(certs: Collection<X509Certificate>, trustAnchors: KeyStore): List<X509Certificate> {

Check notice on line 37 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Class member can have 'private' visibility

Function 'resolveTrustChain' could be private
// Create the selector for the certificate
val selector = X509CertSelector()
selector.certificate = certs.first()

Check warning on line 40 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L39-L40

Added lines #L39 - L40 were not covered by tests

// Create the parameters for path validation
val pkixParams = PKIXBuilderParameters(trustAnchors, selector)

Check warning on line 43 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L43

Added line #L43 was not covered by tests

// Disable CRL checking since we just want to build the path
pkixParams.isRevocationEnabled = false

Check warning on line 46 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L46

Added line #L46 was not covered by tests

// Create a CertStore containing the certificate we want to validate
val ccsp = CollectionCertStoreParameters(certs)
val certStore = CertStore.getInstance("Collection", ccsp)
pkixParams.addCertStore(certStore)

Check warning on line 51 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L49-L51

Added lines #L49 - L51 were not covered by tests

// Get the certification path
val builder = CertPathBuilder.getInstance("PKIX")
val result = builder.build(pkixParams) as PKIXCertPathBuilderResult
val certPath = result.certPath
val chain = (certPath.certificates as List<X509Certificate>).toMutableList()

Check warning on line 57 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L54-L57

Added lines #L54 - L57 were not covered by tests

// Add the trust anchor (root CA) to complete the chain
val trustAnchorCert = result.trustAnchor.trustedCert

Check warning on line 60 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L60

Added line #L60 was not covered by tests
if (trustAnchorCert != null) {
chain.add(trustAnchorCert)

Check warning on line 62 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L62

Added line #L62 was not covered by tests
}

return chain

Check warning on line 65 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L65

Added line #L65 was not covered by tests
}

fun getTrustChain(uri: URI): List<X509Certificate> {

Check warning on line 68 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unused symbol

Function "getTrustChain" is never used
val proxyProvider = JdkProxyProvider.getInstance()
var peerCerts: Array<Certificate> = emptyArray()
val verifierDelegate = DefaultHostnameVerifier()
val client = HttpClientBuilder.create()
.setRoutePlanner(SystemDefaultRoutePlanner(proxyProvider.proxySelector))
.setDefaultCredentialsProvider(SystemDefaultCredentialsProvider())
.setSSLHostnameVerifier { hostname, sslSession ->
peerCerts = sslSession.peerCertificates

Check warning on line 76 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L69-L76

Added lines #L69 - L76 were not covered by tests

verifierDelegate.verify(hostname, sslSession)

Check warning on line 78 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L78

Added line #L78 was not covered by tests
}
// prompt user via modal to accept certificate if needed; otherwise need to prompt separately prior to launching flare
.setSSLContext(CertificateManager.getInstance().sslContext)

Check warning on line 81 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L81

Added line #L81 was not covered by tests

// client request will fail if user did not accept cert
client.build().execute(RequestBuilder.options(uri).build())

Check warning on line 84 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L84

Added line #L84 was not covered by tests

val certificates = peerCerts as Array<X509Certificate>

Check warning on line 86 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L86

Added line #L86 was not covered by tests

// java default + custom system
// excluding leaf cert for case where user has both leaf and issuing CA as trusted roots
val allAccepted = CertificateManager.getInstance().trustManager.acceptedIssuers.toSet() - certificates.first()
val ks = keystoreFromCertificates(allAccepted)

Check warning on line 91 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L90-L91

Added lines #L90 - L91 were not covered by tests

// if this throws then there is a bug because it passed PKIX validation in apache client
val trustChain = try {
resolveTrustChain(certificates.toList(), ks)
} catch (e: Exception) {
LOG.warn(e) { "Passed Apache PKIX verification but could not build trust anchor via CertPathBuilder" }
emptyList()

Check warning on line 98 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L94-L98

Added lines #L94 - L98 were not covered by tests
}

// if trust chain is empty, then somehow user only trusts the leaf cert???
return trustChain.ifEmpty {
// so return the served certificate chain from the server and hope that works
certificates.toList()

Check warning on line 104 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L104

Added line #L104 was not covered by tests
}
}

private fun keystoreFromCertificates(certificates: Collection<X509Certificate>): KeyStore {
val ks = KeyStore.getInstance(KeyStore.getDefaultType())
ks.load(null, null)
certificates.forEachIndexed { index, cert ->
ks.setCertificateEntry(
cert.getSubjectX500Principal().toString() + "-" + DigestUtil.sha256Hex(cert.encoded),

Check notice on line 113 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Accessor call that can be replaced with property access syntax

Use of getter method instead of property access syntax
cert

Check warning on line 114 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L109-L114

Added lines #L109 - L114 were not covered by tests
)
}
return ks

Check warning on line 117 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/TrustChainUtil.kt#L116-L117

Added lines #L116 - L117 were not covered by tests
}
}
Loading