@@ -19,6 +19,7 @@ import com.google.gson.Gson
19
19
import com.google.gson.GsonBuilder
20
20
import com.intellij.ide.plugins.PluginManagerCore
21
21
import com.intellij.openapi.components.Service
22
+ import com.intellij.openapi.diagnostic.Logger
22
23
import com.intellij.openapi.extensions.PluginId
23
24
import com.intellij.openapi.util.SystemInfo
24
25
import okhttp3.OkHttpClient
@@ -36,7 +37,6 @@ import java.net.URL
36
37
import java.nio.file.Path
37
38
import java.security.KeyFactory
38
39
import java.security.KeyStore
39
- import java.security.PrivateKey
40
40
import java.security.cert.CertificateException
41
41
import java.security.cert.CertificateFactory
42
42
import java.security.cert.X509Certificate
@@ -47,6 +47,7 @@ import java.util.Base64
47
47
import java.util.Locale
48
48
import java.util.UUID
49
49
import javax.net.ssl.HostnameVerifier
50
+ import javax.net.ssl.KeyManager
50
51
import javax.net.ssl.KeyManagerFactory
51
52
import javax.net.ssl.SNIHostName
52
53
import javax.net.ssl.SSLContext
@@ -251,53 +252,56 @@ class CoderRestClient(
251
252
}
252
253
}
253
254
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
+ }
258
283
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())
292
290
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
+ }
295
295
296
296
val sslContext = SSLContext .getInstance(" TLS" )
297
297
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
+ }
300
302
303
+ fun coderSocketFactory (settings : CoderSettingsState ) : SSLSocketFactory {
304
+ val sslContext = SSLContextFromPEMs (settings.tlsCertPath, settings.tlsKeyPath, settings.tlsCAPath)
301
305
if (settings.tlsAlternateHostname.isBlank()) {
302
306
return sslContext.socketFactory
303
307
}
@@ -393,12 +397,11 @@ class AlternateNameSSLSocketFactory(private val delegate: SSLSocketFactory, priv
393
397
}
394
398
395
399
class CoderHostnameVerifier (private val alternateName : String ) : HostnameVerifier {
400
+ val logger = Logger .getInstance(CoderRestClientService ::class .java.simpleName)
396
401
override fun verify (host : String , session : SSLSession ): Boolean {
397
402
if (alternateName.isEmpty()) {
398
- println (" using default hostname verifier, alternateName is empty" )
399
403
return OkHostnameVerifier .verify(host, session)
400
404
}
401
- println (" Looking for alternate hostname: $alternateName " )
402
405
val certs = session.peerCertificates ? : return false
403
406
for (cert in certs) {
404
407
if (cert !is X509Certificate ) {
@@ -411,13 +414,12 @@ class CoderHostnameVerifier(private val alternateName: String) : HostnameVerifie
411
414
continue
412
415
}
413
416
val hostname = entry[1 ] as String
414
- println (" Found cert hostname: $hostname " )
417
+ logger.debug (" Found cert hostname: $hostname " )
415
418
if (hostname.lowercase(Locale .getDefault()) == alternateName) {
416
419
return true
417
420
}
418
421
}
419
422
}
420
- println (" No matching hostname found" )
421
423
return false
422
424
}
423
425
}
0 commit comments