1 package org.cacert.gigi.pages.account;
3 import static org.junit.Assert.*;
4 import static org.hamcrest.CoreMatchers.*;
6 import java.io.IOException;
7 import java.io.InputStreamReader;
8 import java.io.OutputStream;
9 import java.net.HttpURLConnection;
11 import java.net.URLConnection;
12 import java.net.URLEncoder;
13 import java.security.GeneralSecurityException;
14 import java.security.KeyPair;
15 import java.security.Signature;
16 import java.util.Arrays;
17 import java.util.Base64;
18 import java.util.Vector;
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
22 import org.cacert.gigi.Digest;
23 import org.cacert.gigi.User;
24 import org.cacert.gigi.crypto.SPKAC;
25 import org.cacert.gigi.testUtils.IOUtils;
26 import org.cacert.gigi.testUtils.ManagedTest;
27 import org.cacert.gigi.util.PEM;
28 import org.junit.Test;
29 import sun.security.pkcs.PKCS9Attribute;
30 import sun.security.pkcs10.PKCS10Attribute;
31 import sun.security.pkcs10.PKCS10Attributes;
32 import sun.security.util.ObjectIdentifier;
33 import sun.security.x509.CertificateExtensions;
34 import sun.security.x509.DNSName;
35 import sun.security.x509.ExtendedKeyUsageExtension;
36 import sun.security.x509.GeneralName;
37 import sun.security.x509.GeneralNameInterface;
38 import sun.security.x509.GeneralNames;
39 import sun.security.x509.RFC822Name;
40 import sun.security.x509.SubjectAlternativeNameExtension;
41 import sun.security.x509.X509Key;
43 public class TestCertificateAdd extends ManagedTest {
45 KeyPair kp = generateKeypair();
47 User u = User.getById(createVerifiedUser("testuser", "testname", uniq + "@testdom.com", TEST_PASSWORD));
49 String session = login(uniq + "@testdom.com", TEST_PASSWORD);
53 public TestCertificateAdd() throws GeneralSecurityException, IOException {
54 TestDomain.addDomain(session, uniq + ".tld");
59 public void testSimpleServer() throws IOException, GeneralSecurityException {
60 PKCS10Attributes atts = buildAtts(new ObjectIdentifier[] {
61 CertificateIssueForm.OID_KEY_USAGE_SSL_SERVER
62 }, new DNSName(uniq + ".tld"));
64 String pem = generatePEMCSR(kp, "CN=a." + uniq + ".tld", atts);
66 String[] res = fillOutForm("CSR=" + URLEncoder.encode(pem, "UTF-8"));
67 assertArrayEquals(new String[] {
68 "server", "CAcert WoT User", "dns:a." + uniq + ".tld\ndns:" + uniq + ".tld\n", Digest.SHA256.toString()
73 public void testSimpleMail() throws IOException, GeneralSecurityException {
74 PKCS10Attributes atts = buildAtts(new ObjectIdentifier[] {
75 CertificateIssueForm.OID_KEY_USAGE_EMAIL_PROTECTION
76 }, new DNSName("a." + uniq + ".tld"), new DNSName("b." + uniq + ".tld"), new RFC822Name(uniq + "@testdom.com"));
78 String pem = generatePEMCSR(kp, "CN=testuser testname", atts, "SHA384WithRSA");
80 String[] res = fillOutForm("CSR=" + URLEncoder.encode(pem, "UTF-8"));
81 assertArrayEquals(new String[] {
82 "mail", "testuser testname", "dns:a." + uniq + ".tld\ndns:b." + uniq + ".tld\nemail:" + uniq + "@testdom.com\n", Digest.SHA384.toString()
87 public void testSimpleClient() throws IOException, GeneralSecurityException {
88 PKCS10Attributes atts = buildAtts(new ObjectIdentifier[] {
89 CertificateIssueForm.OID_KEY_USAGE_SSL_CLIENT
90 }, new RFC822Name(uniq + "@testdom.com"));
92 String pem = generatePEMCSR(kp, "CN=testuser testname,email=" + uniq + "@testdom.com", atts, "SHA512WithRSA");
94 String[] res = fillOutForm("CSR=" + URLEncoder.encode(pem, "UTF-8"));
95 assertArrayEquals(new String[] {
96 "client", "testuser testname", "email:" + uniq + "@testdom.com\n", Digest.SHA512.toString()
101 public void testSPKAC() throws GeneralSecurityException, IOException {
107 public void testIssue() throws IOException, GeneralSecurityException {
108 PKCS10Attributes atts = buildAtts(new ObjectIdentifier[] {
109 CertificateIssueForm.OID_KEY_USAGE_SSL_CLIENT
110 }, new RFC822Name(uniq + "@testdom.com"));
112 String pem = generatePEMCSR(kp, "CN=testuser testname,email=" + uniq + "@testdom.com", atts, "SHA512WithRSA");
114 String[] res = fillOutForm("CSR=" + URLEncoder.encode(pem, "UTF-8"));
115 assertArrayEquals(new String[] {
116 "client", "testuser testname", "email:" + uniq + "@testdom.com\n", Digest.SHA512.toString()
119 HttpURLConnection huc = (HttpURLConnection) ncert.openConnection();
120 huc.setRequestProperty("Cookie", session);
121 huc.setDoOutput(true);
122 OutputStream out = huc.getOutputStream();
123 out.write(("csrf=" + URLEncoder.encode(csrf, "UTF-8")).getBytes());
124 out.write(("&profile=client&CN=testuser+testname&SANs=" + URLEncoder.encode("email:" + uniq + "@testdom.com\n", "UTF-8")).getBytes());
125 out.write(("&hash_alg=SHA512&CCA=y").getBytes());
126 URLConnection uc = authenticate(new URL(huc.getHeaderField("Location") + ".crt"));
127 String crt = IOUtils.readURL(new InputStreamReader(uc.getInputStream(), "UTF-8"));
129 uc = authenticate(new URL(huc.getHeaderField("Location") + ".cer"));
130 byte[] cer = IOUtils.readURL(uc.getInputStream());
131 assertArrayEquals(cer, PEM.decode("CERTIFICATE", crt));
133 uc = authenticate(new URL(huc.getHeaderField("Location") + ".cer?install"));
134 byte[] cer2 = IOUtils.readURL(uc.getInputStream());
135 assertArrayEquals(cer, cer2);
136 assertEquals("application/x-x509-user-cert", uc.getHeaderField("Content-type"));
138 uc = authenticate(new URL(huc.getHeaderField("Location")));
139 String gui = IOUtils.readURL(uc);
140 assertThat(gui, containsString("clientAuth"));
141 assertThat(gui, containsString("CN=testuser testname"));
142 assertThat(gui, containsString("SHA512withRSA"));
143 assertThat(gui, containsString("RFC822Name: " + uniq + "@testdom.com"));
147 private URLConnection authenticate(URL url) throws IOException {
148 URLConnection uc = url.openConnection();
149 uc.setRequestProperty("Cookie", session);
153 protected String testSPKAC(boolean correctChallange) throws GeneralSecurityException, IOException {
154 HttpURLConnection uc = (HttpURLConnection) ncert.openConnection();
155 uc.setRequestProperty("Cookie", session);
156 String s = IOUtils.readURL(uc);
158 csrf = extractPattern(s, Pattern.compile("<input [^>]*name='csrf' [^>]*value='([^']*)'>"));
159 String challenge = extractPattern(s, Pattern.compile("<keygen [^>]*name=\"SPKAC\" [^>]*challenge=\"([^\"]*)\"/>"));
161 SPKAC spk = new SPKAC((X509Key) kp.getPublic(), challenge + (correctChallange ? "" : "b"));
162 Signature sign = Signature.getInstance("SHA512WithRSA");
163 sign.initSign(kp.getPrivate());
165 String[] res = fillOutFormDirect("SPKAC=" + URLEncoder.encode(Base64.getEncoder().encodeToString(spk.getEncoded(sign)), "UTF-8"));
166 if ( !correctChallange) {
167 fail("Should not succeed with wrong challange.");
169 assertArrayEquals(new String[] {
170 "client", CertificateIssueForm.DEFAULT_CN, "", Digest.SHA512.toString()
173 assertTrue(e.getMessage().startsWith("<div>Challenge mismatch"));
178 private PKCS10Attributes buildAtts(ObjectIdentifier[] ekuOIDs, GeneralNameInterface... SANs) throws IOException {
179 CertificateExtensions attributeValue = new CertificateExtensions();
180 GeneralNames names = new GeneralNames();
182 for (GeneralNameInterface name : SANs) {
183 names.add(new GeneralName(name));
185 attributeValue.set("SANs", new SubjectAlternativeNameExtension(names));
186 PKCS10Attributes atts = new PKCS10Attributes(new PKCS10Attribute[] {
187 new PKCS10Attribute(PKCS9Attribute.EXTENSION_REQUEST_OID, attributeValue)
189 ExtendedKeyUsageExtension eku = new ExtendedKeyUsageExtension(//
190 new Vector<>(Arrays.<ObjectIdentifier>asList(ekuOIDs)));
191 attributeValue.set("eku", eku);
195 private final URL ncert = new URL("https://" + getServerName() + CertificateAdd.PATH);
197 private String[] fillOutForm(String pem) throws IOException {
198 HttpURLConnection uc = (HttpURLConnection) ncert.openConnection();
199 uc.setRequestProperty("Cookie", session);
201 return fillOutFormDirect(pem);
205 private String[] fillOutFormDirect(String pem) throws IOException {
207 HttpURLConnection uc = (HttpURLConnection) ncert.openConnection();
208 uc.setRequestProperty("Cookie", session);
209 uc.setDoOutput(true);
210 uc.getOutputStream().write(("csrf=" + URLEncoder.encode(csrf, "UTF-8") + "&" + pem).getBytes());
211 uc.getOutputStream().flush();
213 return extractFormData(uc);
216 private String[] extractFormData(HttpURLConnection uc) throws IOException, Error {
217 String result = IOUtils.readURL(uc);
218 if (result.contains("<div class='formError'>")) {
219 String s = fetchStartErrorMessage(result);
223 String profileKey = extractPattern(result, Pattern.compile("<option value=\"([^\"]*)\" selected>"));
224 String resultingCN = extractPattern(result, Pattern.compile("<input [^>]*name='CN' [^>]*value='([^']*)'>"));
225 String txt = extractPattern(result, Pattern.compile("<textarea [^>]*name='SANs' [^>]*>([^<]*)</textarea>"));
226 String md = extractPattern(result, Pattern.compile("<input type=\"radio\" [^>]*name=\"hash_alg\" value=\"([^\"]*)\" checked='checked'/>"));
227 return new String[] {
228 profileKey, resultingCN, txt, md
232 private String extractPattern(String result, Pattern p) {
233 Matcher m = p.matcher(result);
234 assertTrue(m.find());
235 String resultingCN = m.group(1);