@@ -19,6 +19,7 @@ import com.google.gson.Gson
1919import com.google.gson.GsonBuilder
2020import com.intellij.ide.plugins.PluginManagerCore
2121import com.intellij.openapi.components.Service
22+ import com.intellij.openapi.diagnostic.Logger
2223import com.intellij.openapi.extensions.PluginId
2324import com.intellij.openapi.util.SystemInfo
2425import okhttp3.OkHttpClient
@@ -36,7 +37,6 @@ import java.net.URL
3637import java.nio.file.Path
3738import java.security.KeyFactory
3839import java.security.KeyStore
39- import java.security.PrivateKey
4040import java.security.cert.CertificateException
4141import java.security.cert.CertificateFactory
4242import java.security.cert.X509Certificate
@@ -47,6 +47,7 @@ import java.util.Base64
4747import java.util.Locale
4848import java.util.UUID
4949import javax.net.ssl.HostnameVerifier
50+ import javax.net.ssl.KeyManager
5051import javax.net.ssl.KeyManagerFactory
5152import javax.net.ssl.SNIHostName
5253import javax.net.ssl.SSLContext
@@ -251,53 +252,56 @@ class CoderRestClient(
251252 }
252253}
253254
254- fun coderSocketFactory (settings : CoderSettingsState ) : SSLSocketFactory {
255- if (settings.tlsCertPath.isBlank() || settings.tlsKeyPath.isBlank()) {
256- return SSLSocketFactory .getDefault() as SSLSocketFactory
257- }
255+ fun SSLContextFromPEMs (certPath : String , keyPath : String , caPath : String ) : SSLContext {
256+ var km: Array <KeyManager >? = null
257+ if (certPath.isNotBlank() && keyPath.isNotBlank()) {
258+ val certificateFactory = CertificateFactory .getInstance(" X.509" )
259+ val certInputStream = FileInputStream (expandPath(certPath))
260+ val certChain = certificateFactory.generateCertificates(certInputStream)
261+ certInputStream.close()
262+
263+ // ideally we would use something like PemReader from BouncyCastle, but
264+ // BC is used by the IDE. This makes using BC very impractical since
265+ // type casting will mismatch due to the different class loaders.
266+ val privateKeyPem = File (expandPath(keyPath)).readText()
267+ val start: Int = privateKeyPem.indexOf(" -----BEGIN PRIVATE KEY-----" )
268+ val end: Int = privateKeyPem.indexOf(" -----END PRIVATE KEY-----" , start)
269+ val pemBytes: ByteArray = Base64 .getDecoder().decode(
270+ privateKeyPem.substring(start + " -----BEGIN PRIVATE KEY-----" .length, end)
271+ .replace(" \\ s+" .toRegex(), " " )
272+ )
273+
274+ val privateKey = try {
275+ val kf = KeyFactory .getInstance(" RSA" )
276+ val keySpec = PKCS8EncodedKeySpec (pemBytes)
277+ kf.generatePrivate(keySpec)
278+ } catch (e: InvalidKeySpecException ) {
279+ val kf = KeyFactory .getInstance(" EC" )
280+ val keySpec = PKCS8EncodedKeySpec (pemBytes)
281+ kf.generatePrivate(keySpec)
282+ }
258283
259- val certificateFactory = CertificateFactory .getInstance(" X.509" )
260- val certInputStream = FileInputStream (expandPath(settings.tlsCertPath))
261- val certChain = certificateFactory.generateCertificates(certInputStream)
262- certInputStream.close()
263-
264- // ideally we would use something like PemReader from BouncyCastle, but
265- // BC is used by the IDE. This makes using BC very impractical since
266- // type casting will mismatch due to the different class loaders.
267- val privateKeyPem = File (expandPath(settings.tlsKeyPath)).readText()
268- val start: Int = privateKeyPem.indexOf(" -----BEGIN PRIVATE KEY-----" )
269- val end: Int = privateKeyPem.indexOf(" -----END PRIVATE KEY-----" , start)
270- val pemBytes: ByteArray = Base64 .getDecoder().decode(
271- privateKeyPem.substring(start + " -----BEGIN PRIVATE KEY-----" .length, end)
272- .replace(" \\ s+" .toRegex(), " " )
273- )
274-
275- var privateKey : PrivateKey
276- try {
277- val kf = KeyFactory .getInstance(" RSA" )
278- val keySpec = PKCS8EncodedKeySpec (pemBytes)
279- privateKey = kf.generatePrivate(keySpec)
280- } catch (e: InvalidKeySpecException ) {
281- val kf = KeyFactory .getInstance(" EC" )
282- val keySpec = PKCS8EncodedKeySpec (pemBytes)
283- privateKey = kf.generatePrivate(keySpec)
284- }
285-
286- val keyStore = KeyStore .getInstance(KeyStore .getDefaultType())
287- keyStore.load(null )
288- certChain.withIndex().forEach {
289- keyStore.setCertificateEntry(" cert${it.index} " , it.value as X509Certificate )
290- }
291- keyStore.setKeyEntry(" key" , privateKey, null , certChain.toTypedArray())
284+ val keyStore = KeyStore .getInstance(KeyStore .getDefaultType())
285+ keyStore.load(null )
286+ certChain.withIndex().forEach {
287+ keyStore.setCertificateEntry(" cert${it.index} " , it.value as X509Certificate )
288+ }
289+ keyStore.setKeyEntry(" key" , privateKey, null , certChain.toTypedArray())
292290
293- val keyManagerFactory = KeyManagerFactory .getInstance(KeyManagerFactory .getDefaultAlgorithm())
294- keyManagerFactory.init (keyStore, null )
291+ val keyManagerFactory = KeyManagerFactory .getInstance(KeyManagerFactory .getDefaultAlgorithm())
292+ keyManagerFactory.init (keyStore, null )
293+ km = keyManagerFactory.keyManagers
294+ }
295295
296296 val sslContext = SSLContext .getInstance(" TLS" )
297297
298- val trustManagers = coderTrustManagers(settings.tlsCAPath)
299- sslContext.init (keyManagerFactory.keyManagers, trustManagers, null )
298+ val trustManagers = coderTrustManagers(caPath)
299+ sslContext.init (km, trustManagers, null )
300+ return sslContext
301+ }
300302
303+ fun coderSocketFactory (settings : CoderSettingsState ) : SSLSocketFactory {
304+ val sslContext = SSLContextFromPEMs (settings.tlsCertPath, settings.tlsKeyPath, settings.tlsCAPath)
301305 if (settings.tlsAlternateHostname.isBlank()) {
302306 return sslContext.socketFactory
303307 }
@@ -393,12 +397,11 @@ class AlternateNameSSLSocketFactory(private val delegate: SSLSocketFactory, priv
393397}
394398
395399class CoderHostnameVerifier (private val alternateName : String ) : HostnameVerifier {
400+ val logger = Logger .getInstance(CoderRestClientService ::class .java.simpleName)
396401 override fun verify (host : String , session : SSLSession ): Boolean {
397402 if (alternateName.isEmpty()) {
398- println (" using default hostname verifier, alternateName is empty" )
399403 return OkHostnameVerifier .verify(host, session)
400404 }
401- println (" Looking for alternate hostname: $alternateName " )
402405 val certs = session.peerCertificates ? : return false
403406 for (cert in certs) {
404407 if (cert !is X509Certificate ) {
@@ -411,13 +414,12 @@ class CoderHostnameVerifier(private val alternateName: String) : HostnameVerifie
411414 continue
412415 }
413416 val hostname = entry[1 ] as String
414- println (" Found cert hostname: $hostname " )
417+ logger.debug (" Found cert hostname: $hostname " )
415418 if (hostname.lowercase(Locale .getDefault()) == alternateName) {
416419 return true
417420 }
418421 }
419422 }
420- println (" No matching hostname found" )
421423 return false
422424 }
423425}
0 commit comments