001: package org.jgroups.auth;
002:
003: import org.jgroups.util.Util;
004: import org.jgroups.Message;
005:
006: import javax.crypto.Cipher;
007: import java.io.*;
008: import java.util.HashMap;
009: import java.util.Properties;
010: import java.security.cert.X509Certificate;
011: import java.security.PrivateKey;
012: import java.security.KeyStore;
013:
014: /**
015: * <p>
016: * This is an example of using a preshared token that is encrypted using an X509 certificate for authentication purposes. All members of the group have to have the same string value in the JGroups config.
017: * </p>
018: * <p>
019: * This example uses certificates contained within a specified keystore. Configuration parameters for this example are shown below:
020: * </p>
021: * <ul>
022: * <li>keystore_type = JKS(default)/PKCS12 - see http://java.sun.com/j2se/1.4.2/docs/guide/security/CryptoSpec.html#AppA</li>
023: * <li>keystore_path (required) = the location of the keystore</li>
024: * <li>keystore_password (required) = the password of the keystore</li>
025: * <li>cert_alias (required) = the alias of the certification within the keystore</li>
026: * <li>cert_password = the password of the certification within the keystore</li>
027: * <li>auth_value (required) = the string to encrypt</li>
028: * <li>cipher_type = RSA(default)/AES/Blowfish/DES/DESede/PBEWithMD5AndDES/PBEWithHmacSHA1AndDESede/RC2/RC4/RC5 - see http://java.sun.com/j2se/1.4.2/docs/guide/security/jce/JCERefGuide.html#AppA</li>
029: * </ul>
030: * @see org.jgroups.auth.AuthToken
031: * @author Chris Mills
032: */
033: public class X509Token1_5 extends AuthToken {
034:
035: public static final String KEYSTORE_TYPE = "keystore_type";
036: public static final String KEYSTORE_PATH = "keystore_path";
037: public static final String KEYSTORE_PASSWORD = "keystore_password";
038: public static final String CERT_ALIAS = "cert_alias";
039: public static final String CERT_PASSWORD = "cert_password";
040: public static final String TOKEN_ATTR = "auth_value";
041: public static final String CIPHER_TYPE = "cipher_type";
042:
043: private boolean valueSet = false;
044:
045: private String keystore_type = null;
046: private String cert_alias = null;
047: private String keystore_path = null;
048: private String token_attr = null;
049: private String cipher_type = null;
050:
051: private byte[] encryptedToken = null;
052:
053: private char[] cert_password = null;
054: private char[] keystore_password = null;
055:
056: private Cipher cipher = null;
057: private PrivateKey certPrivateKey = null;
058: private X509Certificate certificate = null;
059:
060: public X509Token1_5() {
061: //need an empty constructor
062: }
063:
064: public void setValue(Properties properties) {
065: if (log.isDebugEnabled()) {
066: log.debug("setting values on X509Token1_5 object");
067: }
068:
069: if (properties.containsKey(X509Token1_5.TOKEN_ATTR)) {
070: this .token_attr = (String) properties
071: .get(X509Token1_5.TOKEN_ATTR);
072: properties.remove(X509Token1_5.TOKEN_ATTR);
073: if (log.isDebugEnabled()) {
074: log.debug("token_attr = " + this .token_attr);
075: }
076: }
077:
078: if (properties.containsKey(X509Token1_5.KEYSTORE_TYPE)) {
079: this .keystore_type = (String) properties
080: .get(X509Token1_5.KEYSTORE_TYPE);
081: properties.remove(X509Token1_5.KEYSTORE_TYPE);
082: if (log.isDebugEnabled()) {
083: log.debug("keystore_type = " + this .keystore_type);
084: }
085: } else {
086: this .keystore_type = "JKS";
087: if (log.isDebugEnabled()) {
088: log.debug("keystore_type = " + this .keystore_type);
089: }
090: }
091:
092: if (properties.containsKey(X509Token1_5.KEYSTORE_PATH)) {
093: this .keystore_path = (String) properties
094: .get(X509Token1_5.KEYSTORE_PATH);
095: properties.remove(X509Token1_5.KEYSTORE_PATH);
096: if (log.isDebugEnabled()) {
097: log.debug("keystore_path = " + this .keystore_path);
098: }
099: }
100:
101: if (properties.containsKey(X509Token1_5.KEYSTORE_PASSWORD)) {
102: this .keystore_password = ((String) properties
103: .get(X509Token1_5.KEYSTORE_PASSWORD)).toCharArray();
104: properties.remove(X509Token1_5.KEYSTORE_PASSWORD);
105: if (log.isDebugEnabled()) {
106: log.debug("keystore_password = "
107: + this .keystore_password);
108: }
109: }
110:
111: if (properties.containsKey(X509Token1_5.CERT_ALIAS)) {
112: this .cert_alias = (String) properties
113: .get(X509Token1_5.CERT_ALIAS);
114: properties.remove(X509Token1_5.CERT_ALIAS);
115: if (log.isDebugEnabled()) {
116: log.debug("cert_alias = " + this .cert_alias);
117: }
118: }
119:
120: if (properties.containsKey(X509Token1_5.CERT_PASSWORD)) {
121: this .cert_password = ((String) properties
122: .get(X509Token1_5.CERT_PASSWORD)).toCharArray();
123: properties.remove(X509Token1_5.CERT_PASSWORD);
124: if (log.isDebugEnabled()) {
125: log.debug("cert_password = " + this .cert_password);
126: }
127: } else {
128: this .cert_password = this .keystore_password;
129: if (log.isDebugEnabled()) {
130: log.debug("cert_password = " + this .cert_password);
131: }
132: }
133:
134: if (properties.containsKey(X509Token1_5.CIPHER_TYPE)) {
135: this .cipher_type = (String) properties
136: .get(X509Token1_5.CIPHER_TYPE);
137: properties.remove(X509Token1_5.CIPHER_TYPE);
138: if (log.isDebugEnabled()) {
139: log.debug("cipher_type = " + this .cipher_type);
140: }
141: } else {
142: this .cipher_type = "RSA";
143: if (log.isDebugEnabled()) {
144: log.debug("cipher_type = " + this .cipher_type);
145: }
146: }
147:
148: if (getCertificate()) {
149: this .valueSet = true;
150: if (log.isDebugEnabled()) {
151: log.debug("X509Token1_5 created correctly");
152: }
153: }
154: }
155:
156: public String getName() {
157: return "org.jgroups.auth.X509Token1_5";
158: }
159:
160: public boolean authenticate(AuthToken token, Message msg) {
161: if (!this .valueSet) {
162: if (log.isFatalEnabled()) {
163: log
164: .fatal("X509Token1_5 not setup correctly - check token attrs");
165: }
166: return false;
167: }
168:
169: if ((token != null) && (token instanceof X509Token1_5)) {
170: //got a valid X509 token object
171: X509Token1_5 serverToken = (X509Token1_5) token;
172: if (!serverToken.valueSet) {
173: if (log.isFatalEnabled()) {
174: log
175: .fatal("X509Token1_5 - recieved token not valid");
176: }
177: return false;
178: }
179:
180: try {
181: if (log.isDebugEnabled()) {
182: log.debug("setting cipher to decrypt mode");
183: }
184: this .cipher.init(Cipher.DECRYPT_MODE,
185: this .certPrivateKey);
186: String serverBytes = new String(this .cipher
187: .doFinal(serverToken.encryptedToken));
188: if ((serverBytes != null)
189: && (serverBytes
190: .equalsIgnoreCase(this .token_attr))) {
191: if (log.isDebugEnabled()) {
192: log.debug("X509 authentication passed");
193: }
194: return true;
195: }
196: } catch (Exception e) {
197: if (log.isFatalEnabled()) {
198: log.fatal(e);
199: }
200: }
201: }
202: if (log.isWarnEnabled()) {
203: log.warn("X509 authentication failed");
204: }
205: return false;
206: }
207:
208: public void writeTo(DataOutputStream out) throws IOException {
209: if (log.isDebugEnabled()) {
210: log.debug("X509Token1_5 writeTo()");
211: }
212: Util.writeByteBuffer(this .encryptedToken, out);
213: }
214:
215: public void readFrom(DataInputStream in) throws IOException,
216: IllegalAccessException, InstantiationException {
217: if (log.isDebugEnabled()) {
218: log.debug("X509Token1_5 readFrom()");
219: }
220: this .encryptedToken = Util.readByteBuffer(in);
221: this .valueSet = true;
222: }
223:
224: /**
225: * Used during setup to get the certification from the keystore and encrypt the auth_value with the private key
226: * @return true if the certificate was found and the string encypted correctly otherwise returns false
227: */
228: private boolean getCertificate() {
229: try {
230: KeyStore store = KeyStore.getInstance(this .keystore_type);
231: java.io.FileInputStream fis = new java.io.FileInputStream(
232: this .keystore_path);
233: store.load(fis, this .keystore_password);
234:
235: this .cipher = Cipher.getInstance(this .cipher_type);
236: this .certificate = (X509Certificate) store
237: .getCertificate(this .cert_alias);
238:
239: if (log.isDebugEnabled()) {
240: log.debug("certificate = "
241: + this .certificate.toString());
242: }
243:
244: this .cipher.init(Cipher.ENCRYPT_MODE, this .certificate);
245: this .encryptedToken = this .cipher.doFinal(this .token_attr
246: .getBytes());
247:
248: if (log.isDebugEnabled()) {
249: log.debug("encryptedToken = " + this .encryptedToken);
250: }
251:
252: KeyStore.PrivateKeyEntry privateKey = (KeyStore.PrivateKeyEntry) store
253: .getEntry(this .cert_alias,
254: new KeyStore.PasswordProtection(
255: this .cert_password));
256: this .certPrivateKey = privateKey.getPrivateKey();
257:
258: if (log.isDebugEnabled()) {
259: log.debug("certPrivateKey = "
260: + this .certPrivateKey.toString());
261: }
262:
263: return true;
264: } catch (Exception e) {
265: if (log.isFatalEnabled()) {
266: log.fatal(e);
267: }
268: return false;
269: }
270: }
271: }
|