|
1 | 1 | /** |
2 | 2 | * This file is part of Qlik Sense Java Examples <https://github.com/StevenJDH/Qlik-Sense-Java-Examples>. |
3 | | - * Copyright (C) 2019 Steven Jenkins De Haro. |
| 3 | + * Copyright (C) 2019-2020 Steven Jenkins De Haro. |
4 | 4 | * |
5 | 5 | * Qlik Sense Java Examples is free software: you can redistribute it and/or modify |
6 | 6 | * it under the terms of the GNU General Public License as published by |
|
18 | 18 |
|
19 | 19 | package TicketAPI; |
20 | 20 |
|
| 21 | +import Shared.Interfaces.AuthCertificate; |
21 | 22 | import java.io.BufferedReader; |
22 | | -import java.io.FileInputStream; |
23 | | -import java.io.FileNotFoundException; |
24 | 23 | import java.io.IOException; |
25 | 24 | import java.io.InputStreamReader; |
26 | 25 | import java.io.OutputStreamWriter; |
|
32 | 31 | import java.net.http.HttpRequest; |
33 | 32 | import java.net.http.HttpResponse; |
34 | 33 | import java.security.KeyManagementException; |
35 | | -import java.security.KeyStore; |
36 | 34 | import java.security.KeyStoreException; |
37 | 35 | import java.security.NoSuchAlgorithmException; |
38 | | -import java.security.SecureRandom; |
39 | 36 | import java.security.UnrecoverableKeyException; |
40 | 37 | import java.security.cert.CertificateException; |
41 | | -import java.security.cert.CertificateFactory; |
42 | | -import java.security.cert.X509Certificate; |
43 | 38 | import java.time.Duration; |
44 | 39 | import java.util.Optional; |
45 | 40 | import java.util.Properties; |
46 | 41 | import java.util.concurrent.CompletableFuture; |
47 | 42 | import javax.net.ssl.HttpsURLConnection; |
48 | | -import javax.net.ssl.KeyManagerFactory; |
49 | | -import javax.net.ssl.SSLContext; |
50 | 43 | import javax.net.ssl.SSLSession; |
51 | | -import javax.net.ssl.TrustManagerFactory; |
52 | 44 |
|
53 | 45 | /** |
54 | 46 | * TicketRequest.java (UTF-8) |
55 | 47 | * An example of a class that can request a Ticket from the Qlik Sense Proxy Service using |
56 | 48 | * standard certificates exported from Qlik Sense without needing to convert them to |
57 | 49 | * Java KeyStore (*.jks) certificates. |
58 | 50 | * |
59 | | - * @version 1.1 |
| 51 | + * @version 1.2 |
60 | 52 | * @author Steven Jenkins De Haro |
61 | 53 | */ |
62 | 54 | public class TicketRequest { |
63 | 55 |
|
64 | 56 | private static final String XRFKEY = "1234567890123456"; // Xrfkey to prevent CSRF attacks. |
65 | | - private static final String PROTOCOL = "TLS"; |
66 | 57 | private final String _apiUrl; |
67 | | - private final String _clientCertPath; // Client certificate with private key. |
68 | | - private final char[] _clientCertPassword; |
69 | | - private final String _rootCertPath; // Required in this example because Qlik Sense certs are used. |
| 58 | + private final AuthCertificate _qlikCert; |
70 | 59 |
|
71 | 60 | /** |
72 | 61 | * Constructions a new {@see TicketRequest} instance to make Ticket requests. |
73 | 62 | * @param hostname Hostname of the Qlik Sense server used for requests. |
74 | 63 | * @param virtualProxyPrefix Optional prefix of virtual proxy if one is used. |
75 | | - * @param clientCertPath Path to a PKCS#12 client certificate. |
76 | | - * @param clientCertPassword Password for the PKCS#12 certificate. |
77 | | - * @param rootCertPath Path to the X.509 root certificate of the client certificate. |
| 64 | + * @param qlikCert Qlik certificate used for authentication. |
78 | 65 | */ |
79 | 66 | public TicketRequest(String hostname, Optional<String> virtualProxyPrefix, |
80 | | - String clientCertPath, char[] clientCertPassword, |
81 | | - String rootCertPath) { |
| 67 | + AuthCertificate qlikCert) { |
82 | 68 |
|
83 | 69 | _apiUrl = String.format("https://%1$s:4243/qps%2$s/ticket?xrfkey=%3$s", |
84 | 70 | hostname, virtualProxyPrefix.isPresent() ? "/" + virtualProxyPrefix.get() : "", XRFKEY); |
85 | | - _clientCertPath = clientCertPath; |
86 | | - _clientCertPassword = clientCertPassword; |
87 | | - _rootCertPath = rootCertPath; |
| 71 | + _qlikCert = qlikCert; |
88 | 72 | } |
89 | 73 |
|
90 | | - /** |
91 | | - * Configures the needed certificates to validate the identity of the HTTPS |
92 | | - * server against a list of trusted certificates and to authenticate to the |
93 | | - * HTTPS server using a private key. |
94 | | - * @return An initialized secure socket context for TLS/SSL connections. |
95 | | - * @throws KeyStoreException |
96 | | - * @throws IOException |
97 | | - * @throws CertificateException |
98 | | - * @throws NoSuchAlgorithmException |
99 | | - * @throws UnrecoverableKeyException |
100 | | - * @throws KeyManagementException |
101 | | - */ |
102 | | - private SSLContext getSSLContext() |
103 | | - throws KeyStoreException, IOException, CertificateException, |
104 | | - NoSuchAlgorithmException, UnrecoverableKeyException, |
105 | | - KeyManagementException { |
106 | | - |
107 | | - var kmf = KeyManagerFactory.getInstance("SunX509"); |
108 | | - var tmf = TrustManagerFactory.getInstance("SunX509"); |
109 | | - var keyStore = getKeyStore(_clientCertPath, _clientCertPassword, false); |
110 | | - var trustStore = getKeyStore(_rootCertPath, null, true); |
111 | | - var context = SSLContext.getInstance(PROTOCOL); |
112 | | - |
113 | | - kmf.init(keyStore, _clientCertPassword); |
114 | | - tmf.init(trustStore); |
115 | | - context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); |
116 | | - |
117 | | - return context; |
118 | | - } |
119 | | - |
120 | | - /** |
121 | | - * Gets a new instance of a {@see KeyStore} in PKCS#12 Format configured with |
122 | | - * standard certificates that are loaded from a file. |
123 | | - * @param certPath Path to a PKCS#12 certificate or to a X.509 public key only certificate. |
124 | | - * @param certPassword Password for the PKCS#12 certificate. |
125 | | - * @param isClientCheck Set true if KeyStore is used for client check, and false if not. |
126 | | - * @return A new KeyStore instance configured with standard certificates. |
127 | | - * @throws KeyStoreException |
128 | | - * @throws FileNotFoundException |
129 | | - * @throws IOException |
130 | | - * @throws NoSuchAlgorithmException |
131 | | - * @throws CertificateException |
132 | | - */ |
133 | | - private KeyStore getKeyStore(String certPath, char[] certPassword, boolean isClientCheck) |
134 | | - throws KeyStoreException, FileNotFoundException, IOException, |
135 | | - NoSuchAlgorithmException, CertificateException { |
136 | | - |
137 | | - var ks = KeyStore.getInstance("PKCS12"); |
138 | | - |
139 | | - try (var inputStream = new FileInputStream(certPath)) { |
140 | | - if (true == isClientCheck) { |
141 | | - var certificateFactoryX509 = CertificateFactory.getInstance("X.509"); |
142 | | - var caCertificate = (X509Certificate) certificateFactoryX509.generateCertificate(inputStream); |
143 | | - ks.load(null, null); |
144 | | - ks.setCertificateEntry("ca-certificate", caCertificate); |
145 | | - } else { |
146 | | - ks.load(inputStream, certPassword); |
147 | | - } |
148 | | - } |
149 | | - |
150 | | - return ks; |
151 | | - } |
152 | | - |
153 | 74 | /** |
154 | 75 | * Requests a ticket from the Qlik Sense Proxy Service that is valid for one minute. |
155 | 76 | * @param userDirectory Directory associated with user. |
@@ -181,7 +102,7 @@ public String getTicket(String userDirectory, String userId) |
181 | 102 | */ |
182 | 103 | HttpsURLConnection.setDefaultHostnameVerifier((String hostname, SSLSession session) -> true); |
183 | 104 |
|
184 | | - connection.setSSLSocketFactory(getSSLContext().getSocketFactory()); |
| 105 | + connection.setSSLSocketFactory(_qlikCert.getSSLContext().getSocketFactory()); |
185 | 106 | connection.setDoOutput(true); |
186 | 107 | connection.setDoInput(true); |
187 | 108 | connection.setConnectTimeout(30000); |
@@ -242,7 +163,7 @@ public CompletableFuture<String> getTicketAsync(String userDirectory, String use |
242 | 163 | var client = HttpClient.newBuilder() |
243 | 164 | .connectTimeout(Duration.ofSeconds(30)) |
244 | 165 | .followRedirects(Redirect.NORMAL) |
245 | | - .sslContext(getSSLContext()) |
| 166 | + .sslContext(_qlikCert.getSSLContext()) |
246 | 167 | .build(); |
247 | 168 |
|
248 | 169 | var request = HttpRequest.newBuilder() |
|
0 commit comments