|
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