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