]> WPIA git - gigi.git/blob - tests/club/wpia/gigi/pages/main/KeyCompromiseTest.java
af9cbb64711e2330f61feab2485c7c733e1d51a4
[gigi.git] / tests / club / wpia / gigi / pages / main / KeyCompromiseTest.java
1 package club.wpia.gigi.pages.main;
2
3 import static org.hamcrest.MatcherAssert.assertThat;
4 import static org.junit.Assert.*;
5
6 import java.io.IOException;
7 import java.io.OutputStream;
8 import java.net.HttpURLConnection;
9 import java.net.URL;
10 import java.net.URLConnection;
11 import java.net.URLEncoder;
12 import java.security.GeneralSecurityException;
13 import java.security.KeyPair;
14 import java.security.PrivateKey;
15 import java.util.Base64;
16 import java.util.regex.Matcher;
17 import java.util.regex.Pattern;
18
19 import org.hamcrest.CoreMatchers;
20 import org.junit.Test;
21 import org.junit.runner.RunWith;
22 import org.junit.runners.Parameterized;
23 import org.junit.runners.Parameterized.Parameters;
24
25 import club.wpia.gigi.GigiApiException;
26 import club.wpia.gigi.dbObjects.Certificate;
27 import club.wpia.gigi.dbObjects.Certificate.CertificateStatus;
28 import club.wpia.gigi.dbObjects.Digest;
29 import club.wpia.gigi.dbObjects.Job;
30 import club.wpia.gigi.pages.account.certs.CertificateRequest;
31 import club.wpia.gigi.testUtils.ClientTest;
32 import club.wpia.gigi.testUtils.IOUtils;
33 import club.wpia.gigi.util.AuthorizationContext;
34 import club.wpia.gigi.util.PEM;
35
36 @RunWith(Parameterized.class)
37 public class KeyCompromiseTest extends ClientTest {
38
39     private static final String NOT_FOUND = "not found";
40
41     private static class TestParameters {
42
43         private final String query;
44
45         private final String error;
46
47         public TestParameters(String query, String error) {
48             this.query = query;
49             this.error = error;
50         }
51
52         public String getError() {
53             if (NOT_FOUND.equals(error)) {
54                 return KeyCompromiseForm.NOT_FOUND.getRaw();
55             }
56             return error;
57         }
58
59         public String getQuery() {
60             return query;
61         }
62
63         @Override
64         public String toString() {
65             return query + ": " + error;
66         }
67     }
68
69     private Certificate cert;
70
71     private String serial;
72
73     private PrivateKey priv;
74
75     private TestParameters pm;
76
77     public KeyCompromiseTest(TestParameters pm) throws GeneralSecurityException, IOException, GigiApiException, InterruptedException {
78         this.pm = pm;
79         KeyPair kp = generateKeypair();
80         priv = kp.getPrivate();
81         String csr = generatePEMCSR(kp, "CN=test");
82         CertificateRequest cr = new CertificateRequest(new AuthorizationContext(u, u), csr);
83         cr.update(CertificateRequest.DEFAULT_CN, Digest.SHA512.toString(), "client", null, null, "email:" + email + "\n");
84         cert = cr.draft();
85         Job j = cert.issue(null, "2y", u);
86         await(j);
87         serial = cert.getSerial();
88     }
89
90     @Parameters(name = "{0}")
91     public static Object[][] getParams() {
92         return new Object[][] {
93                 params("serial=%serial&priv=%priv", null),// serial+key
94                 params("serial=0000%serial&priv=%priv", null),// leading Zeros
95                 params("serial=0000%Serial&priv=%priv", null),// upper case
96                 params("cert=%cert&priv=%priv", null),// cert+key
97                 params("serial=%serial&signature=%signature", null),
98                 // Zero serial
99                 params("serial=0000&priv=%priv", NOT_FOUND),
100                 params("serial=0lkd&priv=%priv", NOT_FOUND),
101                 // tampered cert
102                 params("cert=%tamperedCert&priv=%priv", "not be parsed"),
103                 params("cert=%cert&priv=%tamperedPriv", "Private Key is malformed"),
104                 params("serial=1&priv=%priv", NOT_FOUND),
105                 params("serial=1%serial&priv=%priv", NOT_FOUND),
106                 // missing certificate identification
107                 params("serial=&cert=&priv=%priv", "identification"),
108                 params("cert=&priv=%priv", "identification"),
109                 params("serial=&priv=%priv", "identification"),
110                 params("priv=%priv", "identification"),
111                 // sign missing
112                 params("serial=%serial&priv=&signature=", "No verification"),
113                 params("serial=%serial&signature=", "No verification"),
114                 params("serial=%serial&priv=", "No verification"),
115                 params("serial=%serial", "No verification"),
116                 params("cert=%cert&signature=%tamperedSignature", "Verification does not match"),
117
118                 params("cert=-_&signature=%signature", "certificate could not be parsed"),
119                 params("cert=%cert&signature=-_", "Signature is malformed"),
120                 params("cert=%cert&priv=-_", "Private Key is malformed"),
121         };
122     }
123
124     private static Object[] params(String query, String error) {
125         return new Object[] {
126                 new TestParameters(query, error)
127         };
128     }
129
130     private String getQuery(String data) {
131         String cData = null;
132         {
133             Pattern challenge = Pattern.compile(" data-challenge=\"([a-zA-Z0-9]+)\"");
134             Matcher m = challenge.matcher(data);
135             if (m.find()) {
136                 cData = m.group(1);
137             }
138         }
139         try {
140             byte[] privKeyData = priv.getEncoded();
141             String privKey = URLEncoder.encode(PEM.encode("PRIVATE KEY", privKeyData), "UTF-8");
142             privKeyData[0]++;
143             String privKeyTampered = URLEncoder.encode(PEM.encode("PRIVATE KEY", privKeyData), "UTF-8");
144             byte[] tampered = cert.cert().getEncoded();
145             tampered[0]++;
146             String query = pm.getQuery();
147             query = query.replace("%serial", serial.toLowerCase())//
148                     .replace("%Serial", serial.toUpperCase())//
149                     .replace("%priv", privKey)//
150                     .replace("%cert", URLEncoder.encode(PEM.encode("CERTIFICATE", cert.cert().getEncoded()), "UTF-8"))//
151                     .replace("%tamperedCert", URLEncoder.encode(PEM.encode("CERTIFICATE", tampered), "UTF-8"))//
152                     .replace("%tamperedPriv", privKeyTampered);
153             if (cData != null) {
154                 byte[] sigRaw = KeyCompromiseForm.sign(priv, cData);
155                 String sigData = URLEncoder.encode(Base64.getEncoder().encodeToString(sigRaw), "UTF-8");
156                 sigRaw[0]++;
157                 query = query.replace("%signature", sigData);
158                 query = query.replace("%tamperedSignature", URLEncoder.encode(Base64.getEncoder().encodeToString(sigRaw), "UTF-8"));
159             }
160             return query;
161         } catch (IOException e) {
162             throw new Error(e);
163         } catch (GeneralSecurityException e) {
164             throw new Error(e);
165         }
166     }
167
168     @Test
169     public void testExecution() throws IOException, InterruptedException, GigiApiException, GeneralSecurityException {
170         URLConnection uc = new URL("https://" + getServerName() + KeyCompromisePage.PATH).openConnection();
171         String cookie = stripCookie(uc.getHeaderField("Set-Cookie"));
172         String content = IOUtils.readURL(uc);
173         String csrf = getCSRF(0, content);
174
175         uc = new URL("https://" + getServerName() + KeyCompromisePage.PATH).openConnection();
176         cookie(uc, cookie);
177         uc.setDoOutput(true);
178         OutputStream os = uc.getOutputStream();
179         os.write(("csrf=" + URLEncoder.encode(csrf, "UTF-8") + "&" //
180                 + getQuery(content)//
181         ).getBytes("UTF-8"));
182         os.flush();
183         HttpURLConnection huc = (HttpURLConnection) uc;
184
185         String result = IOUtils.readURL(huc);
186         String error = pm.getError();
187         if (error == null) {
188             assertThat(result, hasNoError());
189             assertRevoked(result);
190         } else if ("error".equals(error)) {
191             assertThat(result, hasError());
192             assertNotEquals(CertificateStatus.REVOKED, cert.getStatus());
193         } else {
194             assertThat(fetchStartErrorMessage(result), CoreMatchers.containsString(error));
195             assertNotEquals(CertificateStatus.REVOKED, cert.getStatus());
196         }
197     }
198
199     private void assertRevoked(String result) {
200         assertThat(result, CoreMatchers.containsString("Certificate is revoked"));
201         assertEquals(CertificateStatus.REVOKED, cert.getStatus());
202     }
203 }