You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CertificateExceptionManager.java 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. package com.dmdirc.tls;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.OutputStream;
  5. import java.nio.file.Files;
  6. import java.nio.file.Path;
  7. import java.security.GeneralSecurityException;
  8. import java.security.KeyStore;
  9. import java.security.cert.PKIXParameters;
  10. import java.security.cert.TrustAnchor;
  11. import java.security.cert.X509Certificate;
  12. import java.util.Collections;
  13. import java.util.Set;
  14. import java.util.stream.Collectors;
  15. import javax.annotation.Nullable;
  16. /**
  17. * Manages certificates that the user has explicitly trusted, excepting them from the normal PKIX checks.
  18. */
  19. public class CertificateExceptionManager {
  20. /** Password to use for our exception keystore. */
  21. private static final char[] PASSWORD = "dmdirc".toCharArray();
  22. private final Path keyStorePath;
  23. public CertificateExceptionManager(final Path keyStorePath) {
  24. this.keyStorePath = keyStorePath;
  25. }
  26. /**
  27. * Returns the set of certificates that the user has explicitly trusted.
  28. */
  29. public Set<X509Certificate> getExceptedCertificates() {
  30. try {
  31. final KeyStore keyStore = getKeyStore();
  32. if (keyStore == null) {
  33. return Collections.emptySet();
  34. } else {
  35. final PKIXParameters params = new PKIXParameters(keyStore);
  36. return params.getTrustAnchors().stream()
  37. .map(TrustAnchor::getTrustedCert)
  38. .collect(Collectors.toSet());
  39. }
  40. } catch (GeneralSecurityException | IOException e) {
  41. return Collections.emptySet();
  42. }
  43. }
  44. /**
  45. * Adds a new certificate that will be excepted from future PKIX checks.
  46. *
  47. * @return True if the certificate was successfully added; false otherwise.
  48. */
  49. public boolean addExceptedCertificate(final X509Certificate certificate) {
  50. try {
  51. final KeyStore keyStore = getKeyStore();
  52. keyStore.setCertificateEntry(
  53. "excepted-" + System.currentTimeMillis(),
  54. certificate);
  55. try (OutputStream os = Files.newOutputStream(keyStorePath)) {
  56. keyStore.store(os, PASSWORD);
  57. return true;
  58. }
  59. } catch (GeneralSecurityException | IOException e) {
  60. return false;
  61. }
  62. }
  63. /**
  64. * Removes a certificate that was previously added.
  65. *
  66. * @return True if the remove was successful; false otherwise
  67. */
  68. public boolean removeExceptedCertificate(final X509Certificate certificate) {
  69. try {
  70. final KeyStore keyStore = getKeyStore();
  71. @Nullable final String alias = keyStore.getCertificateAlias(certificate);
  72. if (alias == null) {
  73. // Not found
  74. return false;
  75. }
  76. keyStore.deleteEntry(alias);
  77. try (OutputStream os = Files.newOutputStream(keyStorePath)) {
  78. keyStore.store(os, PASSWORD);
  79. return true;
  80. }
  81. } catch (GeneralSecurityException | IOException e) {
  82. return false;
  83. }
  84. }
  85. /**
  86. * Loads the existing excepted certs KeyStore from disk, if it exists, or creates a new KeyStore otherwise.
  87. */
  88. private KeyStore getKeyStore() throws GeneralSecurityException, IOException {
  89. try (InputStream is = Files.newInputStream(keyStorePath)) {
  90. final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
  91. keyStore.load(is, PASSWORD);
  92. return keyStore;
  93. } catch (GeneralSecurityException | IOException ex) {
  94. final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
  95. keyStore.load(null, null);
  96. return keyStore;
  97. }
  98. }
  99. }