You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2014/03/30 15:19:10 UTC

svn commit: r1583130 - in /cayenne/main/trunk/cayenne-crypto/src: main/java/org/apache/cayenne/crypto/ main/java/org/apache/cayenne/crypto/key/ test/java/org/apache/cayenne/crypto/key/ test/resources/org/ test/resources/org/apache/ test/resources/org/a...

Author: aadamchik
Date: Sun Mar 30 13:19:09 2014
New Revision: 1583130

URL: http://svn.apache.org/r1583130
Log:
CAY-1916 cayenne-crypto module that enables data encryption for certain model attributes

keystore infrastructure

Added:
    cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/key/
    cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/key/KeyStoreKeySourceTest.java
    cayenne/main/trunk/cayenne-crypto/src/test/resources/org/
    cayenne/main/trunk/cayenne-crypto/src/test/resources/org/apache/
    cayenne/main/trunk/cayenne-crypto/src/test/resources/org/apache/cayenne/
    cayenne/main/trunk/cayenne-crypto/src/test/resources/org/apache/cayenne/crypto/
    cayenne/main/trunk/cayenne-crypto/src/test/resources/org/apache/cayenne/crypto/key/
    cayenne/main/trunk/cayenne-crypto/src/test/resources/org/apache/cayenne/crypto/key/README-keystore.txt
    cayenne/main/trunk/cayenne-crypto/src/test/resources/org/apache/cayenne/crypto/key/ks1.jceks
Modified:
    cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoConstants.java
    cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoModuleBuilder.java
    cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/key/KeyStoreKeySource.java

