Skip to content

Commit 2e45299

Browse files
committed
Refactored Ticket API example for code reuse
1 parent fe7fcb6 commit 2e45299

File tree

3 files changed

+17
-94
lines changed

3 files changed

+17
-94
lines changed

src/main/java/TicketAPI/TicketRequest.java

Lines changed: 9 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* 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.
44
*
55
* Qlik Sense Java Examples is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -18,9 +18,8 @@
1818

1919
package TicketAPI;
2020

21+
import Shared.Interfaces.AuthCertificate;
2122
import java.io.BufferedReader;
22-
import java.io.FileInputStream;
23-
import java.io.FileNotFoundException;
2423
import java.io.IOException;
2524
import java.io.InputStreamReader;
2625
import java.io.OutputStreamWriter;
@@ -32,124 +31,46 @@
3231
import java.net.http.HttpRequest;
3332
import java.net.http.HttpResponse;
3433
import java.security.KeyManagementException;
35-
import java.security.KeyStore;
3634
import java.security.KeyStoreException;
3735
import java.security.NoSuchAlgorithmException;
38-
import java.security.SecureRandom;
3936
import java.security.UnrecoverableKeyException;
4037
import java.security.cert.CertificateException;
41-
import java.security.cert.CertificateFactory;
42-
import java.security.cert.X509Certificate;
4338
import java.time.Duration;
4439
import java.util.Optional;
4540
import java.util.Properties;
4641
import java.util.concurrent.CompletableFuture;
4742
import javax.net.ssl.HttpsURLConnection;
48-
import javax.net.ssl.KeyManagerFactory;
49-
import javax.net.ssl.SSLContext;
5043
import javax.net.ssl.SSLSession;
51-
import javax.net.ssl.TrustManagerFactory;
5244

5345
/**
5446
* TicketRequest.java (UTF-8)
5547
* An example of a class that can request a Ticket from the Qlik Sense Proxy Service using
5648
* standard certificates exported from Qlik Sense without needing to convert them to
5749
* Java KeyStore (*.jks) certificates.
5850
*
59-
* @version 1.1
51+
* @version 1.2
6052
* @author Steven Jenkins De Haro
6153
*/
6254
public class TicketRequest {
6355

6456
private static final String XRFKEY = "1234567890123456"; // Xrfkey to prevent CSRF attacks.
65-
private static final String PROTOCOL = "TLS";
6657
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;
7059

7160
/**
7261
* Constructions a new {@see TicketRequest} instance to make Ticket requests.
7362
* @param hostname Hostname of the Qlik Sense server used for requests.
7463
* @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.
7865
*/
7966
public TicketRequest(String hostname, Optional<String> virtualProxyPrefix,
80-
String clientCertPath, char[] clientCertPassword,
81-
String rootCertPath) {
67+
AuthCertificate qlikCert) {
8268

8369
_apiUrl = String.format("https://%1$s:4243/qps%2$s/ticket?xrfkey=%3$s",
8470
hostname, virtualProxyPrefix.isPresent() ? "/" + virtualProxyPrefix.get() : "", XRFKEY);
85-
_clientCertPath = clientCertPath;
86-
_clientCertPassword = clientCertPassword;
87-
_rootCertPath = rootCertPath;
71+
_qlikCert = qlikCert;
8872
}
8973

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-
15374
/**
15475
* Requests a ticket from the Qlik Sense Proxy Service that is valid for one minute.
15576
* @param userDirectory Directory associated with user.
@@ -181,7 +102,7 @@ public String getTicket(String userDirectory, String userId)
181102
*/
182103
HttpsURLConnection.setDefaultHostnameVerifier((String hostname, SSLSession session) -> true);
183104

184-
connection.setSSLSocketFactory(getSSLContext().getSocketFactory());
105+
connection.setSSLSocketFactory(_qlikCert.getSSLContext().getSocketFactory());
185106
connection.setDoOutput(true);
186107
connection.setDoInput(true);
187108
connection.setConnectTimeout(30000);
@@ -242,7 +163,7 @@ public CompletableFuture<String> getTicketAsync(String userDirectory, String use
242163
var client = HttpClient.newBuilder()
243164
.connectTimeout(Duration.ofSeconds(30))
244165
.followRedirects(Redirect.NORMAL)
245-
.sslContext(getSSLContext())
166+
.sslContext(_qlikCert.getSSLContext())
246167
.build();
247168

248169
var request = HttpRequest.newBuilder()

src/main/java/TicketAPI/TicketRequestDemo.form

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<Form version="1.3" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
44
<Properties>
5-
<Property name="defaultCloseOperation" type="int" value="3"/>
5+
<Property name="defaultCloseOperation" type="int" value="2"/>
66
<Property name="title" type="java.lang.String" value="Ticket Request Demo"/>
77
<Property name="resizable" type="boolean" value="false"/>
88
</Properties>

src/main/java/TicketAPI/TicketRequestDemo.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* 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.
44
*
55
* Qlik Sense Java Examples is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
1818

1919
package TicketAPI;
2020

21+
import Shared.QlikAuthCertificate;
2122
import java.io.File;
2223
import java.io.IOException;
2324
import java.nio.file.Paths;
@@ -40,7 +41,7 @@
4041
* TicketRequestDemo.java (UTF-8)
4142
* A GUI demo that can request tickets using certificates exported from Qlik Sense.
4243
*
43-
* @version 1.1
44+
* @version 1.2
4445
* @author Steven Jenkins De Haro
4546
*/
4647
public class TicketRequestDemo extends javax.swing.JFrame {
@@ -89,7 +90,7 @@ private void initComponents() {
8990
jLabel4 = new javax.swing.JLabel();
9091
jSeparator1 = new javax.swing.JSeparator();
9192

92-
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
93+
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
9394
setTitle("Ticket Request Demo");
9495
setResizable(false);
9596
addWindowListener(new java.awt.event.WindowAdapter() {
@@ -272,12 +273,13 @@ private void btnClientBrowseActionPerformed(java.awt.event.ActionEvent evt) {//G
272273

273274
private void btnRequestActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRequestActionPerformed
274275
char[] clientPass = txtClientPassword.getPassword();
276+
var cert = new QlikAuthCertificate(txtClientCertPath.getText(), clientPass,
277+
txtRootCertPath.getText());
275278
var request = new TicketRequest(txtHostname.getText(),
276279
txtVirtualProxy.getText().trim().equals("") ?
277280
Optional.empty() :
278281
Optional.of(txtVirtualProxy.getText().trim()),
279-
txtClientCertPath.getText(), clientPass,
280-
txtRootCertPath.getText());
282+
cert);
281283

282284
// Avoids requesting a new ticket while there is one that is not expired.
283285
if (null != executorService && !executorService.isTerminated()) {

0 commit comments

Comments
 (0)