]> WPIA git - gigi.git/blob - tests/club/wpia/gigi/ping/TestSSL.java
fix: ResultSet.getDate is often wrong as it fetches day-precision times
[gigi.git] / tests / club / wpia / gigi / ping / TestSSL.java
1 package club.wpia.gigi.ping;
2
3 import static org.junit.Assert.*;
4 import static org.junit.Assume.*;
5
6 import java.io.ByteArrayInputStream;
7 import java.io.IOException;
8 import java.net.Socket;
9 import java.net.URLEncoder;
10 import java.security.GeneralSecurityException;
11 import java.security.KeyManagementException;
12 import java.security.KeyPair;
13 import java.security.NoSuchAlgorithmException;
14 import java.security.Principal;
15 import java.security.PrivateKey;
16 import java.security.SecureRandom;
17 import java.security.cert.CertificateException;
18 import java.security.cert.CertificateFactory;
19 import java.security.cert.X509Certificate;
20 import java.sql.SQLException;
21 import java.util.Arrays;
22 import java.util.Date;
23 import java.util.HashMap;
24 import java.util.LinkedList;
25 import java.util.List;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28
29 import javax.net.ssl.KeyManager;
30 import javax.net.ssl.SSLContext;
31 import javax.net.ssl.SSLServerSocket;
32 import javax.net.ssl.SSLServerSocketFactory;
33 import javax.net.ssl.TrustManager;
34 import javax.net.ssl.X509KeyManager;
35 import javax.net.ssl.X509TrustManager;
36 import javax.security.auth.x500.X500Principal;
37
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 import org.junit.runners.Parameterized;
41 import org.junit.runners.Parameterized.Parameter;
42 import org.junit.runners.Parameterized.Parameters;
43
44 import club.wpia.gigi.GigiApiException;
45 import club.wpia.gigi.database.DatabaseConnection;
46 import club.wpia.gigi.database.DatabaseConnection.Link;
47 import club.wpia.gigi.dbObjects.Certificate;
48 import club.wpia.gigi.dbObjects.CertificateProfile;
49 import club.wpia.gigi.dbObjects.Digest;
50 import club.wpia.gigi.dbObjects.Job;
51 import club.wpia.gigi.dbObjects.User;
52 import club.wpia.gigi.dbObjects.Certificate.CSRType;
53 import club.wpia.gigi.testUtils.IOUtils;
54 import club.wpia.gigi.testUtils.PingTest;
55 import club.wpia.gigi.testUtils.TestEmailReceiver.TestMail;
56 import club.wpia.gigi.util.SimpleSigner;
57
58 @RunWith(Parameterized.class)
59 public class TestSSL extends PingTest {
60
61     @Parameters(name = "self-signed = {0}")
62     public static Iterable<Object[]> genParams() throws IOException {
63         return Arrays.asList(new Object[] {
64                 true
65         }, new Object[] {
66                 false
67         });
68
69     }
70
71     @Parameter
72     public Boolean self = false;
73
74     public abstract static class AsyncTask<T> {
75
76         T res;
77
78         Thread runner;
79
80         Exception ex;
81
82         public T join() throws InterruptedException {
83             runner.join();
84             if (ex != null) {
85                 throw new Error(ex);
86             }
87             return res;
88         }
89
90         public void start() {
91             runner = new Thread() {
92
93                 @Override
94                 public void run() {
95                     try {
96                         res = AsyncTask.this.run();
97                     } catch (Exception e) {
98                         ex = e;
99                     }
100                 }
101             };
102             runner.start();
103         }
104
105         public abstract T run() throws Exception;
106
107     }
108
109     private KeyPair kp;
110
111     private X509Certificate c;
112
113     @Test(timeout = 70000)
114     public void sslAndMailSuccess() throws IOException, InterruptedException, SQLException, GeneralSecurityException, GigiApiException {
115         testEmailAndSSL(0, 0, true);
116     }
117
118     @Test(timeout = 70000)
119     public void sslWongTypeAndMailSuccess() throws IOException, InterruptedException, SQLException, GeneralSecurityException, GigiApiException {
120         testEmailAndSSL(1, 0, true);
121     }
122
123     @Test(timeout = 70000)
124     public void sslOneMissingAndMailSuccess() throws IOException, InterruptedException, SQLException, GeneralSecurityException, GigiApiException {
125         testEmailAndSSL(2, 0, true);
126     }
127
128     @Test(timeout = 70000)
129     public void sslBothMissingAndMailSuccess() throws IOException, InterruptedException, SQLException, GeneralSecurityException, GigiApiException {
130         testEmailAndSSL(3, 0, true);
131     }
132
133     @Test(timeout = 70000)
134     public void sslWrongTypeAndMailFail() throws IOException, InterruptedException, SQLException, GeneralSecurityException, GigiApiException {
135         testEmailAndSSL(1, 1, false);
136     }
137
138     private void testEmailAndSSL(int sslVariant, int emailVariant, boolean successMail) throws IOException, InterruptedException, SQLException, GeneralSecurityException, GigiApiException {
139         try (Link link = DatabaseConnection.newLink(false)) {
140             testEmailAndSSLWithLink(sslVariant, emailVariant, successMail);
141         }
142     }
143
144     /**
145      * @param sslVariant
146      *            <ul>
147      *            <li>0= all valid</li>
148      *            <li>1= wrong type</li>
149      *            <li>2= one server missing</li>
150      *            <li>3= both servers missing</li>
151      *            </ul>
152      * @param emailVariant
153      * @param successSSL
154      * @param successMail
155      * @throws IOException
156      * @throws InterruptedException
157      * @throws SQLException
158      * @throws GeneralSecurityException
159      * @throws GigiApiException
160      */
161
162     private void testEmailAndSSLWithLink(int sslVariant, int emailVariant, boolean successMail) throws IOException, InterruptedException, SQLException, GeneralSecurityException, GigiApiException {
163         String test = getTestProps().getProperty("domain.local");
164         assumeNotNull(test);
165         Matcher m = initailizeDomainForm();
166         String value = m.group(2);
167
168         if (self) {
169             createCertificateSelf(test, sslVariant == 1 ? "clientAuth" : "serverAuth", value);
170         } else {
171             createCertificate(test, CertificateProfile.getByName(sslVariant == 1 ? "client" : "server"));
172         }
173
174         final SSLServerSocket sss = createSSLServer(kp.getPrivate(), c);
175         int port = sss.getLocalPort();
176         final SSLServerSocket sss2 = createSSLServer(kp.getPrivate(), c);
177         int port2 = sss2.getLocalPort();
178         if (sslVariant == 3 || sslVariant == 2) {
179             sss2.close();
180             if (sslVariant == 3) {
181                 sss.close();
182             }
183         }
184         String content = "adddomain&newdomain=" + URLEncoder.encode(test, "UTF-8") + //
185                 "&emailType=y&email=2&SSLType=y" + //
186                 "&ssl-type-0=direct&ssl-port-0=" + port + //
187                 "&ssl-type-1=direct&ssl-port-1=" + port2 + //
188                 "&ssl-type-2=direct&ssl-port-2=" + //
189                 "&ssl-type-3=direct&ssl-port-3=" + //
190                 "&adddomain&csrf=" + csrf;
191         String p2 = sendDomainForm(content);
192         boolean firstSucceeds = sslVariant != 0 && sslVariant != 2;
193         AsyncTask<Boolean> ass = new AsyncTask<Boolean>() {
194
195             @Override
196             public Boolean run() throws Exception {
197                 return acceptSSLServer(sss);
198             }
199         };
200         ass.start();
201         System.out.println(port + " and " + port2 + " ready");
202         System.err.println(port + " and " + port2 + " ready");
203         boolean accept2 = acceptSSLServer(sss2);
204         boolean accept1 = ass.join();
205         // assertTrue(firstSucceeds ^ accept1);
206         boolean secondsSucceeds = sslVariant != 0;
207         // assertTrue(secondsSucceeds ^ accept2);
208
209         TestMail mail = getMailReceiver().receive();
210         if (emailVariant == 0) {
211             mail.verify();
212         }
213         waitForPings(3);
214
215         String newcontent = IOUtils.readURL(get(p2));
216         Pattern pat = Pattern.compile("<td>ssl</td>\\s*<td>success</td>");
217         Matcher matcher = pat.matcher(newcontent);
218         assertTrue(newcontent, firstSucceeds ^ matcher.find());
219         assertTrue(newcontent, secondsSucceeds ^ matcher.find());
220         assertFalse(newcontent, matcher.find());
221         pat = Pattern.compile("<td>email</td>\\s*<td>success</td>");
222         assertTrue(newcontent, !successMail ^ pat.matcher(newcontent).find());
223     }
224
225     private void createCertificate(String test, CertificateProfile profile) throws GeneralSecurityException, IOException, SQLException, InterruptedException, GigiApiException {
226         kp = generateKeypair();
227         String csr = generatePEMCSR(kp, "CN=" + test);
228         User u = User.getById(id);
229         Certificate c = new Certificate(u, u, Certificate.buildDN("CN", test), Digest.SHA256, csr, CSRType.CSR, profile);
230         Job j = c.issue(null, "2y", u);
231         await(j);
232         this.c = c.cert();
233     }
234
235     private void createCertificateSelf(String test, String eku, String tok) throws GeneralSecurityException, IOException, SQLException, InterruptedException, GigiApiException {
236         kp = generateKeypair();
237         HashMap<String, String> name = new HashMap<>();
238         name.put("CN", "");
239         name.put("OU", tok);
240
241         Date from = new Date();
242         Date to = new Date(from.getTime() + 1000 * 60 * 60 * 2);
243         List<Certificate.SubjectAlternateName> l = new LinkedList<>();
244
245         byte[] cert = SimpleSigner.generateCert(kp.getPublic(), kp.getPrivate(), name, new X500Principal(SimpleSigner.genX500Name(name).getEncoded()), l, from, to, Digest.SHA256, eku);
246         c = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(new ByteArrayInputStream(cert));
247     }
248
249     private boolean acceptSSLServer(SSLServerSocket sss) throws IOException {
250         try (Socket s = sss.accept()) {
251             s.getOutputStream().write('b');
252             s.getOutputStream().close();
253             return true;
254         } catch (IOException e) {
255             return false;
256         }
257     }
258
259     private SSLServerSocket createSSLServer(final PrivateKey priv, final X509Certificate cert) throws Error, IOException {
260         SSLContext sc;
261         try {
262             sc = SSLContext.getInstance("SSL");
263             sc.init(new KeyManager[] {
264                     new X509KeyManager() {
265
266                         @Override
267                         public String[] getServerAliases(String keyType, Principal[] issuers) {
268                             return new String[] {
269                                     "server"
270                             };
271                         }
272
273                         @Override
274                         public PrivateKey getPrivateKey(String alias) {
275                             return priv;
276                         }
277
278                         @Override
279                         public String[] getClientAliases(String keyType, Principal[] issuers) {
280                             throw new Error();
281                         }
282
283                         @Override
284                         public X509Certificate[] getCertificateChain(String alias) {
285                             return new X509Certificate[] {
286                                     cert
287                             };
288                         }
289
290                         @Override
291                         public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
292                             throw new Error();
293                         }
294
295                         @Override
296                         public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
297                             return "server";
298                         }
299
300                     }
301             }, new TrustManager[] {
302                     new X509TrustManager() {
303
304                         @Override
305                         public X509Certificate[] getAcceptedIssuers() {
306                             return null;
307                         }
308
309                         @Override
310                         public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
311
312                         @Override
313                         public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
314                     }
315             }, new SecureRandom());
316         } catch (NoSuchAlgorithmException e) {
317             e.printStackTrace();
318             throw new Error(e);
319         } catch (KeyManagementException e) {
320             e.printStackTrace();
321             throw new Error(e);
322         }
323
324         SSLServerSocketFactory sssf = sc.getServerSocketFactory();
325         return (SSLServerSocket) sssf.createServerSocket(0);
326     }
327
328     public static void main(String[] args) throws Exception {
329         initEnvironment();
330         TestSSL t1 = new TestSSL();
331         t1.sslAndMailSuccess();
332         tearDownServer();
333     }
334
335 }