SignatureVerifier.java
- package gov.usgs.earthquake.distribution;
- import java.io.File;
- import java.io.InputStream;
- import java.security.PublicKey;
- import java.util.Optional;
- import java.util.logging.Logger;
- import gov.usgs.earthquake.product.Product;
- import gov.usgs.earthquake.product.ProductId;
- import gov.usgs.util.Config;
- import gov.usgs.util.DefaultConfigurable;
- import gov.usgs.util.StreamUtils;
- public class SignatureVerifier extends DefaultConfigurable {
- /** logging object. */
- private static final Logger LOGGER = Logger
- .getLogger(SignatureVerifier.class.getName());
- /** Property for whether or not to verify signatures. */
- public static final String VERIFY_SIGNATURES_PROPERTY_NAME = "verifySignatures";
- /** Don't verify signatures (Default). */
- public static final String DEFAULT_VERIFY_SIGNATURE = "off";
- /** Test signatures, but don't reject invalid. */
- public static final String TEST_VERIFY_SIGNATURE = "test";
- /** Allow products that do not have a configured key. */
- public static final String ONLY_VERIFY_KNOWN = "allowUnknownSigner";
- /** Property for a list of keys. */
- public static final String KEYCHAIN_PROPERTY_NAME = "keychain";
- /** Property for a file of keys. */
- public static final String KEYCHAIN_FILE_PROPERTY_NAME = "keychainFile";
- /** Whether or not to reject invalid signatures. */
- private boolean rejectInvalidSignatures = false;
- /** If not rejecting invalid signatures, test them anyways. */
- private boolean testSignatures = false;
- /**
- * When rejecting invalid signatures, true will prevent an
- * InvalidSignatureException if there are no candidate keys.
- */
- private boolean allowUnknownSigner = false;
- /** List of candidate keys. */
- private ProductKeyChain keychain;
- @Override
- public void configure(final Config config) throws Exception {
- String verifySignatures = config
- .getProperty(VERIFY_SIGNATURES_PROPERTY_NAME);
- // configured
- if (verifySignatures != null) {
- // "test"
- if (verifySignatures.equals(TEST_VERIFY_SIGNATURE)) {
- testSignatures = true;
- LOGGER.config("[" + getName() + "] test message signatures");
- }
- // not "off"
- else if (!verifySignatures.equals(DEFAULT_VERIFY_SIGNATURE)) {
- rejectInvalidSignatures = true;
- LOGGER.config("[" + getName() + "] reject invalid signatures");
- }
- String keyNames = config.getProperty(KEYCHAIN_PROPERTY_NAME);
- if (keyNames != null) {
- LOGGER.config("[" + getName() + "] using product keys "
- + keyNames);
- keychain = new ProductKeyChain(keyNames, Config.getConfig());
- } else {
- String keychainFileName = config.getProperty(KEYCHAIN_FILE_PROPERTY_NAME);
- if (keychainFileName != null) {
- Config keychainConfig = new Config();
- try (InputStream in = StreamUtils.getInputStream(
- new File(keychainFileName))) {
- keychainConfig.load(in);
- }
- keyNames = keychainConfig.getProperty(KEYCHAIN_PROPERTY_NAME);
- keychain = new ProductKeyChain(keyNames,keychainConfig);
- } else {
- LOGGER.warning("[" + getName() + "] no product keys configured");
- }
- }
- }
- }
- /** @return boolean RejectInvalidSignatures */
- public boolean isRejectInvalidSignatures() {
- return rejectInvalidSignatures;
- }
- /** @param rejectInvalidSignatures boolean to set */
- public void setRejectInvalidSignatures(boolean rejectInvalidSignatures) {
- this.rejectInvalidSignatures = rejectInvalidSignatures;
- }
- /** @return boolean TestSignatures */
- public boolean isTestSignatures() {
- return testSignatures;
- }
- /** @param testSignatures boolean to set */
- public void setTestSignatures(boolean testSignatures) {
- this.testSignatures = testSignatures;
- }
- /** @return Product keychain */
- public ProductKeyChain getKeychain() {
- return keychain;
- }
- /** @param keychain ProductKeyChain to set */
- public void setKeychain(ProductKeyChain keychain) {
- this.keychain = keychain;
- }
- /** @return boolean AllowUnknownSigner */
- public boolean isAllowUnknownSigner() {
- return allowUnknownSigner;
- }
- /** @param allowUnknownSigner boolean to set */
- public void setAllowUnknownSigner(boolean allowUnknownSigner) {
- this.allowUnknownSigner = allowUnknownSigner;
- }
- /**
- * Attempt to verify a products signature.
- *
- * @param product
- * product to verify.
- * @return true if the signature is from a key in the keychain.
- * @throws InvalidSignatureException
- * if rejectInvalidSignatures=true, and signature was not
- * verified; allowUnknownSigner=true prevents this exception
- * when no keys are found in the keychain for the product.
- * @throws Exception if error occurs
- */
- public boolean verifySignature(final Product product) throws Exception {
- boolean verified = false;
- String verifiedKeyName = null;
- if (testSignatures || rejectInvalidSignatures) {
- ProductId id = product.getId();
- PublicKey[] candidateKeys = new PublicKey[] {};
- if (keychain != null) {
- candidateKeys = keychain.getProductKeys(id);
- LOGGER.finer("[" + getName() + "] number of candidate keys="
- + candidateKeys.length);
- if (candidateKeys.length > 0) {
- PublicKey publicKey = product.verifySignatureKey(
- candidateKeys,
- product.getSignatureVersion());
- if (publicKey != null) {
- verified = true;
- // find key that verified
- Optional<ProductKey> verifiedKey = keychain.getKeychain()
- .stream().filter(key -> {
- return publicKey.equals(key.getKey());
- }).findAny();
- if (verifiedKey.isPresent()) {
- verifiedKeyName = verifiedKey.get().getName();
- }
- }
- }
- } else {
- LOGGER.warning("[" + getName() + "] missing Signature Keychain");
- }
- LOGGER.fine("[" + getName() + "] signature verified=" + verified
- + (verified ? " (key=" + verifiedKeyName + ")" : "")
- + ", id=" + product.getId());
- if (allowUnknownSigner && candidateKeys.length == 0) {
- LOGGER.finer("[" + getName()
- + "] unknown signer, allowed by configuration");
- return false;
- }
- if (!verified && rejectInvalidSignatures) {
- throw new InvalidSignatureException("[" + getName()
- + "] bad signature for id=" + id);
- }
- }
- return verified;
- }
- }