Skip to content

Commit a83835c

Browse files
srinivasankavithavepanimas
authored andcommitted
Add certs to keystore for configuring ssl certs in http client.
1 parent 845f1ba commit a83835c

File tree

7 files changed

+148
-8
lines changed

7 files changed

+148
-8
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package com.intellij.lang.jsgraphql.ide.editor;
2+
3+
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.model.GraphQLConfigCertificate;
4+
import org.jetbrains.annotations.NotNull;
5+
import org.jetbrains.annotations.Nullable;
6+
7+
import java.io.IOException;
8+
import java.io.InputStream;
9+
import java.io.UnsupportedEncodingException;
10+
import java.nio.charset.Charset;
11+
import java.nio.charset.StandardCharsets;
12+
import java.nio.file.Files;
13+
import java.nio.file.Path;
14+
import java.security.*;
15+
import java.security.cert.Certificate;
16+
import java.security.cert.CertificateException;
17+
import java.security.cert.CertificateFactory;
18+
import java.security.spec.InvalidKeySpecException;
19+
import java.security.spec.PKCS8EncodedKeySpec;
20+
import java.util.Base64;
21+
import java.util.Collection;
22+
23+
public class GraphQLIntrospectionSSLBuilder {
24+
@NotNull
25+
public static KeyStore makeKeyStore(final Path certPath, final Path keyPath, final GraphQLConfigCertificate.Encoding format) throws UnsupportedEncodingException {
26+
CertificateFactory certificateFactory;
27+
try {
28+
certificateFactory = CertificateFactory.getInstance("X509");
29+
} catch (CertificateException e) {
30+
throw new IllegalStateException(e);
31+
}
32+
33+
java.security.cert.Certificate[] certChain;
34+
try (InputStream inputStream = Files.newInputStream(certPath)) {
35+
Collection<? extends Certificate> certCollection = certificateFactory.generateCertificates(inputStream);
36+
certChain = certCollection.toArray(new java.security.cert.Certificate[certCollection.size()]);
37+
} catch (CertificateException | IOException e) {
38+
throw new RuntimeException(e);
39+
}
40+
41+
// We only support PEM format for now
42+
if (format != GraphQLConfigCertificate.Encoding.PEM) {
43+
throw new UnsupportedEncodingException("Format needs to be specified as PEM");
44+
}
45+
46+
KeyStore keyStore;
47+
try {
48+
PrivateKey privateKey = generatePEMPrivateKey(keyPath);
49+
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
50+
// Not supporting keystore and key passwords yet
51+
keyStore.load(null, null);
52+
keyStore.setKeyEntry("1", privateKey, null, certChain);
53+
} catch (NoSuchAlgorithmException | KeyStoreException | IOException | CertificateException e) {
54+
throw new IllegalStateException(e);
55+
}
56+
return keyStore;
57+
}
58+
59+
@Nullable
60+
private static PrivateKey generatePEMPrivateKey(final Path keyPath) throws IOException {
61+
62+
String key = new String(Files.readAllBytes(keyPath), Charset.defaultCharset());
63+
String privateKeyPEM = key
64+
.replace("-----BEGIN PRIVATE KEY-----", "")
65+
.replaceAll(System.lineSeparator(), "")
66+
.replace("-----END PRIVATE KEY-----", "");
67+
68+
byte[] decoded = Base64.getDecoder().decode(privateKeyPEM);
69+
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
70+
PrivateKey privateKey;
71+
try {
72+
KeyFactory kf = KeyFactory.getInstance("RSA");
73+
privateKey = kf.generatePrivate(keySpec);
74+
} catch (NoSuchAlgorithmException e) {
75+
throw new IllegalStateException(e);
76+
} catch (InvalidKeySpecException e) {
77+
try {
78+
KeyFactory kf = KeyFactory.getInstance("EC");
79+
privateKey = kf.generatePrivate(keySpec);
80+
} catch (InvalidKeySpecException e1) {
81+
try {
82+
KeyFactory kf = KeyFactory.getInstance("DSA");
83+
privateKey = kf.generatePrivate(keySpec);
84+
} catch (InvalidKeySpecException | NoSuchAlgorithmException e2) {
85+
throw new RuntimeException(e2);
86+
}
87+
} catch (NoSuchAlgorithmException e1) {
88+
throw new IllegalStateException(e1);
89+
}
90+
}
91+
92+
return privateKey;
93+
}
94+
}

src/main/com/intellij/lang/jsgraphql/ide/introspection/GraphQLIntrospectionService.java

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@
1616
import com.intellij.lang.jsgraphql.ide.notifications.GraphQLNotificationUtil;
1717
import com.intellij.lang.jsgraphql.ide.project.GraphQLUIProjectService;
1818
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.GraphQLConfigManager;
19-
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.model.GraphQLConfigEndpoint;
20-
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.model.GraphQLConfigVariableAwareEndpoint;
19+
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.model.*;
2120
import com.intellij.lang.jsgraphql.schema.GraphQLKnownTypes;
2221
import com.intellij.lang.jsgraphql.schema.GraphQLRegistryInfo;
2322
import com.intellij.lang.jsgraphql.schema.GraphQLSchemaInfo;
@@ -80,6 +79,7 @@
8079
import org.jetbrains.annotations.Nullable;
8180