Modified: cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoConstants.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoConstants.java?rev=1583130&r1=1583129&r2=1583130&view=diff
==============================================================================
--- cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoConstants.java (original)
+++ cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoConstants.java Sun Mar 30 13:19:09 2014
@@ -24,10 +24,15 @@ package org.apache.cayenne.crypto;
 public interface CryptoConstants {
 
     /**
-     * An injection key for the map of the crypto properties.
+     * An injection key for the Map<String, String> of the crypto properties.
      */
     public static final String PROPERTIES_MAP = "cayenne.crypto.properties";
 
+    /**
+     * An injection key for the map Map<String, char[]> of credentials.
+     */
+    public static final String CREDENTIALS_MAP = "cayenne.crypto.properties";
+
     public static final String CIPHER_ALGORITHM = "cayenne.crypto.cipher.algorithm";
 
     public static final String CIPHER_MODE = "cayenne.crypto.cipher.mode";
@@ -36,11 +41,8 @@ public interface CryptoConstants {
 
     public static final String KEYSTORE_URL = "cayenne.crypto.keystore.url";
 
-    public static final String KEYSTORE_PASSWORD = "cayenne.crypto.keystore.password";
-
     /**
-     * A password to access a secret key within the keystore. (As opposed to
-     * keystore password specified with KEYSTORE_PASSWORD property).
+     * A password to access a secret key within the keystore.
      */
     public static final String KEY_PASSWORD = "cayenne.crypto.key.password";
 

Modified: cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoModuleBuilder.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoModuleBuilder.java?rev=1583130&r1=1583129&r2=1583130&view=diff
==============================================================================
--- cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoModuleBuilder.java (original)
+++ cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoModuleBuilder.java Sun Mar 30 13:19:09 2014
@@ -34,6 +34,7 @@ import org.apache.cayenne.crypto.transfo
 import org.apache.cayenne.crypto.transformer.TransformerFactory;
 import org.apache.cayenne.crypto.transformer.value.ValueTransformerFactory;
 import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.MapBuilder;
 import org.apache.cayenne.di.Module;
 
 /**
@@ -62,6 +63,9 @@ public class CryptoModuleBuilder {
 
     private String keyStoreUrl;
     private File keyStoreFile;
+    private Class<? extends KeySource> keySourceType;
+
+    private char[] keyPassword;
 
     public CryptoModuleBuilder() {
 
@@ -72,6 +76,7 @@ public class CryptoModuleBuilder {
         this.cipherPadding = DEFAULT_CIPHER_PADDING;
 
         this.cipherFactoryType = DefaultCipherFactory.class;
+        this.keySourceType = KeyStoreKeySource.class;
     }
 
     public CryptoModuleBuilder cipherAlgorithm(String algorithm) {
@@ -106,6 +111,14 @@ public class CryptoModuleBuilder {
         return this;
     }
 
+    /**
+     * Sets a password used that unlocks a secret key.
+     */
+    public CryptoModuleBuilder keyPassword(char[] password) {
+        this.keyPassword = password;
+        return this;
+    }
+
     public CryptoModuleBuilder keyStore(File file) {
         this.keyStoreUrl = null;
         this.keyStoreFile = file;
@@ -120,6 +133,11 @@ public class CryptoModuleBuilder {
         return this;
     }
 
+    public CryptoModuleBuilder keySource(Class<? extends KeySource> type) {
+        this.keySourceType = type;
+        return this;
+    }
+
     /**
      * Produces a module that can be used to start Cayenne runtime.
      */
@@ -137,37 +155,44 @@ public class CryptoModuleBuilder {
             throw new IllegalStateException("'CipherFactory' is not initialized");
         }
 
-        if (keyStoreUrl == null && keyStoreFile == null) {
-            throw new IllegalStateException("'keyStore' is not initialized");
-        }
-
-        final String keyStoreUrl;
-        if (this.keyStoreUrl != null) {
-            keyStoreUrl = this.keyStoreUrl;
-        } else {
-            try {
-                keyStoreUrl = keyStoreFile.toURI().toURL().toExternalForm();
-            } catch (MalformedURLException e) {
-                throw new IllegalStateException("Invalid keyStore file", e);
-            }
-        }
-
         return new Module() {
 
             @Override
             public void configure(Binder binder) {
 
-                // init default cipher settings
-                binder.<String> bindMap(CryptoConstants.PROPERTIES_MAP)
+                String keyStoreUrl = null;
+                if (CryptoModuleBuilder.this.keyStoreUrl != null) {
+                    keyStoreUrl = CryptoModuleBuilder.this.keyStoreUrl;
+                } else if (keyStoreFile != null) {
+                    try {
+                        keyStoreUrl = keyStoreFile.toURI().toURL().toExternalForm();
+                    } catch (MalformedURLException e) {
+                        throw new IllegalStateException("Invalid keyStore file", e);
+                    }
+                }
+
+                // String properties
+                MapBuilder<String> props = binder.<String> bindMap(CryptoConstants.PROPERTIES_MAP)
                         .put(CryptoConstants.CIPHER_ALGORITHM, cipherAlgoritm)
                         .put(CryptoConstants.CIPHER_MODE, cipherMode)
-                        .put(CryptoConstants.CIPHER_PADDING, cipherPadding)
-                        .put(CryptoConstants.KEYSTORE_URL, keyStoreUrl);
+                        .put(CryptoConstants.CIPHER_PADDING, cipherPadding);
+
+                if (keyStoreUrl != null) {
+                    props.put(CryptoConstants.KEYSTORE_URL, keyStoreUrl);
+                }
+
+                // char[] credentials... stored as char[] to potentially allow
+                // wiping them clean in memory...
+                MapBuilder<char[]> creds = binder.<char[]> bindMap(CryptoConstants.CREDENTIALS_MAP);
+
+                if (keyPassword != null) {
+                    creds.put(CryptoConstants.KEY_PASSWORD, keyPassword);
+                }
 
                 binder.bind(CipherFactory.class).to(cipherFactoryType);
                 binder.bind(TransformerFactory.class).to(DefaultTransformerFactory.class);
                 binder.bind(ValueTransformerFactory.class).to(valueTransformerFactoryType);
-                binder.bind(KeySource.class).to(KeyStoreKeySource.class);
+                binder.bind(KeySource.class).to(keySourceType);
 
                 if (columnMapperType != null) {
                     binder.bind(ColumnMapper.class).to(columnMapperType);

Modified: cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/key/KeyStoreKeySource.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/key/KeyStoreKeySource.java?rev=1583130&r1=1583129&r2=1583130&view=diff
==============================================================================
--- cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/key/KeyStoreKeySource.java (original)
+++ cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/key/KeyStoreKeySource.java Sun Mar 30 13:19:09 2014
@@ -40,42 +40,40 @@ import org.apache.cayenne.di.Inject;
  */
 public class KeyStoreKeySource implements KeySource {
 
+    // this is the only standard keystore type that supports storing secret keys
+    private static final String JCEKS_KEYSTORE_TYPE = "jceks";
+
     private KeyStore keyStore;
-    private char[] keyPasswordChars;
+    private char[] keyPassword;
 
-    public KeyStoreKeySource(@Inject(CryptoConstants.PROPERTIES_MAP) Map<String, String> properties)
-            throws KeyStoreException {
+    public KeyStoreKeySource(@Inject(CryptoConstants.PROPERTIES_MAP) Map<String, String> properties,
+            @Inject(CryptoConstants.CREDENTIALS_MAP) Map<String, char[]> credentials) {
 
         String keyStoreUrl = properties.get(CryptoConstants.KEYSTORE_URL);
         if (keyStoreUrl == null) {
             throw new CayenneCryptoException("KeyStore URL is not set. Property name: " + CryptoConstants.KEYSTORE_URL);
         }
 
-        String keyStorePassword = properties.get(CryptoConstants.KEYSTORE_PASSWORD);
-        // NULL password is valid, though not secure .. so no NULL validation
-
-        String keyPassword = properties.get(CryptoConstants.KEY_PASSWORD);
-        this.keyPasswordChars = keyPassword != null ? keyPassword.toCharArray() : null;
+        this.keyPassword = credentials.get(CryptoConstants.KEY_PASSWORD);
         // NULL password is valid, though not secure .. so no NULL validation
 
         try {
-            this.keyStore = createKeyStore(keyStoreUrl, keyStorePassword);
+            this.keyStore = createKeyStore(keyStoreUrl);
         } catch (Exception e) {
             throw new CayenneCryptoException("Error loading keystore at " + keyStoreUrl, e);
         }
     }
 
-    private KeyStore createKeyStore(String keyStoreUrl, String keyStorePassword) throws KeyStoreException, IOException,
+    private KeyStore createKeyStore(String keyStoreUrl) throws KeyStoreException, IOException,
             NoSuchAlgorithmException, CertificateException {
-        char[] keyStorePasswordChars = keyStorePassword != null ? keyStorePassword.toCharArray() : null;
 
-        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+        KeyStore keyStore = KeyStore.getInstance(JCEKS_KEYSTORE_TYPE);
 
         URL url = new URL(keyStoreUrl);
         InputStream in = url.openStream();
 
         try {
-            keyStore.load(in, keyStorePasswordChars);
+            keyStore.load(in, null);
         } finally {
             in.close();
         }
@@ -86,7 +84,7 @@ public class KeyStoreKeySource implement
     @Override
     public Key getKey(String alias) {
         try {
-            return keyStore.getKey(alias, keyPasswordChars);
+            return keyStore.getKey(alias, keyPassword);
         } catch (Exception e) {
             throw new CayenneCryptoException("Error accessing key for alias: " + alias, e);
         }

Added: cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/key/KeyStoreKeySourceTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/key/KeyStoreKeySourceTest.java?rev=1583130&view=auto
==============================================================================
--- cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/key/KeyStoreKeySourceTest.java (added)
+++ cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/key/KeyStoreKeySourceTest.java Sun Mar 30 13:19:09 2014
@@ -0,0 +1,74 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.crypto.key;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.net.URL;
+import java.security.Key;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cayenne.crypto.CayenneCryptoException;
+import org.apache.cayenne.crypto.CryptoConstants;
+import org.junit.Test;
+
+public class KeyStoreKeySourceTest {
+
+    private static final char[] TEST_KEY_PASS = "testkeypass".toCharArray();
+
+    @Test(expected = CayenneCryptoException.class)
+    public void testConstructor_NoUrl() {
+        Map<String, String> props = new HashMap<String, String>();
+        Map<String, char[]> creds = new HashMap<String, char[]>();
+        new KeyStoreKeySource(props, creds);
+    }
+
+    @Test
+    public void testGetKey_JCEKS_DES() {
+
+        URL url = getClass().getResource("ks1.jceks");
+        assertNotNull(url);
+
+        Map<String, String> props = new HashMap<String, String>();
+        props.put(CryptoConstants.KEYSTORE_URL, url.toExternalForm());
+        
+        Map<String, char[]> creds = new HashMap<String, char[]>();
+        creds.put(CryptoConstants.KEY_PASSWORD, TEST_KEY_PASS);
+
+        KeyStoreKeySource ks = new KeyStoreKeySource(props, creds);
+
+        assertNull(ks.getKey("no-such-key"));
+
+        Key k1 = ks.getKey("k1");
+        assertNotNull(k1);
+        assertEquals("DES", k1.getAlgorithm());
+
+        Key k2 = ks.getKey("k2");
+        assertNotNull(k2);
+        assertEquals("DES", k2.getAlgorithm());
+        
+        Key k3 = ks.getKey("k3");
+        assertNotNull(k3);
+        assertEquals("AES", k3.getAlgorithm());
+    }
+
+}

Added: cayenne/main/trunk/cayenne-crypto/src/test/resources/org/apache/cayenne/crypto/key/README-keystore.txt
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-crypto/src/test/resources/org/apache/cayenne/crypto/key/README-keystore.txt?rev=1583130&view=auto
==============================================================================
--- cayenne/main/trunk/cayenne-crypto/src/test/resources/org/apache/cayenne/crypto/key/README-keystore.txt (added)
+++ cayenne/main/trunk/cayenne-crypto/src/test/resources/org/apache/cayenne/crypto/key/README-keystore.txt Sun Mar 30 13:19:09 2014
@@ -0,0 +1,15 @@
+Contains unit test keystore passwords. Storing in plaintext here, as none of these 
+keystores store any real keys.
+
+ks1.jceks
+---------
+
+Created with: 
+keytool -genseckey -keystore ./ks1.jceks -storetype JCEKS -alias k1
+keytool -genseckey -keystore ./ks1.jceks -storetype JCEKS -alias k2
+keytool -genseckey -keystore ./ks1.jceks -storetype JCEKS -keyalg AES -keysize 128 -alias k3
+
+	Keystore - testkspass
+		k1 - testkeypass
+		k2 - testkeypass
+		k3 - testkeypass

Added: cayenne/main/trunk/cayenne-crypto/src/test/resources/org/apache/cayenne/crypto/key/ks1.jceks
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-crypto/src/test/resources/org/apache/cayenne/crypto/key/ks1.jceks?rev=1583130&view=auto
==============================================================================
Files cayenne/main/trunk/cayenne-crypto/src/test/resources/org/apache/cayenne/crypto/key/ks1.jceks (added) and cayenne/main/trunk/cayenne-crypto/src/test/resources/org/apache/cayenne/crypto/key/ks1.jceks Sun Mar 30 13:19:09 2014 differ