SignatureVerifier.java

  1. package gov.usgs.earthquake.distribution;

  2. import java.io.File;
  3. import java.io.InputStream;
  4. import java.security.PublicKey;
  5. import java.util.Optional;
  6. import java.util.logging.Logger;

  7. import gov.usgs.earthquake.product.Product;
  8. import gov.usgs.earthquake.product.ProductId;
  9. import gov.usgs.util.Config;
  10. import gov.usgs.util.DefaultConfigurable;
  11. import gov.usgs.util.StreamUtils;

  12. public class SignatureVerifier extends DefaultConfigurable {

  13.     /** logging object. */
  14.     private static final Logger LOGGER = Logger
  15.             .getLogger(SignatureVerifier.class.getName());

  16.     /** Property for whether or not to verify signatures. */
  17.     public static final String VERIFY_SIGNATURES_PROPERTY_NAME = "verifySignatures";
  18.     /** Don't verify signatures (Default). */
  19.     public static final String DEFAULT_VERIFY_SIGNATURE = "off";
  20.     /** Test signatures, but don't reject invalid. */

  21.     public static final String TEST_VERIFY_SIGNATURE = "test";
  22.     /** Allow products that do not have a configured key. */
  23.     public static final String ONLY_VERIFY_KNOWN = "allowUnknownSigner";

  24.     /** Property for a list of keys. */
  25.     public static final String KEYCHAIN_PROPERTY_NAME = "keychain";

  26.     /** Property for a file of keys. */
  27.     public static final String KEYCHAIN_FILE_PROPERTY_NAME = "keychainFile";

  28.     /** Whether or not to reject invalid signatures. */
  29.     private boolean rejectInvalidSignatures = false;

  30.     /** If not rejecting invalid signatures, test them anyways. */
  31.     private boolean testSignatures = false;

  32.     /**
  33.      * When rejecting invalid signatures, true will prevent an
  34.      * InvalidSignatureException if there are no candidate keys.
  35.      */
  36.     private boolean allowUnknownSigner = false;

  37.     /** List of candidate keys. */
  38.     private ProductKeyChain keychain;

  39.     @Override
  40.     public void configure(final Config config) throws Exception {
  41.         String verifySignatures = config
  42.                 .getProperty(VERIFY_SIGNATURES_PROPERTY_NAME);
  43.         // configured
  44.         if (verifySignatures != null) {
  45.             // "test"
  46.             if (verifySignatures.equals(TEST_VERIFY_SIGNATURE)) {
  47.                 testSignatures = true;
  48.                 LOGGER.config("[" + getName() + "] test message signatures");
  49.             }
  50.             // not "off"
  51.             else if (!verifySignatures.equals(DEFAULT_VERIFY_SIGNATURE)) {
  52.                 rejectInvalidSignatures = true;
  53.                 LOGGER.config("[" + getName() + "] reject invalid signatures");
  54.             }

  55.             String keyNames = config.getProperty(KEYCHAIN_PROPERTY_NAME);
  56.             if (keyNames != null) {
  57.                 LOGGER.config("[" + getName() + "] using product keys "
  58.                         + keyNames);
  59.                 keychain = new ProductKeyChain(keyNames, Config.getConfig());
  60.             } else {
  61.                 String keychainFileName = config.getProperty(KEYCHAIN_FILE_PROPERTY_NAME);
  62.                 if (keychainFileName != null) {
  63.                     Config keychainConfig = new Config();

  64.                     try (InputStream in = StreamUtils.getInputStream(
  65.                             new File(keychainFileName))) {
  66.                         keychainConfig.load(in);
  67.                     }
  68.                     keyNames = keychainConfig.getProperty(KEYCHAIN_PROPERTY_NAME);
  69.                     keychain = new ProductKeyChain(keyNames,keychainConfig);
  70.                 } else {
  71.                     LOGGER.warning("[" + getName() + "] no product keys configured");
  72.                 }
  73.             }
  74.         }
  75.     }

  76.     /** @return boolean RejectInvalidSignatures */
  77.     public boolean isRejectInvalidSignatures() {
  78.         return rejectInvalidSignatures;
  79.     }

  80.     /** @param rejectInvalidSignatures boolean to set */
  81.     public void setRejectInvalidSignatures(boolean rejectInvalidSignatures) {
  82.         this.rejectInvalidSignatures = rejectInvalidSignatures;
  83.     }

  84.     /** @return boolean TestSignatures */
  85.     public boolean isTestSignatures() {
  86.         return testSignatures;
  87.     }

  88.     /** @param testSignatures boolean to set */
  89.     public void setTestSignatures(boolean testSignatures) {
  90.         this.testSignatures = testSignatures;
  91.     }

  92.     /** @return Product keychain */
  93.     public ProductKeyChain getKeychain() {
  94.         return keychain;
  95.     }

  96.     /** @param keychain ProductKeyChain to set */
  97.     public void setKeychain(ProductKeyChain keychain) {
  98.         this.keychain = keychain;
  99.     }

  100.     /** @return boolean AllowUnknownSigner */
  101.     public boolean isAllowUnknownSigner() {
  102.         return allowUnknownSigner;
  103.     }

  104.     /** @param allowUnknownSigner boolean to set */
  105.     public void setAllowUnknownSigner(boolean allowUnknownSigner) {
  106.         this.allowUnknownSigner = allowUnknownSigner;
  107.     }

  108.     /**
  109.      * Attempt to verify a products signature.
  110.      *
  111.      * @param product
  112.      *            product to verify.
  113.      * @return true if the signature is from a key in the keychain.
  114.      * @throws InvalidSignatureException
  115.      *             if rejectInvalidSignatures=true, and signature was not
  116.      *             verified; allowUnknownSigner=true prevents this exception
  117.      *             when no keys are found in the keychain for the product.
  118.      * @throws Exception if error occurs
  119.      */
  120.     public boolean verifySignature(final Product product) throws Exception {
  121.         boolean verified = false;
  122.         String verifiedKeyName = null;

  123.         if (testSignatures || rejectInvalidSignatures) {
  124.             ProductId id = product.getId();
  125.             PublicKey[] candidateKeys = new PublicKey[] {};

  126.             if (keychain != null) {
  127.                 candidateKeys = keychain.getProductKeys(id);
  128.                 LOGGER.finer("[" + getName() + "] number of candidate keys="
  129.                         + candidateKeys.length);
  130.                 if (candidateKeys.length > 0) {
  131.                     PublicKey publicKey = product.verifySignatureKey(
  132.                             candidateKeys,
  133.                             product.getSignatureVersion());
  134.                     if (publicKey != null) {
  135.                         verified = true;
  136.                         // find key that verified
  137.                         Optional<ProductKey> verifiedKey = keychain.getKeychain()
  138.                                 .stream().filter(key -> {
  139.                                     return publicKey.equals(key.getKey());
  140.                                 }).findAny();
  141.                         if (verifiedKey.isPresent()) {
  142.                             verifiedKeyName = verifiedKey.get().getName();
  143.                         }
  144.                     }
  145.                 }
  146.             } else {
  147.                 LOGGER.warning("[" + getName() + "] missing Signature Keychain");
  148.             }

  149.             LOGGER.fine("[" + getName() + "] signature verified=" + verified
  150.                     + (verified ? " (key=" + verifiedKeyName + ")" : "")
  151.                     + ", id=" + product.getId());

  152.             if (allowUnknownSigner && candidateKeys.length == 0) {
  153.                     LOGGER.finer("[" + getName()
  154.                             + "] unknown signer, allowed by configuration");
  155.                     return false;
  156.             }

  157.             if (!verified && rejectInvalidSignatures) {
  158.                 throw new InvalidSignatureException("[" + getName()
  159.                         + "] bad signature for id=" + id);
  160.             }
  161.         }

  162.         return verified;
  163.     }

  164. }