8281
import java.io.IOException;
82+
8383
import java.security.GeneralSecurityException;
8484
import java.security.KeyManagementException;
8585
import java.security.KeyStoreException;
@@ -88,6 +88,10 @@
8888
import java.util.Collections;
8989
import java.util.List;
9090
import java.util.Map;
91+
import java.nio.file.Path;
92+
import java.nio.file.Paths;
93+
import java.security.*;
94+
import java.security.cert.CertificateException;
9195

9296
import static com.intellij.lang.jsgraphql.ide.project.GraphQLUIProjectService.setHeadersFromOptions;
9397

@@ -99,6 +103,7 @@ public class GraphQLIntrospectionService implements Disposable {
99103

100104
private GraphQLIntrospectionTask latestIntrospection = null;
101105
private final Project myProject;
106+
private Project myProject1;
102107

103108
public static GraphQLIntrospectionService getInstance(@NotNull Project project) {
104109
return ServiceManager.getService(project, GraphQLIntrospectionService.class);
@@ -186,14 +191,38 @@ public static HttpPost createRequest(@NotNull GraphQLConfigVariableAwareEndpoint
186191
}
187192

188193
@NotNull
189-
public CloseableHttpClient createHttpClient() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
194+
public CloseableHttpClient createHttpClient() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, IOException, UnrecoverableKeyException, CertificateException {
190195
HttpClientBuilder builder = HttpClients.custom();
191196
builder.setRedirectStrategy(LaxRedirectStrategy.INSTANCE);
192197

193198
if (PropertiesComponent.getInstance(myProject).isTrueValue(GRAPHQL_TRUST_ALL_HOSTS)) {
194-
builder
195-
.setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build())
196-
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
199+
Map<VirtualFile, GraphQLConfigData> configEntries =
200+
GraphQLConfigManager.getService(myProject).getConfigurationsByPath();
201+
GraphQLConfigSecurity sslConfig = configEntries.get(myProject.getBaseDir()).sslConfiguration;
202+
if (sslConfig != null) {
203+
if (sslConfig.clientCertificate.path == null || sslConfig.clientCertificateKey.path == null) {
204+
throw new RuntimeException("Path needs to be specified for the key and certificate");
205+
}
206+
Path certPath = Paths.get(sslConfig.clientCertificate.path);
207+
Path keyPath = Paths.get(sslConfig.clientCertificateKey.path);
208+
GraphQLConfigCertificate.Encoding keyFormat = sslConfig.clientCertificateKey.format;
209+
210+
KeyStore store = GraphQLIntrospectionSSLBuilder.makeKeyStore(certPath, keyPath, keyFormat);
211+
builder
212+
.setSSLContext(
213+
new SSLContextBuilder()
214+
.loadTrustMaterial(null, TrustAllStrategy.INSTANCE)
215+
.loadKeyMaterial(store, null)
216+
.build())
217+
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
218+
} else {
219+
builder
220+
.setSSLContext(
221+
new SSLContextBuilder()
222+
.loadTrustMaterial(null, TrustAllStrategy.INSTANCE)
223+
.build())
224+
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
225+
}
197226
}
198227
return builder.build();
199228
}

src/main/com/intellij/lang/jsgraphql/ide/project/graphqlconfig/GraphQLConfigManager.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.intellij.lang.jsgraphql.ide.notifications.GraphQLNotificationUtil;
2626
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.model.GraphQLConfigData;
2727
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.model.GraphQLConfigEndpoint;
28+
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.model.GraphQLConfigSecurity;
2829
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.model.GraphQLResolvedConfigData;
2930
import com.intellij.lang.jsgraphql.ide.findUsages.GraphQLFindUsagesUtil;
3031
import com.intellij.lang.jsgraphql.psi.GraphQLFile;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.intellij.lang.jsgraphql.ide.project.graphqlconfig.model;
2+
3+
public class GraphQLConfigCertificate {
4+
public enum Encoding {PEM}
5+
6+
public String path;
7+
public Encoding format = Encoding.PEM;
8+
}

src/main/com/intellij/lang/jsgraphql/ide/project/graphqlconfig/model/GraphQLConfigData.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,5 @@
1313
* graphql-config root config
1414
*/
1515
public class GraphQLConfigData extends GraphQLResolvedConfigData {
16-
1716
public Map<String, GraphQLResolvedConfigData> projects;
18-
1917
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.intellij.lang.jsgraphql.ide.project.graphqlconfig.model;
2+
3+
/**
4+
* graphql-config security config
5+
*/
6+
public class GraphQLConfigSecurity {
7+
public GraphQLConfigCertificate clientCertificate;
8+
public GraphQLConfigCertificate clientCertificateKey;
9+
}

src/main/com/intellij/lang/jsgraphql/ide/project/graphqlconfig/model/GraphQLResolvedConfigData.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ public class GraphQLResolvedConfigData {
2424

2525
public Map<String, Object> extensions;
2626

27+
public GraphQLConfigSecurity sslConfiguration;
2728
}

0 commit comments

Comments
 (0)