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