@@ -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.valueas 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.valueas 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 (certin 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
}