1 package org.cacert.gigi.ping;
3 import static org.junit.Assert.*;
4 import static org.junit.Assume.*;
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;
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;
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;
57 @RunWith(Parameterized.class)
58 public class TestSSL extends PingTest {
60 @Parameters(name = "self-signed = {0}")
61 public static Iterable<Object[]> genParams() throws IOException {
62 return Arrays.asList(new Object[] {
71 public Boolean self = false;
73 public abstract static class AsyncTask<T> {
81 public T join() throws InterruptedException {
90 runner = new Thread() {
95 res = AsyncTask.this.run();
96 } catch (Exception e) {
104 public abstract T run() throws Exception;
110 private X509Certificate c;
112 @Test(timeout = 70000)
113 public void sslAndMailSuccess() throws IOException, InterruptedException, SQLException, GeneralSecurityException, GigiApiException {
114 testEmailAndSSL(0, 0, true);
117 @Test(timeout = 70000)
118 public void sslWongTypeAndMailSuccess() throws IOException, InterruptedException, SQLException, GeneralSecurityException, GigiApiException {
119 testEmailAndSSL(1, 0, true);
122 @Test(timeout = 70000)
123 public void sslOneMissingAndMailSuccess() throws IOException, InterruptedException, SQLException, GeneralSecurityException, GigiApiException {
124 testEmailAndSSL(2, 0, true);
127 @Test(timeout = 70000)
128 public void sslBothMissingAndMailSuccess() throws IOException, InterruptedException, SQLException, GeneralSecurityException, GigiApiException {
129 testEmailAndSSL(3, 0, true);
132 @Test(timeout = 70000)
133 public void sslWrongTypeAndMailFail() throws IOException, InterruptedException, SQLException, GeneralSecurityException, GigiApiException {
134 testEmailAndSSL(1, 1, false);
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);
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>
151 * @param emailVariant
154 * @throws IOException
155 * @throws InterruptedException
156 * @throws SQLException
157 * @throws GeneralSecurityException
158 * @throws GigiApiException
161 private void testEmailAndSSLWithLink(int sslVariant, int emailVariant, boolean successMail) throws IOException, InterruptedException, SQLException, GeneralSecurityException, GigiApiException {
162 String test = getTestProps().getProperty("domain.local");
164 Matcher m = initailizeDomainForm();
165 String value = m.group(2);
168 createCertificateSelf(test, sslVariant == 1 ? "clientAuth" : "serverAuth", value);
170 createCertificate(test, CertificateProfile.getByName(sslVariant == 1 ? "client" : "server"));
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) {
179 if (sslVariant == 3) {
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>() {
195 public Boolean run() throws Exception {
196 return acceptSSLServer(sss);
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);
208 TestMail mail = getMailReceiver().receive();
209 if (emailVariant == 0) {
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());
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);
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<>();
240 Date from = new Date();
241 Date to = new Date(from.getTime() + 1000 * 60 * 60 * 2);
242 List<Certificate.SubjectAlternateName> l = new LinkedList<>();
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));
248 private boolean acceptSSLServer(SSLServerSocket sss) throws IOException {
249 try (Socket s = sss.accept()) {
250 s.getOutputStream().write('b');
251 s.getOutputStream().close();
253 } catch (IOException e) {
258 private SSLServerSocket createSSLServer(final PrivateKey priv, final X509Certificate cert) throws Error, IOException {
261 sc = SSLContext.getInstance("SSL");
262 sc.init(new KeyManager[] {
263 new X509KeyManager() {
266 public String[] getServerAliases(String keyType, Principal[] issuers) {
267 return new String[] {
273 public PrivateKey getPrivateKey(String alias) {
278 public String[] getClientAliases(String keyType, Principal[] issuers) {
283 public X509Certificate[] getCertificateChain(String alias) {
284 return new X509Certificate[] {
290 public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
295 public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
300 }, new TrustManager[] {
301 new X509TrustManager() {
304 public X509Certificate[] getAcceptedIssuers() {
309 public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
312 public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
314 }, new SecureRandom());
315 } catch (NoSuchAlgorithmException e) {
318 } catch (KeyManagementException e) {
323 SSLServerSocketFactory sssf = sc.getServerSocketFactory();
324 return (SSLServerSocket) sssf.createServerSocket(0);
327 public static void main(String[] args) throws Exception {
329 TestSSL t1 = new TestSSL();
330 t1.sslAndMailSuccess();