1 package club.wpia.gigi.pages.main;
3 import static org.hamcrest.MatcherAssert.assertThat;
4 import static org.junit.Assert.*;
6 import java.io.IOException;
7 import java.io.OutputStream;
8 import java.net.HttpURLConnection;
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;
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;
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;
36 @RunWith(Parameterized.class)
37 public class KeyCompromiseTest extends ClientTest {
39 private static final String NOT_FOUND = "not found";
41 private static class TestParameters {
43 private final String query;
45 private final String error;
47 public TestParameters(String query, String error) {
52 public String getError() {
53 if (NOT_FOUND.equals(error)) {
54 return KeyCompromiseForm.NOT_FOUND.getRaw();
59 public String getQuery() {
64 public String toString() {
65 return query + ": " + error;
69 private Certificate cert;
71 private String serial;
73 private PrivateKey priv;
75 private TestParameters pm;
77 public KeyCompromiseTest(TestParameters pm) throws GeneralSecurityException, IOException, GigiApiException, InterruptedException {
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");
85 Job j = cert.issue(null, "2y", u);
87 serial = cert.getSerial();
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),
99 params("serial=0000&priv=%priv", "Malformed serial"),
100 params("serial=0lkd&priv=%priv", "Malformed serial"),
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", "No information to identify"),
108 params("cert=&priv=%priv", "No information to identify"),
109 params("serial=&priv=%priv", "No information to identify"),
110 params("priv=%priv", "No information to identify"),
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"),
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"),
124 private static Object[] params(String query, String error) {
125 return new Object[] {
126 new TestParameters(query, error)
130 private String getQuery(String data) throws GigiApiException {
133 Pattern challenge = Pattern.compile(" data-challenge=\"([a-zA-Z0-9]+)\"");
134 Matcher m = challenge.matcher(data);
140 byte[] privKeyData = priv.getEncoded();
141 String privKey = URLEncoder.encode(PEM.encode("PRIVATE KEY", privKeyData), "UTF-8");
143 String privKeyTampered = URLEncoder.encode(PEM.encode("PRIVATE KEY", privKeyData), "UTF-8");
144 byte[] tampered = cert.cert().getEncoded();
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);
154 byte[] sigRaw = KeyCompromiseForm.sign(priv, cData);
155 String sigData = URLEncoder.encode(Base64.getEncoder().encodeToString(sigRaw), "UTF-8");
157 query = query.replace("%signature", sigData);
158 query = query.replace("%tamperedSignature", URLEncoder.encode(Base64.getEncoder().encodeToString(sigRaw), "UTF-8"));
161 } catch (IOException e) {
163 } catch (GeneralSecurityException e) {
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);
175 uc = new URL("https://" + getServerName() + KeyCompromisePage.PATH).openConnection();
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"));
183 HttpURLConnection huc = (HttpURLConnection) uc;
185 String result = IOUtils.readURL(huc);
186 String error = pm.getError();
188 assertThat(result, hasNoError());
189 assertRevoked(result);
190 getMailReceiver().receive(u.getEmail());
191 } else if ("error".equals(error)) {
192 assertThat(result, hasError());
193 assertNotEquals(CertificateStatus.REVOKED, cert.getStatus());
195 assertThat(fetchStartErrorMessage(result), CoreMatchers.containsString(error));
196 assertNotEquals(CertificateStatus.REVOKED, cert.getStatus());
200 private void assertRevoked(String result) {
201 assertThat(result, CoreMatchers.containsString("Certificate is revoked"));
202 assertEquals(CertificateStatus.REVOKED, cert.getStatus());