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