org.jgroups.protocols.SYM_ENCRYPT Maven / Gradle / Ivy
package org.jgroups.protocols;
import org.jgroups.annotations.MBean;
import org.jgroups.annotations.Property;
import org.jgroups.util.Util;
import javax.crypto.SecretKey;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
/**
* Encrypts and decrypts communication in JGroups by using a secret key shared by all cluster members.
*
* The secret key is identical for all cluster members and is injected into this protocol at startup, e.g. by reading
* it from a keystore. Messages are sent by encrypting them with the secret key and received by decrypting them with
* the secret key. Note that all cluster members must be shipped with the same keystore file
*
* This protocol is typically placed under {@link org.jgroups.protocols.pbcast.NAKACK2}, so that most important
* headers are encrypted as well, to prevent replay attacks.
*
* A possible configuration looks like this:
* {@code }
*
*
* In order to use SYM_ENCRYPT layer in this manner, it is necessary to have the secret key already generated in a
* keystore file. The directory containing the keystore file must be on the application's classpath. You cannot create a
* secret key keystore file using the keytool application shipped with the JDK. A java file called KeyStoreGenerator is
* included in the demo package that can be used from the command line (or IDE) to generate a suitable keystore.
*
* @author Bela Ban
* @author Steve Woodcock
*/
@MBean(description="Symmetric encryption protocol. The (shared) shared secret key is configured up front, " +
"e.g. via a key store, or injection")
public class SYM_ENCRYPT extends EncryptBase {
/* ----------------------------------------- Properties -------------------------------------------------- */
@Property(description="File on classpath that contains keystore repository")
protected String keystore_name;
@Property(description="Password used to check the integrity/unlock the keystore. Change the default",
exposeAsManagedAttribute=false)
protected String store_password="changeit"; // JDK default
@Property(description="Password for recovering the key. Change the default", exposeAsManagedAttribute=false)
protected String key_password; // allows to assign keypwd=storepwd if not set (https://issues.jboss.org/browse/JGRP-1375)
@Property(name="alias", description="Alias used for recovering the key. Change the default",exposeAsManagedAttribute=false)
protected String alias="mykey"; // JDK default
public String keystoreName() {return this.keystore_name;}
public SYM_ENCRYPT keystoreName(String n) {this.keystore_name=n; return this;}
public String alias() {return alias;}
public SYM_ENCRYPT alias(String a) {this.alias=a; return this;}
public String storePassword() {return store_password;}
public SYM_ENCRYPT storePassword(String pwd) {this.store_password=pwd; return this;}
public void init() throws Exception {
if(key_password == null && store_password != null) {
key_password=store_password;
log.debug("%s: key_password used is same as store_password", local_addr);
}
readSecretKeyFromKeystore();
super.init();
}
/**
* Initialisation if a supplied key is defined in the properties. This supplied key must be in a keystore which
* can be generated using the keystoreGenerator file in demos. The keystore must be on the classpath to find it.
*/
protected void readSecretKeyFromKeystore() throws Exception {
InputStream inputStream=null;
// must not use default keystore type - as it does not support secret keys
KeyStore store=KeyStore.getInstance("JCEKS");
SecretKey tempKey=null;
try {
if(this.secret_key == null) { // in case the secret key was set before, e.g. via injection in a unit test
// load in keystore using this thread's classloader
inputStream=Thread.currentThread().getContextClassLoader().getResourceAsStream(keystore_name);
if(inputStream == null)
inputStream=new FileInputStream(keystore_name);
// we can't find a keystore here -
if(inputStream == null)
throw new Exception("Unable to load keystore " + keystore_name + " ensure file is on classpath");
// we have located a file lets load the keystore
try {
store.load(inputStream, store_password.toCharArray());
// loaded keystore - get the key
tempKey=(SecretKey)store.getKey(alias, key_password.toCharArray());
}
catch(IOException e) {
throw new Exception("Unable to load keystore " + keystore_name + ": " + e);
}
catch(NoSuchAlgorithmException e) {
throw new Exception("No Such algorithm " + keystore_name + ": " + e);
}
catch(CertificateException e) {
throw new Exception("Certificate exception " + keystore_name + ": " + e);
}
if(tempKey == null)
throw new Exception("Unable to retrieve key '" + alias + "' from keystore " + keystore_name);
this.secret_key=tempKey;
if(sym_algorithm.equals(DEFAULT_SYM_ALGO))
sym_algorithm=tempKey.getAlgorithm();
}
}
finally {
Util.close(inputStream);
}
}
}