X-Git-Url: https://code.wpia.club/?a=blobdiff_plain;f=src%2Fclub%2Fwpia%2Fgigi%2Fpages%2Fmain%2FKeyCompromiseForm.java;h=e0690844d69189034628fb5a4cc2ce571918b103;hb=e14d9e6fd0fd485f278e24a3db7445e034dc0b74;hp=197f24da31f647e55e26429ca147c123aef662aa;hpb=11786136386ee067ed7a17b25bc07b3cd257ecb0;p=gigi.git diff --git a/src/club/wpia/gigi/pages/main/KeyCompromiseForm.java b/src/club/wpia/gigi/pages/main/KeyCompromiseForm.java index 197f24da..e0690844 100644 --- a/src/club/wpia/gigi/pages/main/KeyCompromiseForm.java +++ b/src/club/wpia/gigi/pages/main/KeyCompromiseForm.java @@ -1,6 +1,5 @@ package club.wpia.gigi.pages.main; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; @@ -8,13 +7,12 @@ import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.Signature; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.security.spec.PKCS8EncodedKeySpec; -import java.util.Arrays; import java.util.Base64; +import java.util.HashMap; +import java.util.Locale; import java.util.Map; import javax.servlet.http.HttpServletRequest; @@ -22,18 +20,25 @@ import javax.servlet.http.HttpServletRequest; import club.wpia.gigi.GigiApiException; import club.wpia.gigi.dbObjects.Certificate; import club.wpia.gigi.dbObjects.Certificate.CertificateStatus; +import club.wpia.gigi.dbObjects.CertificateOwner; import club.wpia.gigi.dbObjects.Job; +import club.wpia.gigi.dbObjects.Organisation; +import club.wpia.gigi.dbObjects.User; import club.wpia.gigi.localisation.Language; import club.wpia.gigi.output.template.Form; +import club.wpia.gigi.output.template.MailTemplate; import club.wpia.gigi.output.template.Template; import club.wpia.gigi.output.template.TranslateCommand; import club.wpia.gigi.util.PEM; import club.wpia.gigi.util.RandomToken; import club.wpia.gigi.util.RateLimit; import club.wpia.gigi.util.RateLimit.RateLimitException; +import club.wpia.gigi.util.ServerConstants; public class KeyCompromiseForm extends Form { + public static final String CONFIDENTIAL_MARKER = "*CONFIDENTIAL*"; + private static final Template t = new Template(KeyCompromiseForm.class.getResource("KeyCompromiseForm.templ")); // 50 per 5 min @@ -43,10 +48,10 @@ public class KeyCompromiseForm extends Form { public static final String CHALLENGE_PREFIX = "This private key has been compromised. Challenge: "; - public static final TranslateCommand NOT_LOADED = new TranslateCommand("Certificate could not be loaded"); - public static final TranslateCommand NOT_FOUND = new TranslateCommand("Certificate to revoke not found"); + private static final MailTemplate revocationNotice = new MailTemplate(KeyCompromiseForm.class.getResource("RevocationNotice.templ")); + public KeyCompromiseForm(HttpServletRequest hsr) { super(hsr); challenge = RandomToken.generateToken(32); @@ -57,47 +62,23 @@ public class KeyCompromiseForm extends Form { if (RATE_LIMIT.isLimitExceeded(req.getRemoteAddr())) { throw new RateLimitException(); } - Certificate c = null; - X509Certificate cert = null; - String serial = req.getParameter("serial"); - String certData = req.getParameter("cert"); - if (serial != null && !serial.isEmpty()) { - c = fetchCertificate(serial); - try { - cert = c.cert(); - } catch (IOException e) { - throw new PermamentFormException(new GigiApiException(NOT_LOADED)); - } catch (GeneralSecurityException e) { - throw new PermamentFormException(new GigiApiException(NOT_LOADED)); - } - } - if (certData != null && !certData.isEmpty()) { - X509Certificate c0; - byte[] supplied; - try { - supplied = PEM.decode("CERTIFICATE", certData); - c0 = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(new ByteArrayInputStream(supplied)); - } catch (IllegalArgumentException e1) { - throw new PermamentFormException(new GigiApiException("Your certificate could not be parsed")); - } catch (CertificateException e1) { - throw new PermamentFormException(new GigiApiException("Your certificate could not be parsed")); - } - try { - String ser = c0.getSerialNumber().toString(16); - c = fetchCertificate(ser); - cert = c.cert(); - if ( !Arrays.equals(supplied, cert.getEncoded())) { - throw new PermamentFormException(new GigiApiException(NOT_FOUND)); - } - } catch (IOException e) { - throw new PermamentFormException(new GigiApiException(NOT_LOADED)); - } catch (GeneralSecurityException e) { - throw new PermamentFormException(new GigiApiException(NOT_LOADED)); + Certificate c; + try { + c = Certificate.locateCertificate(req.getParameter("serial"), req.getParameter("cert")); + if (c == null) { + throw new GigiApiException(NOT_FOUND); } + } catch (GigiApiException e) { + throw new PermamentFormException(e); } - if (c == null) { - throw new PermamentFormException(new GigiApiException("No certificate identification information provided")); + + X509Certificate cert; + try { + cert = c.cert(); + } catch (IOException | GeneralSecurityException e) { + throw new PermamentFormException(new GigiApiException(Certificate.NOT_LOADED)); } + if (c.getStatus() == CertificateStatus.REVOKED) { return new SuccessMessageResult(new TranslateCommand("Certificate had already been revoked")); } @@ -139,7 +120,52 @@ public class KeyCompromiseForm extends Form { } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } - Job j = c.revoke(challenge, Base64.getEncoder().encodeToString(signature), ""); + String message = req.getParameter("message"); + if (message != null && message.isEmpty()) { + message = null; + } + if (message != null) { + if (message.startsWith(CONFIDENTIAL_MARKER)) { + message = " " + message; + } + String confidential = req.getParameter("confidential"); + if (confidential != null && !confidential.isEmpty()) { + message = CONFIDENTIAL_MARKER + "\r\n" + message; + } + if (message.contains("---")) { + throw new GigiApiException("Your message may not contain '---'."); + } + // convert all line endings to CRLF + message = message.replace("\r\n", "\n").replace("\r", "\n").replace("\n", "\r\n"); + if ( !message.matches("[ -~\r\n\t]*")) { + throw new GigiApiException("Your message may only contain printable ASCII characters, tab, newline and space."); + } + } + CertificateOwner co = c.getOwner(); + String primaryEmail; + Language l = Language.getInstance(Locale.ENGLISH); + if (co instanceof User) { + primaryEmail = ((User) co).getEmail(); + l = Language.getInstance(((User) co).getPreferredLocale()); + } else if (co instanceof Organisation) { + primaryEmail = ((Organisation) co).getContactEmail(); + } else { + throw new IllegalArgumentException("certificate owner of unknown type"); + } + HashMap vars = new HashMap<>(); + vars.put("appName", ServerConstants.getAppName()); + if (message != null && !message.startsWith(CONFIDENTIAL_MARKER)) { + vars.put("message", message); + } else { + vars.put("message", null); + } + vars.put("serial", c.getSerial()); + try { + revocationNotice.sendMail(l, vars, primaryEmail); + } catch (IOException e) { + throw new GigiApiException("Sending the notification mail failed."); + } + Job j = c.revoke(challenge, Base64.getEncoder().encodeToString(signature), message); if ( !j.waitFor(60000)) { throw new PermamentFormException(new GigiApiException("Revocation timed out.")); } @@ -163,21 +189,6 @@ public class KeyCompromiseForm extends Form { return signature; } - private Certificate fetchCertificate(String serial) { - Certificate c; - serial = serial.trim().toLowerCase(); - int idx = 0; - while (idx < serial.length() && serial.charAt(idx) == '0') { - idx++; - } - serial = serial.substring(idx); - c = Certificate.getBySerial(serial); - if (c == null) { - throw new PermamentFormException(new GigiApiException(NOT_FOUND)); - } - return c; - } - @Override protected void outputContent(PrintWriter out, Language l, Map vars) { vars.put("challenge", challenge);