Merge "add: show more certificates on the "roots" page"
authorLucas Werkmeister <mail@lucaswerkmeister.de>
Tue, 7 Nov 2017 19:36:37 +0000 (20:36 +0100)
committerGerrit Code Review <gigi-system@dogcraft.de>
Tue, 7 Nov 2017 19:36:37 +0000 (20:36 +0100)
36 files changed:
debian/copyright
src/club/wpia/gigi/Gigi.java
src/club/wpia/gigi/crypto/key/KeyCheck.java [new file with mode: 0644]
src/club/wpia/gigi/crypto/key/KeyCheckPublicKeyFormat.java [new file with mode: 0644]
src/club/wpia/gigi/crypto/key/KeyCheckROCA.java [new file with mode: 0644]
src/club/wpia/gigi/crypto/key/KeyCheckSmallFactors.java [new file with mode: 0644]
src/club/wpia/gigi/pages/account/certs/CertificateRequest.java
src/club/wpia/gigi/ping/PingerDaemon.java
tests/club/wpia/gigi/LoginTest.java
tests/club/wpia/gigi/TestUser.java
tests/club/wpia/gigi/api/TestFindAgent.java
tests/club/wpia/gigi/crypto/key/KeyCheckPublicKeyFormatTest.java [new file with mode: 0644]
tests/club/wpia/gigi/crypto/key/KeyCheckROCATest.java [new file with mode: 0644]
tests/club/wpia/gigi/crypto/key/KeyCheckSmallFactorsTest.java [new file with mode: 0644]
tests/club/wpia/gigi/crypto/key/KeyCheckTest.java [new file with mode: 0644]
tests/club/wpia/gigi/dbObjects/TestVerificationMail.java
tests/club/wpia/gigi/pages/TestVerify.java
tests/club/wpia/gigi/pages/account/TestCertificateAdd.java
tests/club/wpia/gigi/pages/account/TestMailManagement.java
tests/club/wpia/gigi/pages/account/TestPasswordResetExternal.java
tests/club/wpia/gigi/pages/admin/TestSEAdminNotificationMail.java
tests/club/wpia/gigi/pages/admin/TestSEAdminPageCertSearch.java
tests/club/wpia/gigi/pages/admin/TestSEAdminPageDetails.java
tests/club/wpia/gigi/pages/main/KeyCompromiseTest.java
tests/club/wpia/gigi/pages/main/KeyCompromiseTestMessage.java
tests/club/wpia/gigi/pages/main/RegisterPageTest.java
tests/club/wpia/gigi/pages/wot/TestVerification.java
tests/club/wpia/gigi/ping/TestDNS.java
tests/club/wpia/gigi/ping/TestHTTP.java
tests/club/wpia/gigi/ping/TestSSL.java
tests/club/wpia/gigi/template/TestTemplateMail.java
tests/club/wpia/gigi/testUtils/BusinessTest.java
tests/club/wpia/gigi/testUtils/ConfiguredTest.java
tests/club/wpia/gigi/testUtils/MailReceiver.java
tests/club/wpia/gigi/testUtils/ManagedTest.java
tests/club/wpia/gigi/testUtils/TestEmailReceiver.java

index 7ccbafb..50f2e57 100644 (file)
@@ -1,4 +1,4 @@
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 Upstream-Name: gigi
 Source: <https://code.wpia.club>
 
@@ -22,9 +22,26 @@ Files: src/club/wpia/gigi/database
 Copyright: 2014-2017 WPIA Software Team <software@wpia.club>
 License: AGPL-3.0 or BSD
 
+Files: src/club/wpia/gigi/crypto/key/KeyCheckROCA.java
+Copyright: 2017, CRoCS, EnigmaBridge Ltd.
+License: AGPL-3.0 or MIT
+
+License: AGPL-3.0
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ .
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+ .
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 License: BSD
- Copyright (c) 2014, WPIA
- All rights reserved.
+ Copyright (c) <year> <copyright holders>
  .
  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions are met:
@@ -48,16 +65,23 @@ License: BSD
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
  THE POSSIBILITY OF SUCH DAMAGE.
 
-License: AGPL-3.0
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+License: MIT
+ Copyright (c) <year> <copyright holders>
  .
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU Affero General Public License for more details.
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
  .
- You should have received a copy of the GNU Affero General Public License
- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
index 1981876..6a0b793 100644 (file)
@@ -395,7 +395,12 @@ public final class Gigi extends HttpServlet {
                 resp.sendError(403);
                 return;
             }
-            if (p.beforeTemplate(req, resp)) {
+            try {
+                if (p.beforeTemplate(req, resp)) {
+                    return;
+                }
+            } catch (CSRFException e) {
+                resp.sendError(500, "CSRF invalid");
                 return;
             }
             HashMap<String, Object> vars = new HashMap<String, Object>();
diff --git a/src/club/wpia/gigi/crypto/key/KeyCheck.java b/src/club/wpia/gigi/crypto/key/KeyCheck.java
new file mode 100644 (file)
index 0000000..b6efd54
--- /dev/null
@@ -0,0 +1,62 @@
+package club.wpia.gigi.crypto.key;
+
+import java.security.PublicKey;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import club.wpia.gigi.GigiApiException;
+
+public abstract class KeyCheck {
+
+    protected static final Set<KeyCheck> checks = new LinkedHashSet<KeyCheck>();
+
+    public static List<KeyCheck> getChecks() {
+        return Collections.list(Collections.enumeration(checks));
+    }
+
+    public static void register(KeyCheck check) {
+        checks.add(check);
+    }
+
+    public abstract void check(PublicKey key) throws GigiApiException;
+
+    public static void checkKey(PublicKey key) throws GigiApiException {
+
+        if (checks.isEmpty() || checks.size() < 3) {
+            // Mandatory checks are registered here
+            register(new KeyCheckPublicKeyFormat());
+            register(new KeyCheckSmallFactors());
+            register(new KeyCheckROCA());
+        }
+
+        if (key == null) {
+            throw new GigiApiException("Failed key sanity check: No key given!");
+        }
+
+        for (KeyCheck kc : checks) {
+            kc.check(key);
+        }
+
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null) {
+            return false;
+        }
+
+        if (o == this) {
+            return true;
+        }
+
+        return getClass().equals(o.getClass());
+    }
+
+    @Override
+    public int hashCode() {
+        return getClass().hashCode();
+    }
+
+}
diff --git a/src/club/wpia/gigi/crypto/key/KeyCheckPublicKeyFormat.java b/src/club/wpia/gigi/crypto/key/KeyCheckPublicKeyFormat.java
new file mode 100644 (file)
index 0000000..c85b16c
--- /dev/null
@@ -0,0 +1,36 @@
+package club.wpia.gigi.crypto.key;
+
+import java.security.PublicKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+
+import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.output.template.SprintfCommand;
+
+public class KeyCheckPublicKeyFormat extends KeyCheck {
+
+    static {
+        register(new KeyCheckPublicKeyFormat());
+    }
+
+    @Override
+    public void check(PublicKey key) throws GigiApiException {
+
+        if (key instanceof RSAPublicKey) {
+            return;
+        }
+
+        if (key instanceof DSAPublicKey) {
+            return;
+        }
+
+        if (key instanceof ECPublicKey) {
+            return;
+        }
+
+        throw new GigiApiException(SprintfCommand.createSimple("Public Key Format Check: Unknown or unsupported public key algorithm {0}", key.getAlgorithm()));
+
+    }
+
+}
diff --git a/src/club/wpia/gigi/crypto/key/KeyCheckROCA.java b/src/club/wpia/gigi/crypto/key/KeyCheckROCA.java
new file mode 100644 (file)
index 0000000..e808cdd
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2017, CRoCS, EnigmaBridge Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+/*
+ * Credits: ported to Java by Martin Paljak
+ */
+/*
+ * Credits: ported to Gigi KeyCheck interface by Benny Baumann
+ */
+
+package club.wpia.gigi.crypto.key;
+
+import java.math.BigInteger;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPublicKey;
+
+import club.wpia.gigi.GigiApiException;
+
+/**
+ * Due to a bug in several chips produced by Infineon several cryptographic
+ * processors in smartcards and other HSM appliances produced RSA keys with a
+ * weakness that made them subject for easy factorization. The vulnerability has
+ * been present in Infineon's library from about 2014 through 2017 making keys
+ * up to 2048 bit RSA practically factorable and strongly weakens larger RSA
+ * keys produced with such an implementation. This check implements a
+ * fingerprinting mechanism detecting such keys using a heuristic found by the
+ * researchers describing the vulnerability. Any such keys SHALL NOT be
+ * certified. This implementation is based on the Java port by Martin Paljak.
+ *
+ * @see <a href="https://crocs.fi.muni.cz/public/papers/rsa_ccs17">Original ROCA
+ *      vulnerability website</a>
+ * @see <a href="https://arstechnica.com/?post_type=post&p=1186901">Report by
+ *      ArsTechnica on ROCA</a>
+ * @see <a
+ *      href="https://gist.github.com/hannob/ad37d9e9e3cbf3b89bc0a8fc80cb9475">Gist
+ *      by Hanno Böck detailling impacted keys</a>
+ * @see <a
+ *      href="https://github.com/crocs-muni/roca/blob/master/java/BrokenKey.java">Java
+ *      port by Martin Paljak</a>
+ */
+public class KeyCheckROCA extends KeyCheck {
+
+    /**
+     * List of Lengths of masks for the listed {@link #markers}.
+     */
+    private static final int[] prims = new int[] {
+            11, 13, 17, 19, 37, 53, 61, 71, 73, 79, 97, 103, 107, 109, 127, 151, 157
+    };
+
+    private static final BigInteger[] primes = new BigInteger[prims.length];
+
+    /**
+     * List of markers used to fingerprint keys vulnerable to ROCA.
+     *
+     * This list is reduced according to:
+     * {@link https://groups.google.com/d/msg/mozilla.dev.security.policy/4RqKdD0FeF4/DcxMqchSAQAJ}
+     */
+    private static final BigInteger[] markers = new BigInteger[] {
+            new BigInteger("1026"), // 11:402
+            new BigInteger("5658"), // 13:161a
+            new BigInteger("107286"), // 17:1a316
+            new BigInteger("199410"), // 19:30af2
+            new BigInteger("67109890"), // 37:4000402
+            new BigInteger("5310023542746834"), // 53:12dd703303aed2
+            new BigInteger("1455791217086302986"), // 61:1434026619900b0a
+            new BigInteger("20052041432995567486"), // 71:1164729716b1d977e
+            new BigInteger("6041388139249378920330"), // 73:147811a48004962078a
+            new BigInteger("207530445072488465666"), // 79:b4010404000640502
+            new BigInteger("79228162521181866724264247298"), // 97:1000000006000001800000002
+            new BigInteger("1760368345969468176824550810518"), // 103:16380e9115bd964257768fe396
+            new BigInteger("50079290986288516948354744811034"), // 107:27816ea9821633397be6a897e1a
+            new BigInteger("473022961816146413042658758988474"), // 109:1752639f4e85b003685cbe7192ba
+            new BigInteger("144390480366845522447407333004847678774"), // 127:6ca09850c2813205a04c81430a190536
+            new BigInteger("1800793591454480341970779146165214289059119882"), // 151:50c018bc00482458dac35b1a2412003d18030a
+            new BigInteger("126304807362733370595828809000324029340048915994"), // 157:161fb414d76af63826461899071bd5baca0b7e1a
+    };
+
+    static {
+        for (int i = 0; i < prims.length; i++) {
+            primes[i] = BigInteger.valueOf(prims[i]);
+        }
+
+        register(new KeyCheckROCA());
+    }
+
+    @Override
+    public void check(PublicKey key) throws GigiApiException {
+
+        if ( !(key instanceof RSAPublicKey)) {
+            return;
+        }
+
+        BigInteger modulus = ((RSAPublicKey) key).getModulus();
+
+        for (int i = 0; i < primes.length; i++) {
+            if (BigInteger.ONE.shiftLeft(modulus.remainder(primes[i]).intValue()).and(markers[i]).equals(BigInteger.ZERO)) {
+                return;
+            }
+        }
+
+        throw new GigiApiException("ROCA vulnerability check for public key: Key likely vulnerable as fingerprint matches.");
+
+    }
+
+}
diff --git a/src/club/wpia/gigi/crypto/key/KeyCheckSmallFactors.java b/src/club/wpia/gigi/crypto/key/KeyCheckSmallFactors.java
new file mode 100644 (file)
index 0000000..a096534
--- /dev/null
@@ -0,0 +1,52 @@
+package club.wpia.gigi.crypto.key;
+
+import java.math.BigInteger;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+
+import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.output.template.SprintfCommand;
+
+public class KeyCheckSmallFactors extends KeyCheck {
+
+    private static final long MAX_CHECKED_SMALL_PRIME_BOUNDARY = 10000;
+
+    private static final BigInteger[] primes;
+
+    static {
+        ArrayList<BigInteger> prims = new ArrayList<>(1024);
+
+        NextPrime:
+        for (long i = 2; i < MAX_CHECKED_SMALL_PRIME_BOUNDARY; i++) {
+            for (BigInteger p : prims) {
+                if (BigInteger.ZERO.equals(BigInteger.valueOf(i).mod(p))) {
+                    continue NextPrime;
+                }
+            }
+
+            prims.add(BigInteger.valueOf(i));
+        }
+
+        primes = prims.toArray(new BigInteger[0]);
+
+        register(new KeyCheckSmallFactors());
+    }
+
+    @Override
+    public void check(PublicKey key) throws GigiApiException {
+        if ( !(key instanceof RSAPublicKey)) {
+            return;
+        }
+
+        BigInteger modulus = ((RSAPublicKey) key).getModulus();
+
+        // Check for small prime factors below 10000
+        for (BigInteger n : primes) {
+            if (BigInteger.ZERO.equals(modulus.mod(n))) {
+                throw new GigiApiException(SprintfCommand.createSimple("Small factors check of public key: Key is divisible by {0}.", n.toString()));
+            }
+        }
+    }
+
+}
index 4158e9b..8a1bc59 100644 (file)
@@ -16,6 +16,7 @@ import java.util.TreeSet;
 
 import club.wpia.gigi.GigiApiException;
 import club.wpia.gigi.crypto.SPKAC;
+import club.wpia.gigi.crypto.key.KeyCheck;
 import club.wpia.gigi.dbObjects.Certificate;
 import club.wpia.gigi.dbObjects.Certificate.CSRType;
 import club.wpia.gigi.dbObjects.Certificate.SANType;
@@ -108,7 +109,7 @@ public class CertificateRequest {
         this(c, csr, (CertificateProfile) null);
     }
 
-    public CertificateRequest(AuthorizationContext ctx, String csr, CertificateProfile cp) throws GeneralSecurityException, IOException, IOException {
+    public CertificateRequest(AuthorizationContext ctx, String csr, CertificateProfile cp) throws GeneralSecurityException, IOException, IOException, GigiApiException {
         this.ctx = ctx;
         if (cp != null) {
             profile = cp;
@@ -190,6 +191,8 @@ public class CertificateRequest {
         }
         this.SANs = SANs;
         pk = parsed.getSubjectPublicKeyInfo();
+        KeyCheck.checkKey(pk);
+
         String sign = getSignatureAlgorithm(data);
         guessDigest(sign);
 
@@ -206,12 +209,13 @@ public class CertificateRequest {
             throw new GigiApiException("Challenge mismatch");
         }
         pk = parsed.getPubkey();
+        KeyCheck.checkKey(pk);
+
         String sign = getSignatureAlgorithm(data);
         guessDigest(sign);
         this.SANs = new HashSet<>();
         this.csr = "SPKAC=" + cleanedSPKAC;
         this.csrType = CSRType.SPKAC;
-
     }
 
     private static String getSignatureAlgorithm(byte[] data) throws IOException {
index 51cc985..d6016c6 100644 (file)
@@ -12,7 +12,6 @@ import club.wpia.gigi.database.GigiResultSet;
 import club.wpia.gigi.dbObjects.Domain;
 import club.wpia.gigi.dbObjects.DomainPingConfiguration;
 import club.wpia.gigi.dbObjects.DomainPingType;
-import club.wpia.gigi.util.RandomToken;
 
 public class PingerDaemon extends Thread {
 
@@ -91,11 +90,6 @@ public class PingerDaemon extends Thread {
         String config = conf.getInfo();
         DomainPinger dp = pingers.get(type);
         if (dp != null) {
-            if (dp instanceof EmailPinger) {
-                String token = null;
-                token = RandomToken.generateToken(16);
-                config = config + ":" + token;
-            }
             Domain target = conf.getTarget();
             System.err.println("Executing " + dp + " on " + target + " (" + System.currentTimeMillis() + ")");
             try {
index ab2780c..551d88d 100644 (file)
@@ -28,7 +28,7 @@ public class LoginTest extends ManagedTest {
     public void testLoginUnverified() throws IOException {
         String email = createUniqueName() + "@testmail.org";
         registerUser("an", "bn", email, TEST_PASSWORD);
-        getMailReceiver().receive();
+        getMailReceiver().receive(email);
         assertFalse(isLoggedin(login(email, TEST_PASSWORD)));
     }
 
index 7ad3f49..a2206ee 100644 (file)
@@ -10,8 +10,6 @@ import java.util.Locale;
 import org.hamcrest.CoreMatchers;
 import org.junit.Test;
 
-import club.wpia.gigi.dbObjects.Verification;
-import club.wpia.gigi.dbObjects.Verification.VerificationType;
 import club.wpia.gigi.dbObjects.Country;
 import club.wpia.gigi.dbObjects.Country.CountryCodeType;
 import club.wpia.gigi.dbObjects.Domain;
@@ -20,6 +18,8 @@ import club.wpia.gigi.dbObjects.Name;
 import club.wpia.gigi.dbObjects.NamePart;
 import club.wpia.gigi.dbObjects.NamePart.NamePartType;
 import club.wpia.gigi.dbObjects.User;
+import club.wpia.gigi.dbObjects.Verification;
+import club.wpia.gigi.dbObjects.Verification.VerificationType;
 import club.wpia.gigi.testUtils.BusinessTest;
 import club.wpia.gigi.util.DayDate;
 import club.wpia.gigi.util.Notary;
@@ -90,9 +90,9 @@ public class TestUser extends BusinessTest {
         User u = User.getById(id);
 
         new EmailAddress(u, uq + "b@email.org", Locale.ENGLISH);
-        getMailReceiver().receive().verify();
+        getMailReceiver().receive(uq + "b@email.org").verify();
         new EmailAddress(u, uq + "c@email.org", Locale.ENGLISH);
-        getMailReceiver().receive();// no-verify
+        getMailReceiver().receive(uq + "c@email.org");// no-verify
         assertEquals(3, u.getEmails().length);
 
         assertTrue(u.isValidEmail(uq + "a@email.org"));
index b1bec53..753eb77 100644 (file)
@@ -15,14 +15,13 @@ import org.json.JSONTokener;
 import org.junit.Test;
 
 import club.wpia.gigi.GigiApiException;
-import club.wpia.gigi.api.FindAgent;
 import club.wpia.gigi.dbObjects.Certificate;
+import club.wpia.gigi.dbObjects.Certificate.CSRType;
+import club.wpia.gigi.dbObjects.Certificate.SANType;
 import club.wpia.gigi.dbObjects.CertificateProfile;
 import club.wpia.gigi.dbObjects.Digest;
 import club.wpia.gigi.dbObjects.Group;
 import club.wpia.gigi.dbObjects.User;
-import club.wpia.gigi.dbObjects.Certificate.CSRType;
-import club.wpia.gigi.dbObjects.Certificate.SANType;
 import club.wpia.gigi.pages.account.FindAgentAccess;
 import club.wpia.gigi.testUtils.IOUtils;
 import club.wpia.gigi.testUtils.RestrictedApiTest;
@@ -73,10 +72,9 @@ public class TestFindAgent extends RestrictedApiTest {
         grant((userUFirst ? us2 : u), Group.LOCATE_AGENT);
         v = doApi(FindAgent.PATH_MAIL, "from=" + id + "&to=" + u2 + "&subject=the-subject&body=body");
         assertEquals(v.getResponseMessage(), 200, v.getResponseCode());
-        TestMail mail = getMailReceiver().receive();
+        TestMail mail = getMailReceiver().receive(us2.getEmail());
         assertEquals("body", mail.getMessage());
         assertThat(mail.getSubject(), containsString("the-subject"));
-        assertEquals(us2.getEmail(), mail.getTo());
     }
 
     @Test
diff --git a/tests/club/wpia/gigi/crypto/key/KeyCheckPublicKeyFormatTest.java b/tests/club/wpia/gigi/crypto/key/KeyCheckPublicKeyFormatTest.java
new file mode 100644 (file)
index 0000000..e8d3ec6
--- /dev/null
@@ -0,0 +1,117 @@
+package club.wpia.gigi.crypto.key;
+
+import static org.junit.Assert.*;
+import static org.junit.Assume.*;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+
+import org.junit.Test;
+
+import club.wpia.gigi.GigiApiException;
+
+public class KeyCheckPublicKeyFormatTest {
+
+    @Test
+    public void testFormatRSA() throws GeneralSecurityException, IOException {
+
+        // openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:2048
+        // -pkeyopt rsa_keygen_pubexp:7331 2>/dev/null |
+        // openssl pkey -pubout -outform pem
+        String sfk = "-----BEGIN PUBLIC KEY-----\n" + //
+                "MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQEArcAPmy3RnXdwyFg3V9k1\n" + //
+                "RaFR/peHa3hLsmh25BInRVArbaMctSBaJBVZwQIgBdqjyITQQZP38i6k+WdsETn9\n" + //
+                "J491UDLKU3E3UG60ZS3BzcJllNdpn4g0IZROxmmUz2JlAXkGtIglmWWDx14qHSNj\n" + //
+                "ON58mc3ihfn/oWkPk2hk/csDxGQq5jSaBUwa9THBg9UQHHBqQbhp2nGfa5a5VRlI\n" + //
+                "0QeIy+8GmKlXYMchReUI25ksLOzaqETD0UXiAPyt+vpvkKCDjWGc3kjabn6OkuTt\n" + //
+                "na7N/52qrEC2ImuanYlzR5gv9jkbFF2PiMIEBD+3B0842rLx0X/lbXhRr1MtuHtN\n" + //
+                "tQICHKM=\n" + //
+                "-----END PUBLIC KEY-----\n";
+
+        PublicKey pk = KeyCheckTest.pkFromString(sfk);
+        try {
+            KeyCheck c = new KeyCheckPublicKeyFormat();
+            c.check(pk);
+        } catch (GigiApiException gae) {
+            throw new Error("Valid key (RSA Public Key) rejected.", gae);
+        }
+
+    }
+
+    @Test
+    public void testFormatDSA() throws GeneralSecurityException, IOException {
+
+        // DSA (using OpenSSH)
+        String sfk = "-----BEGIN PUBLIC KEY-----\n" + //
+                "MIIBtzCCASsGByqGSM44BAEwggEeAoGBAJpcf099rROPSjbJ5KWk5RF1ngRqXSo7\n" + //
+                "cmKin9QPxIg0tXmxMGVS2sdtXYtmSJ9fewSAx0vHbojysEGY9ASXEoFpzDye4BbK\n" + //
+                "yog9oHaUUEjxkSTwKcipu5BgM9b/nvigw/bs4dlEM+egdzf36lXXXJgvaTeXSpu9\n" + //
+                "gKrKXTSi0jcvAhUAoH2Nbl6mRgAX4l6U5EXeg0zts3MCgYAW16cPIxLzmvrajRVR\n" + //
+                "aIzAWpN1ApE/kx4CbtWZCdNttHu3c8D6qSnVrWpxY6FzrpeFniwg4vu73Ykh3crH\n" + //
+                "0rVa20lrdRUAYGzbgInS+GLoPDGu1LukF0evJYZwyt6qsaFkQ54h4StSK+oM/mOi\n" + //
+                "haLI45Rvlmade3KRQ/7YkV7DZQOBhQACgYEAjVGvOHImKynxgBl+eHeN2Ddqgj1+\n" + //
+                "AKEOFKuFuedG9tKHtZXx04j982kaDnNc5cZY1KPFPYlS7jVJwcFPuf9Hi1/Aqq+3\n" + //
+                "GnqaPl+tJtSpY2Chu8iIHIi5OXiwQC9ImtIEASZkkO+RIPLpzgb3GTn306NtMxae\n" + //
+                "e+mhIx1IrbzMxSA=\n" + //
+                "-----END PUBLIC KEY-----\n";
+
+        PublicKey pk = KeyCheckTest.pkFromString(sfk);
+        try {
+            KeyCheck c = new KeyCheckPublicKeyFormat();
+            c.check(pk);
+        } catch (GigiApiException gae) {
+            throw new Error("Valid key (DSA Public Key) rejected.", gae);
+        }
+
+    }
+
+    @Test
+    public void testFormatECDSA() throws GeneralSecurityException, IOException {
+
+        // ECDSA (secp256r1 / P-256)
+        String sfk = "-----BEGIN PUBLIC KEY-----\n" + //
+                "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIQeJlVJLBpevYZjGWPPkD6hSrUEI\n" + //
+                "G86i9e2p2QGCanQzNNM8Dkqv5Oa13qjxhRZNo2w+lVOBkAZyAptNKKT5Kw==\n" + //
+                "-----END PUBLIC KEY-----\n";
+
+        PublicKey pk = KeyCheckTest.pkFromString(sfk);
+        try {
+            KeyCheck c = new KeyCheckPublicKeyFormat();
+            c.check(pk);
+        } catch (GigiApiException gae) {
+            throw new Error("Valid key (ECDSA Public Key on P-256) rejected.", gae);
+        }
+
+    }
+
+    @Test
+    public void testFormatGOST() throws GeneralSecurityException, IOException {
+
+        // GOST R 34.10-2001 (256 bits)
+        // https://lib.void.so/an-example-of-using-openssl-gost-engine-in-cc/
+        String sfk = "-----BEGIN PUBLIC KEY-----\n" + //
+                "MGMwHAYGKoUDAgITMBIGByqFAwICIwEGByqFAwICHgEDQwAEQDv/qpUxeRWXnyF8\n" + //
+                "YwSUq7qQsL6MtD42GxLxqzLGx3NmpD4rHRay4xgQp91oTtqJjnybsplij0haRq7i\n" + //
+                "Nf7QEdY=\n" + //
+                "-----END PUBLIC KEY-----";
+
+        final PublicKey pk;
+        try {
+            pk = KeyCheckTest.pkFromString(sfk);
+        } catch (GeneralSecurityException gse) {
+            assumeTrue("Could not load the GOST key due to lack of support", false);
+            return;
+        }
+
+        try {
+            KeyCheck c = new KeyCheckPublicKeyFormat();
+            c.check(pk);
+            fail("Unsupported key (GOST Public Key) accepted.");
+        } catch (GigiApiException gae) {
+            // expected
+        }
+
+    }
+
+}
diff --git a/tests/club/wpia/gigi/crypto/key/KeyCheckROCATest.java b/tests/club/wpia/gigi/crypto/key/KeyCheckROCATest.java
new file mode 100644 (file)
index 0000000..3812d03
--- /dev/null
@@ -0,0 +1,122 @@
+package club.wpia.gigi.crypto.key;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+
+import org.junit.Test;
+
+import club.wpia.gigi.GigiApiException;
+
+// Vulnerable keys for this test taken from
+// @link https://misissued.com/batch/28/
+public class KeyCheckROCATest {
+
+    @Test
+    public void testROCASaneKey() throws GeneralSecurityException, IOException {
+
+        // Normal public key generated with OpenSSL:
+        // openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:2048
+        // -pkeyopt rsa_keygen_pubexp:7331 2>/dev/null |
+        // openssl pkey -pubout -outform pem
+        String sfk = "-----BEGIN PUBLIC KEY-----\n" + //
+                "MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQEArcAPmy3RnXdwyFg3V9k1\n" + //
+                "RaFR/peHa3hLsmh25BInRVArbaMctSBaJBVZwQIgBdqjyITQQZP38i6k+WdsETn9\n" + //
+                "J491UDLKU3E3UG60ZS3BzcJllNdpn4g0IZROxmmUz2JlAXkGtIglmWWDx14qHSNj\n" + //
+                "ON58mc3ihfn/oWkPk2hk/csDxGQq5jSaBUwa9THBg9UQHHBqQbhp2nGfa5a5VRlI\n" + //
+                "0QeIy+8GmKlXYMchReUI25ksLOzaqETD0UXiAPyt+vpvkKCDjWGc3kjabn6OkuTt\n" + //
+                "na7N/52qrEC2ImuanYlzR5gv9jkbFF2PiMIEBD+3B0842rLx0X/lbXhRr1MtuHtN\n" + //
+                "tQICHKM=\n" + //
+                "-----END PUBLIC KEY-----\n";
+
+        PublicKey pk = KeyCheckTest.pkFromString(sfk);
+        try {
+            KeyCheck c = new KeyCheckROCA();
+            c.check(pk);
+        } catch (GigiApiException gae) {
+            throw new Error("Valid key (not vulnerable to ROCA vulnerability) rejected.", gae);
+        }
+
+    }
+
+    @Test
+    public void testROCAVulnerable1() throws GeneralSecurityException, IOException {
+
+        // D-TRUST Qualified Root CA 1 2014:PN
+        // https://crt.sh/?id=26311918&opt=cablint
+        String sfk = "-----BEGIN PUBLIC KEY-----\n" + //
+                "MIIBJDANBgkqhkiG9w0BAQEFAAOCAREAMIIBDAKCAQEAlT2Gi8cR+hX+0iYaYH0e\n" + //
+                "Pmxrqq1tNKlvcesp1wwIeixqeQ2/QJkFMEAVq3hX45Cri7Z/p9ch8+Nd7eva80Ym\n" + //
+                "nn0llfQ2kJDhi1fOTfodR7IN24105y5D6Lf3zre6J2FOxqPH/q0dDJAbTbuaO4kS\n" + //
+                "yI9xUEhvHo8oZ0L3SGq6VyeeOBXDoBg4xp6xp1w6cZ76/3HhuBc26sgoO9AvDRzp\n" + //
+                "M74wvzGBSVaA8+SU1O46plY4os4GlHEdcZM/0NcHeiWwJvycPKkurVL9AxDBq9Iw\n" + //
+                "Dox/+zQzxcS7txvrJeI1ahQwPpzYdJEwFQ6/rCt43KALWt+OoAIvW5TVYllaF62Z\n" + //
+                "XwIFAJLK1sU=\n" + //
+                "-----END PUBLIC KEY-----\n";
+
+        PublicKey pk = KeyCheckTest.pkFromString(sfk);
+        try {
+            KeyCheck c = new KeyCheckROCA();
+            c.check(pk);
+            fail("Invalid key (ROCA vulnerable) accepted.");
+        } catch (GigiApiException gae) {
+            // expected
+        }
+
+    }
+
+    @Test
+    public void testROCAVulnerable2() throws GeneralSecurityException, IOException {
+
+        // D-TRUST Qualified Root CA 2 2014:PN
+        // https://crt.sh/?id=26310640&opt=cablint
+        String sfk = "-----BEGIN PUBLIC KEY-----\n" + //
+                "MIIBJDANBgkqhkiG9w0BAQEFAAOCAREAMIIBDAKCAQEAmDbSRazHfc1YoqH6dXWz\n" + //
+                "k2zBJadliqHgpft1Z5HqXF6AzXQ8duHLN3Db+SSDUWP+fDv1Ti69wmH5HqrdSGcl\n" + //
+                "EvoNStTRjFpnzj/7c5AkALWeZlRzcrBjeIFTtSdZvgluA14BnQXmRViC3tgOFMyU\n" + //
+                "I72wqCGuf7Y8cW/DSfSzBWFTO+A9uoj0oMKEaaLd1iVF4mctKf/atrHzy3Ny1/d9\n" + //
+                "WgbLLxiGtrNxVh78j9HCS4rs17AEC3OZnosUE3jCzLCHyQjwI+frkmINj5Qy4L3j\n" + //
+                "GJqxtIBBb9LwaCkkuV3g679/V4BhWKpDt6YIo/YYINRu42GhXSB9x13KhSMGe9vn\n" + //
+                "eQIFAKY6EqM=\n" + //
+                "-----END PUBLIC KEY-----\n";
+
+        PublicKey pk = KeyCheckTest.pkFromString(sfk);
+        try {
+            KeyCheck c = new KeyCheckROCA();
+            c.check(pk);
+            fail("Invalid key (ROCA vulnerable) accepted.");
+        } catch (GigiApiException gae) {
+            // expected
+        }
+
+    }
+
+    @Test
+    public void testROCAVulnerable3() throws GeneralSecurityException, IOException {
+
+        // D-TRUST Qualified Root CA 3 2014:PN
+        // https://crt.sh/?id=26310642&opt=cablint
+        String sfk = "-----BEGIN PUBLIC KEY-----\n" + //
+                "MIIBJDANBgkqhkiG9w0BAQEFAAOCAREAMIIBDAKCAQEAlpwnRwC1ogIM/Wywu3ys\n" + //
+                "HhREKeT56eDAMO+68dvz/mWL7dzFhIFHdehRpSpICx06tb7YpK6/XX9/0okTKajt\n" + //
+                "K0paM3mqZWNilpZnCzItFjwYjxKZL8Bgxww0ztqGD/2oHtmviZNO6yeaLYmm2Eqv\n" + //
+                "hXCVPUCcE17BPjybSZaW3ULaTiIQFYcCB5/utyXu3RT8ss2NBNoD9D4S5r3dMMJY\n" + //
+                "qUE/oojbg/4Y955M0S+yEUuv2dfbE+BCkZqgM05yk/wNr9L8F2f7cG2h/qjFUBE5\n" + //
+                "91kZXZ0g3lBhbKx9SUM8/Vq3WMmfDDpV2qk9wXC0sMgVAwTYLN1J3LWow/C+4Ffo\n" + //
+                "xQIFAI0kKjs=\n" + //
+                "-----END PUBLIC KEY-----\n";
+
+        PublicKey pk = KeyCheckTest.pkFromString(sfk);
+        try {
+            KeyCheck c = new KeyCheckROCA();
+            c.check(pk);
+            fail("Invalid key (ROCA vulnerable) accepted.");
+        } catch (GigiApiException gae) {
+            // expected
+        }
+
+    }
+
+}
diff --git a/tests/club/wpia/gigi/crypto/key/KeyCheckSmallFactorsTest.java b/tests/club/wpia/gigi/crypto/key/KeyCheckSmallFactorsTest.java
new file mode 100644 (file)
index 0000000..2d5e4cb
--- /dev/null
@@ -0,0 +1,67 @@
+package club.wpia.gigi.crypto.key;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+
+import org.junit.Test;
+
+import club.wpia.gigi.GigiApiException;
+
+public class KeyCheckSmallFactorsTest {
+
+    @Test
+    public void testSmallPrimesSaneKey() throws GeneralSecurityException, IOException {
+
+        // Normal public key generated with OpenSSL:
+        // openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:2048
+        // -pkeyopt rsa_keygen_pubexp:7331 2>/dev/null |
+        // openssl pkey -pubout -outform pem
+        String sfk = "-----BEGIN PUBLIC KEY-----\n" + //
+                "MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQEArcAPmy3RnXdwyFg3V9k1\n" + //
+                "RaFR/peHa3hLsmh25BInRVArbaMctSBaJBVZwQIgBdqjyITQQZP38i6k+WdsETn9\n" + //
+                "J491UDLKU3E3UG60ZS3BzcJllNdpn4g0IZROxmmUz2JlAXkGtIglmWWDx14qHSNj\n" + //
+                "ON58mc3ihfn/oWkPk2hk/csDxGQq5jSaBUwa9THBg9UQHHBqQbhp2nGfa5a5VRlI\n" + //
+                "0QeIy+8GmKlXYMchReUI25ksLOzaqETD0UXiAPyt+vpvkKCDjWGc3kjabn6OkuTt\n" + //
+                "na7N/52qrEC2ImuanYlzR5gv9jkbFF2PiMIEBD+3B0842rLx0X/lbXhRr1MtuHtN\n" + //
+                "tQICHKM=\n" + //
+                "-----END PUBLIC KEY-----\n";
+
+        PublicKey pk = KeyCheckTest.pkFromString(sfk);
+        try {
+            KeyCheck c = new KeyCheckSmallFactors();
+            c.check(pk);
+        } catch (GigiApiException gae) {
+            throw new Error("Valid key (regarding small factors) rejected.", gae);
+        }
+
+    }
+
+    @Test
+    public void testSmallPrimes() throws GeneralSecurityException, IOException {
+
+        // The following key is the above one multiplied by 7331.
+        String sfk = "-----BEGIN PUBLIC KEY-----\n" + //
+                "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQITb6L+6NEZsFNiuTY42LEg\n" + //
+                "iPqvDa1K+pXftgWEpTPalebLpKX/Ft11V09pQh/bB6QgNzNXxfBVXE2+UhyrsU+c\n" + //
+                "g+Esd55384MjBFI37W1U50Xi9VS1s3ls3ZoL2+GAbs6yeSzLA9bMt8YMtj2QAGxi\n" + //
+                "ZYtKKHLd4qYja0OZCkaED8ys4QB4flRWpbJn+4/Yoj5sXmcy2AP/SoPIRf09T/MQ\n" + //
+                "OerCZ/3p5blhOGZt1I3MqJNcCoK5oKkzkeQ3AkPqOjGo2qSXObJPMYBHKjIs2JHA\n" + //
+                "ioTVHwAOgsEfu69srVcgOzsleAVSeDNFWUv5BayWVlGpHtJi4mGHDdyLL7r2SfMC\n" + //
+                "Rj8CAhyj\n" + //
+                "-----END PUBLIC KEY-----\n";
+
+        PublicKey pk = KeyCheckTest.pkFromString(sfk);
+        try {
+            KeyCheck c = new KeyCheckSmallFactors();
+            c.check(pk);
+            fail("Invalid key (containing small factors) accepted.");
+        } catch (GigiApiException gae) {
+            // expected
+        }
+
+    }
+
+}
diff --git a/tests/club/wpia/gigi/crypto/key/KeyCheckTest.java b/tests/club/wpia/gigi/crypto/key/KeyCheckTest.java
new file mode 100644 (file)
index 0000000..aaca8b8
--- /dev/null
@@ -0,0 +1,39 @@
+package club.wpia.gigi.crypto.key;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+
+import org.junit.Test;
+
+import sun.security.util.DerValue;
+import sun.security.x509.X509Key;
+import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.util.PEM;
+
+public class KeyCheckTest {
+
+    public static PublicKey pkFromString(String pub) throws GeneralSecurityException, IOException {
+        byte[] data = PEM.decode("PUBLIC KEY", pub);
+        DerValue der = new DerValue(data);
+        PublicKey key = X509Key.parse(der);
+
+        return key;
+    }
+
+    @Test
+    public void testNullKey() {
+        try {
+            KeyCheck.checkKey(null);
+            fail("Providing a null key should fail!");
+        } catch (GigiApiException gae) {
+            // Expected failure
+        }
+
+        // Check that at least one key check has been loaded
+        assertFalse(KeyCheck.getChecks().isEmpty());
+    }
+
+}
index 43efda4..f9e43c7 100644 (file)
@@ -9,13 +9,9 @@ import org.junit.Test;
 
 import club.wpia.gigi.GigiApiException;
 import club.wpia.gigi.database.GigiPreparedStatement;
-import club.wpia.gigi.dbObjects.Country;
-import club.wpia.gigi.dbObjects.Name;
-import club.wpia.gigi.dbObjects.NamePart;
-import club.wpia.gigi.dbObjects.User;
-import club.wpia.gigi.dbObjects.Verification.VerificationType;
 import club.wpia.gigi.dbObjects.Country.CountryCodeType;
 import club.wpia.gigi.dbObjects.NamePart.NamePartType;
+import club.wpia.gigi.dbObjects.Verification.VerificationType;
 import club.wpia.gigi.testUtils.BusinessTest;
 import club.wpia.gigi.util.DayDate;
 import club.wpia.gigi.util.Notary;
@@ -83,7 +79,7 @@ public class TestVerificationMail extends BusinessTest {
 
         // verify preferred name only
         enterVerification(firstName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("John Doe: with 10 to total 10 Verification Points." + "\n" + requiresMore(40)));
 
@@ -91,7 +87,7 @@ public class TestVerificationMail extends BusinessTest {
         newAgent();
 
         enterVerification(firstName, secondName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("John Doe: with 10 to total 20 Verification Points." + "\n" + requiresMore(30)));
         assertThat(message, containsString("James Doe: with 10 to total 10 Verification Points." + "\n" + requiresMore(40)));
@@ -100,7 +96,7 @@ public class TestVerificationMail extends BusinessTest {
         newAgent();
 
         enterVerification(firstName, secondName, thirdName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("John Doe: with 10 to total 30 Verification Points." + "\n" + requiresMore(20)));
         assertThat(message, containsString("James Doe: with 10 to total 20 Verification Points." + "\n" + requiresMore(30)));
@@ -110,7 +106,7 @@ public class TestVerificationMail extends BusinessTest {
         newAgent();
 
         enterVerification(firstName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("John Doe: with 10 to total 40 Verification Points." + "\n" + requiresMore(10)));
 
@@ -118,7 +114,7 @@ public class TestVerificationMail extends BusinessTest {
         newAgent();
 
         enterVerification(firstName, secondName, thirdName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("John Doe: with 10 to total 50 Verification Points." + "\n" + "You can now issue client certificates with this name."));
         assertThat(message, containsString("James Doe: with 10 to total 30 Verification Points." + "\n" + requiresMore(20)));
@@ -129,7 +125,7 @@ public class TestVerificationMail extends BusinessTest {
         newAgent();
 
         enterVerification(firstName, secondName, thirdName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("John Doe: with 10 to total 60 Verification Points."));
         assertThat(message, containsString("James Doe: with 10 to total 40 Verification Points." + "\n" + requiresMore(10)));
@@ -140,7 +136,7 @@ public class TestVerificationMail extends BusinessTest {
         newAgent();
 
         enterVerification(firstName, secondName, thirdName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("John Doe: with 10 to total 70 Verification Points."));
         assertThat(message, containsString("James Doe: with 10 to total 50 Verification Points." + "\n" + "You can now issue client certificates with this name."));
@@ -151,7 +147,7 @@ public class TestVerificationMail extends BusinessTest {
         newAgent();
 
         enterVerification(firstName, secondName, thirdName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("John Doe: with 10 to total 80 Verification Points."));
         assertThat(message, containsString("James Doe: with 10 to total 60 Verification Points."));
@@ -162,7 +158,7 @@ public class TestVerificationMail extends BusinessTest {
         newAgent();
 
         enterVerification(firstName, secondName, thirdName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("John Doe: with 10 to total 90 Verification Points."));
         assertThat(message, containsString("James Doe: with 10 to total 70 Verification Points."));
@@ -173,7 +169,7 @@ public class TestVerificationMail extends BusinessTest {
         newAgent();
 
         enterVerification(firstName, secondName, thirdName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("John Doe: with 10 to total 100 Verification Points."));
         assertThat(message, containsString("James Doe: with 10 to total 80 Verification Points."));
@@ -184,7 +180,7 @@ public class TestVerificationMail extends BusinessTest {
         newAgent();
 
         enterVerification(firstName, secondName, thirdName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("John Doe: with 10 to total 110 Verification Points."));
         assertThat(message, containsString("James Doe: with 10 to total 90 Verification Points."));
@@ -206,24 +202,24 @@ public class TestVerificationMail extends BusinessTest {
         // verify preferred name only 5 times
         newAgent();
         enterVerification(firstName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
 
         newAgent();
         enterVerification(firstName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
 
         newAgent();
         enterVerification(firstName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
 
         newAgent();
         enterVerification(firstName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
 
         newAgent();
         enterVerification(firstName);
 
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("John Doe: with 10 to total 50 Verification Points." + "\n" + "You can now issue client certificates with this name."));
         assertThat(message, containsString(requiresMoreTotal(50)));
@@ -231,7 +227,7 @@ public class TestVerificationMail extends BusinessTest {
         // verify preferred name second name
         newAgent();
         enterVerification(secondName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("James Doe: with 10 to total 10 Verification Points." + "\n" + requiresMore(40)));
         assertThat(message, containsString(requiresMoreTotal(40)));
@@ -239,19 +235,19 @@ public class TestVerificationMail extends BusinessTest {
         // verify preferred name second name 4 more times
         newAgent();
         enterVerification(secondName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
 
         newAgent();
         enterVerification(secondName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
 
         newAgent();
         enterVerification(secondName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
 
         newAgent();
         enterVerification(secondName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("James Doe: with 10 to total 50 Verification Points." + "\n" + "You can now issue client certificates with this name."));
         assertThat(message, containsString("You can now apply for RA Agent status or code signing ability."));
@@ -259,14 +255,14 @@ public class TestVerificationMail extends BusinessTest {
         // get more than 100 VP in total
         newAgent();
         enterVerification(secondName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("James Doe: with 10 to total 60 Verification Points."));
 
         // verify third name
         newAgent();
         enterVerification(thirdName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("James John Doe: with 10 to total 10 Verification Points." + "\n" + requiresMore(40)));
 
@@ -300,7 +296,7 @@ public class TestVerificationMail extends BusinessTest {
 
         // add second Verification result first name 50 VP
         enterVerification(15, firstName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("John Doe: with 15 to total 50 Verification Points." + "\n" + "You can now issue client certificates with this name."));
         assertThat(message, containsString(requiresMoreTotal(50)));
@@ -326,10 +322,14 @@ public class TestVerificationMail extends BusinessTest {
 
         // add second Verification result first name 100 VP, second name 50 VP
         enterVerification(15, firstName, secondName);
-        message = getMailReceiver().receive().getMessage();
+        message = applicantMail();
         assertThat(message, containsString("RA-Agent Marianne Mustermann verified your name(s):"));
         assertThat(message, containsString("John Doe: with 15 to total 100 Verification Points."));
         assertThat(message, containsString("James Doe: with 15 to total 50 Verification Points." + "\n" + "You can now issue client certificates with this name."));
         assertThat(message, containsString("You can now apply for RA Agent status or code signing ability."));
     }
+
+    private String applicantMail() {
+        return getMailReceiver().receive(applicant.getEmail()).getMessage();
+    }
 }
index d1b799b..34723f9 100644 (file)
@@ -19,7 +19,7 @@ public class TestVerify extends ClientBusinessTest {
     @Test
     public void testVerify() throws GigiApiException {
         EmailAddress ea = new EmailAddress(u, "test@example.com", Locale.ENGLISH);
-        TestMail tm = getMailReceiver().receive();
+        TestMail tm = getMailReceiver().receive(ea.getAddress());
         Pattern p = Pattern.compile(".*hash=(.*)");
         Matcher m = p.matcher(tm.extractLink());
         assertTrue(m.matches());
index 854cca7..8d46cf2 100644 (file)
@@ -68,6 +68,12 @@ public class TestCertificateAdd extends ClientTest {
 
     KeyPair kp = generateKeypair();
 
+    /**
+     * This KeyPair is used for testing the KeyCheck for proper rejection of
+     * invalid keys. The generated keys suffers from small factors.
+     */
+    KeyPair kpBroken = generateBrokenKeypair();
+
     String csrf;
 
     public TestCertificateAdd() throws GeneralSecurityException, IOException {
@@ -383,4 +389,17 @@ public class TestCertificateAdd extends ClientTest {
         X509Certificate parsedLoginEnabled = createCertWithValidity("&validFrom=now&validity=1m", true);
         assertEquals(u, CertificateOwner.getByEnabledSerial(parsedLoginEnabled.getSerialNumber().toString(16).toLowerCase()));
     }
+
+    @Test
+    public void testInvalidKeyInCSR() throws IOException, GeneralSecurityException {
+        PKCS10Attributes atts = buildAtts(new ObjectIdentifier[] {
+                CertificateRequest.OID_KEY_USAGE_SSL_SERVER
+        }, new DNSName(uniq + ".tld"));
+
+        String pem = generatePEMCSR(kpBroken, "CN=a." + uniq + ".tld", atts);
+
+        HttpURLConnection huc = post(CertificateAdd.PATH, "CSR=" + URLEncoder.encode(pem, "UTF-8"));
+        assertThat(IOUtils.readURL(huc), hasError());
+    }
+
 }
index 84ad60d..1a42d20 100644 (file)
@@ -47,6 +47,7 @@ public class TestMailManagement extends ClientTest {
         String newMail = createUniqueName() + "uni@example.org";
         assertNull(addMail(newMail));
         assertTrue(existsEmail(newMail));
+        getMailReceiver().receive(newMail);
     }
 
     @Test
@@ -62,6 +63,7 @@ public class TestMailManagement extends ClientTest {
         String newMail = u + "uni@eXample.org";
         assertNull(addMail(newMail));
         assertTrue(existsEmail(newMail.toLowerCase()));
+        getMailReceiver().receive(newMail.toLowerCase());
 
         String newMail2 = u + "uni@eXamPlE.org";
         assertNotNull(addMail(newMail2));
@@ -71,6 +73,7 @@ public class TestMailManagement extends ClientTest {
         assertNull(addMail(newMail3));
         assertTrue(existsEmail(newMail.toLowerCase()));
         assertTrue(existsEmail(newMail3.toLowerCase()));
+        getMailReceiver().receive(newMail3.toLowerCase());
     }
 
     private String addMail(String newMail) throws IOException, MalformedURLException, UnsupportedEncodingException {
@@ -100,7 +103,7 @@ public class TestMailManagement extends ClientTest {
         EmailAddress addr = new EmailAddress(u, createUniqueName() + "test@test.tld", Locale.ENGLISH);
         assertNotNull(executeBasicWebInteraction(cookie, path, "default=" + addr.getId()));
         assertNotEquals(User.getById(u.getId()).getEmail(), addr.getAddress());
-        getMailReceiver().clearMails();
+        getMailReceiver().receive(addr.getAddress());
     }
 
     @Test
@@ -116,7 +119,7 @@ public class TestMailManagement extends ClientTest {
         assertNotEquals(id, -1);
         assertNotNull(executeBasicWebInteraction(cookie, path, "default=" + id));
         assertNotEquals(User.getById(u.getId()).getEmail(), u2.getEmail());
-        getMailReceiver().clearMails();
+        getMailReceiver().assertEmpty();
     }
 
     @Test
index 35c482d..1efc5f7 100644 (file)
@@ -35,10 +35,9 @@ public class TestPasswordResetExternal extends ClientTest {
         String error = fetchStartErrorMessage(IOUtils.readURL(uc));
         assertNull(error);
 
-        TestMail mail = getMailReceiver().receive();
+        TestMail mail = getMailReceiver().receive(this.u.getEmail());
         assertThat(mail.getSubject(), containsString("Verification"));
-        mail = getMailReceiver().receive();
-        assertEquals(mail.getTo(), this.u.getEmail());
+        mail = getMailReceiver().receive(this.u.getEmail());
         String link = mail.extractLink();
         String npw = TEST_PASSWORD + "'";
         System.out.println(link);
index 8a439a3..9bcaa82 100644 (file)
@@ -27,15 +27,17 @@ public class TestSEAdminNotificationMail extends ClientTest {
 
     private int targetID;
 
+    private String targetEmail;
+
     public TestSEAdminNotificationMail() throws IOException, GigiApiException {
         grant(u, Group.SUPPORTER);
         cookie = login(email, TEST_PASSWORD);
         assertEquals(302, post(cookie, SupportEnterTicketPage.PATH, "ticketno=a20140808.8&setTicket=action", 0).getResponseCode());
 
-        String email = createUniqueName() + "@example.com";
+        targetEmail = createUniqueName() + "@example.com";
         String fname = "Först";
         String lname = "Secönd";
-        targetID = createVerifiedUser(fname, lname, email, TEST_PASSWORD);
+        targetID = createVerifiedUser(fname, lname, targetEmail, TEST_PASSWORD);
     }
 
     @Test
@@ -44,22 +46,19 @@ public class TestSEAdminNotificationMail extends ClientTest {
         executeBasicWebInteraction(cookie, SupportUserDetailsPage.PATH + targetID + "/", "dobd=1&dobm=2&doby=2000&detailupdate", 0);
 
         // mail to support
-        String message = getMailReceiver().receive().getMessage();
+        String message = getMailReceiver().receive(ServerConstants.getSupportMailAddress()).getMessage();
         assertThat(message, containsString("The DoB was changed"));
         assertThat(message, containsString("supporter " + u.getPreferredName().toString() + " triggered:"));
         // mail to user
-        message = getMailReceiver().receive().getMessage();
+        message = getMailReceiver().receive(targetEmail).getMessage();
         assertThat(message, containsString("The DoB in your account was changed to 2000-02-01."));
     }
 
     @Test
     public void testPasswordReset() throws MalformedURLException, IOException {
         executeBasicWebInteraction(cookie, SupportUserDetailsPage.PATH + targetID + "/", "aword=SecretWord&resetPass", 0);
-        TestMail tm;
-        String targetMail = ServerConstants.getSupportMailAddress();
-        do {
-            tm = getMailReceiver().receive();
-        } while ( !tm.getTo().equals(targetMail));
+        getMailReceiver().receive(targetEmail);
+        TestMail tm = getMailReceiver().receive(ServerConstants.getSupportMailAddress());
         assertThat(tm.getMessage(), containsString("A password reset was triggered and an email was sent to user."));
     }
 
@@ -72,10 +71,10 @@ public class TestSEAdminNotificationMail extends ClientTest {
         Group.CODESIGNING.getName().output(pw, Language.getInstance(Locale.ENGLISH), new HashMap<String, Object>());
 
         // mail to support
-        String message = getMailReceiver().receive().getMessage();
+        String message = getMailReceiver().receive(ServerConstants.getSupportMailAddress()).getMessage();
         assertThat(message, containsString("The group permission '" + sw.toString() + "' was granted."));
         // mail to user
-        message = getMailReceiver().receive().getMessage();
+        message = getMailReceiver().receive(targetEmail).getMessage();
         assertThat(message, containsString("The group permission '" + sw.toString() + "' was granted to your account."));
     }
 
@@ -88,10 +87,10 @@ public class TestSEAdminNotificationMail extends ClientTest {
         Group.CODESIGNING.getName().output(pw, Language.getInstance(Locale.ENGLISH), new HashMap<String, Object>());
 
         // mail to support
-        String message = getMailReceiver().receive().getMessage();
+        String message = getMailReceiver().receive(ServerConstants.getSupportMailAddress()).getMessage();
         assertThat(message, containsString("The group permission '" + sw.toString() + "' was revoked."));
         // mail to user
-        message = getMailReceiver().receive().getMessage();
+        message = getMailReceiver().receive(targetEmail).getMessage();
         assertThat(message, containsString("The group permission '" + sw.toString() + "' was revoked from your account."));
     }
 
@@ -105,13 +104,13 @@ public class TestSEAdminNotificationMail extends ClientTest {
         User target = User.getById(targetID);
 
         // mail to support
-        String message = getMailReceiver().receive().getMessage();
+        String message = getMailReceiver().receive(ServerConstants.getSupportMailAddress()).getMessage();
         assertThat(message, containsString("The group permission '" + sw.toString() + "' was granted."));
         // mail to user
-        message = getMailReceiver().receive().getMessage();
+        message = getMailReceiver().receive(targetEmail).getMessage();
         assertThat(message, containsString("The group permission '" + sw.toString() + "' was granted to your account."));
         // mail to board
-        message = getMailReceiver().receive().getMessage();
+        message = getMailReceiver().receive(ServerConstants.getBoardMailAddress()).getMessage();
         assertThat(message, containsString("The group permission '" + sw.toString() + "' was granted for '" + target.getPreferredName().toString() + "'."));
     }
 
@@ -125,13 +124,13 @@ public class TestSEAdminNotificationMail extends ClientTest {
         User target = User.getById(targetID);
 
         // mail to support
-        String message = getMailReceiver().receive().getMessage();
+        String message = getMailReceiver().receive(ServerConstants.getSupportMailAddress()).getMessage();
         assertThat(message, containsString("The group permission '" + sw.toString() + "' was revoked."));
         // mail to user
-        message = getMailReceiver().receive().getMessage();
+        message = getMailReceiver().receive(targetEmail).getMessage();
         assertThat(message, containsString("The group permission '" + sw.toString() + "' was revoked from your account."));
         // mail to board
-        message = getMailReceiver().receive().getMessage();
+        message = getMailReceiver().receive(ServerConstants.getBoardMailAddress()).getMessage();
         assertThat(message, containsString("The group permission '" + sw.toString() + "' was revoked for '" + target.getPreferredName().toString() + "'."));
     }
 
@@ -141,10 +140,10 @@ public class TestSEAdminNotificationMail extends ClientTest {
         User user = User.getById(targetID);
 
         // mail to support
-        String message = getMailReceiver().receive().getMessage();
+        String message = getMailReceiver().receive(ServerConstants.getSupportMailAddress()).getMessage();
         assertThat(message, containsString("All certificates in the account " + user.getPreferredName().toString()));
         // mail to user
-        message = getMailReceiver().receive().getMessage();
+        message = getMailReceiver().receive(targetEmail).getMessage();
         assertThat(message, containsString("All certificates in your account have been revoked."));
     }
 }
index dd7d197..e61f559 100644 (file)
@@ -39,7 +39,7 @@ public class TestSEAdminPageCertSearch extends ClientTest {
         cookie = login(email, TEST_PASSWORD);
         assertEquals(302, post(cookie, SupportEnterTicketPage.PATH, "ticketno=a20140808.8&setTicket=action", 0).getResponseCode());
 
-        certMail = uniq + "_certOwner@example.com";
+        certMail = uniq + "_certowner@example.com";
         int id = createVerifiedUser("fn", "ln", certMail, TEST_PASSWORD);
         User u1 = User.getById(id);
         KeyPair kp = generateKeypair();
@@ -73,22 +73,11 @@ public class TestSEAdminPageCertSearch extends ClientTest {
     public void testRevoke() throws IOException {
         URLConnection conn = post(Certificates.SUPPORT_PATH + "/" + c.getSerial(), "action=revoke");
         assertEquals("https://" + ServerConstants.getHostNamePortSecure(Host.WWW) + Certificates.SUPPORT_PATH + "/" + c.getSerial(), conn.getHeaderField("Location"));
-        boolean hadSupport = false;
-        boolean hadUser = false;
         for (int i = 0; i < 2; i++) {
-            TestMail tm = getMailReceiver().receive();
-            if (tm.getTo().equals(ServerConstants.getSupportMailAddress())) {
-                hadSupport = true;
-            } else if (tm.getTo().equals(certMail)) {
-                hadUser = true;
-            } else {
-                throw new Error("Unknown mail:" + tm.getTo());
-            }
+            TestMail tm = getMailReceiver().receive(i == 0 ? ServerConstants.getSupportMailAddress() : certMail);
             assertThat(tm.getMessage(), CoreMatchers.containsString(certMail));
             assertThat(tm.getMessage(), CoreMatchers.containsString(c.getSerial()));
         }
-        assertTrue(hadSupport);
-        assertTrue(hadUser);
         assertEquals(CertificateStatus.REVOKED, c.getStatus());
     }
 
index 909dd03..72930b3 100644 (file)
@@ -56,7 +56,7 @@ public class TestSEAdminPageDetails extends ClientTest {
         int id = createVerifiedUser(fname, lname, email, TEST_PASSWORD);
         String email2 = createUniqueName() + "@example.com";
         EmailAddress ea = new EmailAddress(User.getById(id), email2, Locale.ENGLISH);
-        getMailReceiver().receive().verify();
+        getMailReceiver().receive(email2).verify();
         // Refresh email Object
         ObjectCache.clearAllCaches();
         ea = EmailAddress.getById(ea.getId());
@@ -108,6 +108,10 @@ public class TestSEAdminPageDetails extends ClientTest {
         assertEquals(3, logCountAdmin(id));
         assertEquals(3, logCountUser(clientCookie));
 
+        // discarding all 6 mails
+        for (int i = 0; i < 6; i++) {
+            getMailReceiver().receive(null);
+        }
     }
 
     @Test
index af9cbb6..46ffe0d 100644 (file)
@@ -187,6 +187,7 @@ public class KeyCompromiseTest extends ClientTest {
         if (error == null) {
             assertThat(result, hasNoError());
             assertRevoked(result);
+            getMailReceiver().receive(u.getEmail());
         } else if ("error".equals(error)) {
             assertThat(result, hasError());
             assertNotEquals(CertificateStatus.REVOKED, cert.getStatus());
index 06a5ba5..7dfb415 100644 (file)
@@ -92,7 +92,7 @@ public class KeyCompromiseTestMessage extends ClientTest {
         String data = IOUtils.readURL(rc);
         assertThat(data, hasError());
         assertThat(data, CoreMatchers.containsString(HTMLEncoder.encodeHTML("message may not contain '---'")));
-        assertNull(getMailReceiver().poll());
+        assertNull(getMailReceiver().poll(null));
         assertEquals(CertificateStatus.ISSUED, cert.getStatus());
     }
 
@@ -108,7 +108,7 @@ public class KeyCompromiseTestMessage extends ClientTest {
     private TestMail reportCompromiseAndCheck(String params) throws IOException, UnsupportedEncodingException, CertificateEncodingException, GeneralSecurityException {
         HttpURLConnection huc = reportCompromise(params);
         assertThat(IOUtils.readURL(huc), hasNoError());
-        TestMail rc = getMailReceiver().receive();
+        TestMail rc = getMailReceiver().receive(email);
         assertEquals(u.getEmail(), rc.getTo());
         assertThat(rc.getMessage(), CoreMatchers.containsString(cert.getSerial()));
         assertEquals(CertificateStatus.REVOKED, cert.getStatus());
index aee9ad5..d9de3a9 100644 (file)
@@ -45,14 +45,14 @@ public class RegisterPageTest extends ManagedTest {
     public void testSuccess() throws IOException, InterruptedException {
         long uniq = System.currentTimeMillis();
         registerUser("ab", "b", "correct" + uniq + "@email.de", TEST_PASSWORD);
-        assertSuccessfullRegMail();
+        assertSuccessfullRegMail("correct" + uniq + "@email.de");
 
         String defaultSignup = "fname=" + URLEncoder.encode("ab", "UTF-8") + "&lname=" + URLEncoder.encode("b", "UTF-8") + "&pword1=" + URLEncoder.encode(TEST_PASSWORD, "UTF-8") + "&pword2=" + URLEncoder.encode(TEST_PASSWORD, "UTF-8") + "&day=1&month=1&year=1910&tos_agree=1&mname=mn&suffix=sf&email=";
 
         String query = defaultSignup + URLEncoder.encode("correct3_" + uniq + "@email.de", "UTF-8") + "&general=1&country=1&regional=1&radius=1&name-type=western";
         String data = fetchStartErrorMessage(runRegister(query));
         assertNull(data);
-        assertSuccessfullRegMail();
+        assertSuccessfullRegMail("correct3_" + uniq + "@email.de");
 
         getMailReceiver().setEmailCheckError("400 Greylisted");
         getMailReceiver().setApproveRegex(Pattern.compile("a"));
@@ -60,12 +60,12 @@ public class RegisterPageTest extends ManagedTest {
         data = fetchStartErrorMessage(runRegister(query));
         assertNotNull(data);
 
-        assertNull(getMailReceiver().poll());
+        assertNull(getMailReceiver().poll(null));
 
     }
 
-    private void assertSuccessfullRegMail() {
-        String link = getMailReceiver().receive().extractLink();
+    private void assertSuccessfullRegMail(String mail) {
+        String link = getMailReceiver().receive(mail).extractLink();
         assertTrue(link, link.startsWith("https://"));
     }
 
@@ -188,6 +188,7 @@ public class RegisterPageTest extends ManagedTest {
     public void testDoubleMail() throws IOException {
         long uniq = System.currentTimeMillis();
         registerUser("RegisterTest", "User", "testmail" + uniq + "@example.com", TEST_PASSWORD);
+        getMailReceiver().receive("testmail" + uniq + "@example.com");
         try {
             registerUser("RegisterTest", "User", "testmail" + uniq + "@example.com", TEST_PASSWORD);
             throw new Error("Registering a user with the same email needs to fail.");
@@ -225,6 +226,7 @@ public class RegisterPageTest extends ManagedTest {
         assertNull(data);
         User u = User.getByEmail(email);
         assertEquals("DE", u.getResidenceCountry().getCode());
+        getMailReceiver().receive(email);
     }
 
     @Test
@@ -239,5 +241,6 @@ public class RegisterPageTest extends ManagedTest {
         assertNull(data);
         User u = User.getByEmail(email);
         assertEquals(null, u.getResidenceCountry());
+        getMailReceiver().receive(email);
     }
 }
index 7a6aed4..5b61f23 100644 (file)
@@ -101,6 +101,7 @@ public class TestVerification extends ManagedTest {
         String body = executeSuccess("date=" + validVerificationDateString() + "&location=testcase&countryCode=DE&certify=1&rules=1&assertion=1&points=10");
         assertThat(body, containsString("10"));
         assertThat(body, containsString(applicantM));
+        getMailReceiver().receive(applicantM);
     }
 
     @Test
@@ -129,6 +130,7 @@ public class TestVerification extends ManagedTest {
         uc.getOutputStream().write(("date=" + validVerificationDateString() + "&location=testcase&countryCode=DE&certify=1&rules=1&assertion=1&points=10").getBytes("UTF-8"));
         uc.getOutputStream().flush();
         assertEquals(500, uc.getResponseCode());
+        uc.getErrorStream().close();
     }
 
     @Test
@@ -138,6 +140,7 @@ public class TestVerification extends ManagedTest {
         uc.getOutputStream().write(("date=" + validVerificationDateString() + "&location=testcase&countryCode=DE&certify=1&rules=1&assertion=1&points=10&csrf=aragc").getBytes("UTF-8"));
         uc.getOutputStream().flush();
         assertEquals(500, uc.getResponseCode());
+        uc.getErrorStream().close();
     }
 
     @Test
@@ -163,6 +166,7 @@ public class TestVerification extends ManagedTest {
         String error = fetchStartErrorMessage(IOUtils.readURL(uc));
         if (succeed) {
             assertNull(error);
+            getMailReceiver().receive(applicantM);
         } else {
             assertTrue(error, !error.startsWith("</div>"));
             assertThat(error, containsString("changed his personal details"));
@@ -184,11 +188,13 @@ public class TestVerification extends ManagedTest {
         c.add(Calendar.HOUR_OF_DAY, 12);
 
         executeSuccess("date=" + sdf.format(new Date(c.getTimeInMillis())) + "&location=testcase&countryCode=DE&certify=1&rules=1&assertion=1&points=10");
+        getMailReceiver().receive(applicantM);
     }
 
     @Test
     public void testVerifyFormPastInRange() throws IOException {
         executeSuccess("date=" + validVerificationDateString() + "&location=testcase&countryCode=DE&certify=1&rules=1&assertion=1&points=10");
+        getMailReceiver().receive(applicantM);
     }
 
     @Test
@@ -200,6 +206,7 @@ public class TestVerification extends ManagedTest {
         c.add(Calendar.DAY_OF_MONTH, 1);
 
         executeSuccess("date=" + sdf.format(new Date(c.getTimeInMillis())) + "&location=testcase&countryCode=DE&certify=1&rules=1&assertion=1&points=10");
+        getMailReceiver().receive(applicantM);
     }
 
     @Test
@@ -234,7 +241,8 @@ public class TestVerification extends ManagedTest {
     @Test
     public void testVerifyListingValid() throws IOException, GigiApiException {
         String uniqueLoc = createUniqueName();
-        execute("date=" + validVerificationDateString() + "&location=" + uniqueLoc + "&countryCode=DE&certify=1&rules=1&assertion=1&points=10");
+        executeSuccess("date=" + validVerificationDateString() + "&location=" + uniqueLoc + "&countryCode=DE&certify=1&rules=1&assertion=1&points=10");
+        getMailReceiver().receive(applicantM);
 
         String cookie = login(applicantM, TEST_PASSWORD);
         URLConnection url = get(cookie, Points.PATH);
@@ -248,6 +256,8 @@ public class TestVerification extends ManagedTest {
     public void testAgentListingValid() throws IOException, GigiApiException {
         String uniqueLoc = createUniqueName();
         executeSuccess("date=" + validVerificationDateString() + "&location=" + uniqueLoc + "&countryCode=DE&certify=1&rules=1&assertion=1&points=10");
+        getMailReceiver().receive(applicantM);
+
         String cookie = login(agentM, TEST_PASSWORD);
         URLConnection url = get(cookie, Points.PATH);
         String resp = IOUtils.readURL(url);
@@ -315,6 +325,7 @@ public class TestVerification extends ManagedTest {
         // enter second entry
         String uniqueLoc = createUniqueName();
         executeSuccess("date=" + validVerificationDateString() + "&location=" + uniqueLoc + "&countryCode=DE&certify=1&rules=1&assertion=1&points=10");
+        getMailReceiver().receive(applicantM);
 
         // enter third entry on the same day
         URLConnection uc = get(cookie, VerifyPage.PATH);
@@ -331,7 +342,7 @@ public class TestVerification extends ManagedTest {
 
     @Test
     public void testRANotificationSet() throws IOException, GigiApiException {
-        getMailReceiver().clearMails();
+        getMailReceiver().assertEmpty();
 
         User users[] = User.findByEmail(agentM);
         assertTrue("user RA Agent not found", users != null && users.length > 0);
@@ -346,18 +357,15 @@ public class TestVerification extends ManagedTest {
         // enter verification
         String uniqueLoc = createUniqueName();
         executeSuccess("date=" + validVerificationDateString() + "&location=" + uniqueLoc + "&countryCode=DE&certify=1&rules=1&assertion=1&points=10");
-        TestMail tm;
-
-        do {
-            tm = getMailReceiver().receive();
-        } while ( !tm.getTo().equals(targetMail));
+        getMailReceiver().receive(applicantM);
+        TestMail tm = getMailReceiver().receive(targetMail);
         assertThat(tm.getMessage(), containsString("You entered a verification for the account with email address " + applicantM));
 
     }
 
     @Test
     public void testRANotificationNotSet() throws IOException, GigiApiException {
-        getMailReceiver().clearMails();
+        getMailReceiver().assertEmpty();
 
         User users[] = User.findByEmail(agentM);
         assertTrue("user RA Agent not found", users != null && users.length > 0);
@@ -373,7 +381,7 @@ public class TestVerification extends ManagedTest {
 
         TestMail tm;
 
-        tm = getMailReceiver().receive();
+        tm = getMailReceiver().receive(applicantM);
         assertThat(tm.getMessage(), not(containsString("You entered a verification for the account with email address " + applicantM)));
 
     }
index 4285e8b..f970cb2 100644 (file)
@@ -75,7 +75,7 @@ public class TestDNS extends PingTest {
                 "&adddomain&csrf=" + csrf;
         String p2 = sendDomainForm(content);
 
-        TestMail mail = getMailReceiver().receive();
+        TestMail mail = getMailReceiver().receive("postmaster@" + test);
         if (emailVariant == 0) {
             mail.verify();
         }
index 8dc041f..ae29f97 100644 (file)
@@ -76,7 +76,7 @@ public class TestHTTP extends PingTest {
                 "&adddomain&csrf=" + csrf;
         String p2 = sendDomainForm(content);
 
-        TestMail mail = getMailReceiver().receive();
+        TestMail mail = getMailReceiver().receive("postmaster@" + test);
         if (emailVariant == 0) {
             mail.verify();
         }
@@ -175,7 +175,7 @@ public class TestHTTP extends PingTest {
         }
         waitForPings(2);
 
-        TestMail mail = getMailReceiver().receive();
+        TestMail mail = getMailReceiver().receive("postmaster@" + test);
         mail.verify();
 
         String newcontent = IOUtils.readURL(get(p2));
index 9b93c77..e4c5de0 100644 (file)
@@ -45,11 +45,11 @@ import club.wpia.gigi.GigiApiException;
 import club.wpia.gigi.database.DatabaseConnection;
 import club.wpia.gigi.database.DatabaseConnection.Link;
 import club.wpia.gigi.dbObjects.Certificate;
+import club.wpia.gigi.dbObjects.Certificate.CSRType;
 import club.wpia.gigi.dbObjects.CertificateProfile;
 import club.wpia.gigi.dbObjects.Digest;
 import club.wpia.gigi.dbObjects.Job;
 import club.wpia.gigi.dbObjects.User;
-import club.wpia.gigi.dbObjects.Certificate.CSRType;
 import club.wpia.gigi.testUtils.IOUtils;
 import club.wpia.gigi.testUtils.PingTest;
 import club.wpia.gigi.testUtils.TestEmailReceiver.TestMail;
@@ -206,7 +206,7 @@ public class TestSSL extends PingTest {
         boolean secondsSucceeds = sslVariant != 0;
         // assertTrue(secondsSucceeds ^ accept2);
 
-        TestMail mail = getMailReceiver().receive();
+        TestMail mail = getMailReceiver().receive("postmaster@" + test);
         if (emailVariant == 0) {
             mail.verify();
         }
index c660714..940c157 100644 (file)
@@ -30,7 +30,7 @@ public class TestTemplateMail extends BusinessTest {
     public void testSimple() throws IOException {
         vars.put("var", "val");
         testExecuteMail(vars, "Subject: subj\n\n<?=$var?>l");
-        TestMail tm = getMailReceiver().receive();
+        TestMail tm = getMailReceiver().receive(TEST_MAIL);
         assertEquals(MailTemplate.SUBJECT_TAG + "subj", tm.getSubject());
         assertThat(tm.getMessage(), startsWith("vall"));
     }
@@ -40,7 +40,7 @@ public class TestTemplateMail extends BusinessTest {
         vars.put("var", "val\">");
         vars.put("var2", "sl\">");
         testExecuteMail(vars, "Subject: a<?=$var?>b\n\n<?=$var2?>l");
-        TestMail tm = getMailReceiver().receive();
+        TestMail tm = getMailReceiver().receive(TEST_MAIL);
         assertEquals(MailTemplate.SUBJECT_TAG + "aval\">b", tm.getSubject());
         assertThat(tm.getMessage(), startsWith("sl\">l"));
 
@@ -49,7 +49,7 @@ public class TestTemplateMail extends BusinessTest {
     @Test
     public void testTranslate() throws IOException {
         testExecuteMail(vars, "Subject: a<?=_a<?>b\n\nc<?=_b\"?>l");
-        TestMail tm = getMailReceiver().receive();
+        TestMail tm = getMailReceiver().receive(TEST_MAIL);
         assertEquals(MailTemplate.SUBJECT_TAG + "aa<b", tm.getSubject());
         assertThat(tm.getMessage(), startsWith("cb\"l"));
 
index 727310d..28da80f 100644 (file)
@@ -70,12 +70,12 @@ public abstract class BusinessTest extends ConfiguredTest {
         }
 
         @Override
-        public void clearMails() {
+        public void assertEmpty() {
             mails.clear();
         }
 
         @Override
-        public TestMail receive() {
+        public TestMail receive(String to) {
             TestMail poll;
             try {
                 poll = mails.poll(30, TimeUnit.SECONDS);
@@ -85,6 +85,9 @@ public abstract class BusinessTest extends ConfiguredTest {
             if (poll == null) {
                 throw new AssertionError("Mail receiving timed out");
             }
+            if (to != null) {
+                assertEquals(to, poll.getTo());
+            }
             return poll;
         }
 
@@ -99,7 +102,7 @@ public abstract class BusinessTest extends ConfiguredTest {
         }
 
         @Override
-        public TestMail poll() {
+        public TestMail poll(String to) {
             throw new Error("Currently unimplemented");
         }
 
index 4dd091e..16abb06 100644 (file)
@@ -8,10 +8,17 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.math.BigInteger;
 import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
 import java.security.Signature;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
 import java.sql.SQLException;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
@@ -177,6 +184,96 @@ public abstract class ConfiguredTest {
         return keyPair;
     }
 
+    public static KeyPair generateBrokenKeypair() throws GeneralSecurityException {
+        KeyPair keyPair = null;
+        File f = new File("testBrokenKeypair");
+        if (f.exists()) {
+            try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f))) {
+                keyPair = (KeyPair) ois.readObject();
+            } catch (ClassNotFoundException e) {
+                e.printStackTrace();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } else {
+            // -----BEGIN SHAMELESSLY ADAPTED BLOCK-----
+            /**
+             * Modified original RSA key generator to use three primes with one
+             * prime set to fixed value to allow simple checking for such faulty
+             * keys.
+             *
+             * @link sun.security.rsa.RSAKeyPairGenerator#generateKeyPair
+             */
+
+            KeyFactory factory = KeyFactory.getInstance("RSA");
+            Random random = new SecureRandom();
+            int keySize = 4096;
+            long r_lv = 7331;
+
+            int lp = (keySize + 1) >> 1;
+            int lr = BigInteger.valueOf(r_lv).bitLength();
+            int lq = keySize - lp - lr;
+
+            BigInteger e = BigInteger.valueOf(7331);
+
+            keyPair = null;
+            while (keyPair == null) {
+                // generate two random primes of size lp/lq
+                BigInteger p, q, r, n;
+
+                p = BigInteger.probablePrime(lp, random);
+                r = BigInteger.valueOf(r_lv);
+                do {
+                    q = BigInteger.probablePrime(lq, random);
+
+                    // convention is for p > q > r
+                    if (p.compareTo(q) < 0) {
+                        BigInteger tmp = p;
+                        p = q;
+                        q = tmp;
+                    }
+
+                    // modulus n = p * q * r
+                    n = p.multiply(q).multiply(r);
+
+                    // even with correctly sized p, q and r, there is a chance
+                    // that n will be one bit short. re-generate the smaller
+                    // prime if so.
+                } while (n.bitLength() < keySize);
+
+                // phi = (p - 1) * (q - 1) * (r - 1) must be relative prime to e
+                // otherwise RSA just won't work ;-)
+                BigInteger p1 = p.subtract(BigInteger.ONE);
+                BigInteger q1 = q.subtract(BigInteger.ONE);
+                BigInteger r1 = r.subtract(BigInteger.ONE);
+                BigInteger phi = p1.multiply(q1).multiply(r1);
+
+                // generate new p and q until they work. typically
+                if (e.gcd(phi).equals(BigInteger.ONE) == false) {
+                    continue;
+                }
+
+                // private exponent d is the inverse of e mod phi
+                BigInteger d = e.modInverse(phi);
+
+                RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(n, e);
+                RSAPrivateKeySpec privateSpec = new RSAPrivateKeySpec(n, d);
+                PublicKey publicKey = factory.generatePublic(publicSpec);
+                PrivateKey privateKey = factory.generatePrivate(privateSpec);
+                keyPair = new KeyPair(publicKey, privateKey);
+            }
+            // -----END SHAMELESSLY ADAPTED BLOCK-----
+
+            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f))) {
+                oos.writeObject(keyPair);
+                oos.close();
+            } catch (IOException ioe) {
+                ioe.printStackTrace();
+            }
+        }
+        return keyPair;
+    }
+
     public static String generatePEMCSR(KeyPair kp, String dn) throws GeneralSecurityException, IOException {
         return generatePEMCSR(kp, dn, new PKCS10Attributes());
     }
@@ -249,7 +346,7 @@ public abstract class ConfiguredTest {
     public void verify(Domain d) {
         try {
             d.addPing(DomainPingType.EMAIL, "admin");
-            TestMail testMail = getMailReceiver().receive();
+            TestMail testMail = getMailReceiver().receive("admin@" + d.getSuffix());
             testMail.verify();
             assertTrue(d.isVerified());
         } catch (GigiApiException e) {
index 4d1bc10..139c554 100644 (file)
@@ -6,14 +6,14 @@ import club.wpia.gigi.testUtils.TestEmailReceiver.TestMail;
 
 public interface MailReceiver {
 
-    void clearMails();
+    void assertEmpty();
 
-    TestMail receive();
+    TestMail receive(String to);
 
     void setApproveRegex(Pattern compiled);
 
     void setEmailCheckError(String string);
 
-    TestMail poll();
+    TestMail poll(String to);
 
 }
index 3eeda2c..a2eb449 100644 (file)
@@ -291,7 +291,7 @@ public class ManagedTest extends ConfiguredTest {
     public static int createVerifiedUser(String firstName, String lastName, String email, String password) {
         registerUser(firstName, lastName, email, password);
         try {
-            ter.receive().verify();
+            ter.receive(email).verify();
 
             try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `id` FROM `users` WHERE `email`=?")) {
                 ps.setString(1, email);
@@ -496,11 +496,10 @@ public class ManagedTest extends ConfiguredTest {
 
     public EmailAddress createVerifiedEmail(User u, String email) throws InterruptedException, GigiApiException {
         EmailAddress addr = new EmailAddress(u, email, Locale.ENGLISH);
-        TestMail testMail = getMailReceiver().receive();
-        assertEquals(addr.getAddress(), testMail.getTo());
+        TestMail testMail = getMailReceiver().receive(addr.getAddress());
         String hash = testMail.extractLink().substring(testMail.extractLink().lastIndexOf('=') + 1);
         addr.verify(hash);
-        getMailReceiver().clearMails();
+        getMailReceiver().assertEmpty();
         return addr;
     }
 
index 01dbe78..9bba9b3 100644 (file)
@@ -1,5 +1,7 @@
 package club.wpia.gigi.testUtils;
 
+import static org.junit.Assert.*;
+
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
@@ -83,6 +85,10 @@ public final class TestEmailReceiver extends EmailProvider implements Runnable,
             uc.getInputStream().close();
         }
 
+        @Override
+        public String toString() {
+            return "TestMail: " + subject + " for " + to;
+        }
     }
 
     private Socket s;
@@ -130,7 +136,7 @@ public final class TestEmailReceiver extends EmailProvider implements Runnable,
      * @see #poll()
      */
     @Override
-    public TestMail receive() {
+    public TestMail receive(String to) {
         TestMail poll;
 
         try {
@@ -142,6 +148,9 @@ public final class TestEmailReceiver extends EmailProvider implements Runnable,
         if (poll == null) {
             throw new AssertionError("Mail receiving timed out");
         }
+        if (to != null) {
+            assertEquals(to, poll.getTo());
+        }
 
         return poll;
     }
@@ -154,8 +163,12 @@ public final class TestEmailReceiver extends EmailProvider implements Runnable,
      *         has been sent.
      * @see #receive()
      */
-    public TestMail poll() {
-        return mails.poll();
+    public TestMail poll(String to) {
+        TestMail tm = mails.poll();
+        if (tm != null && to != null) {
+            assertEquals(to, tm.getTo());
+        }
+        return tm;
     }
 
     @Override
@@ -226,19 +239,21 @@ public final class TestEmailReceiver extends EmailProvider implements Runnable,
      * Removes all queued mails.
      */
     @Override
-    public void clearMails() {
+    public void assertEmpty() {
+        int originalSize = mails.size();
         mails.clear();
+        assertEquals("test case should consume all produced emails", 0, originalSize);
     }
 
     /**
      * Resets this class to its initial state
      * 
-     * @see #clearMails()
+     * @see #assertEmpty()
      * @see #setApproveRegex(Pattern)
      * @see #setEmailCheckError(String)
      */
     public void reset() {
-        clearMails();
+        assertEmpty();
         error = "FAIL";
         approveRegex = Pattern.compile(".*");
     }