You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@archiva.apache.org by ma...@apache.org on 2017/02/12 17:48:49 UTC

[2/4] archiva-redback-core git commit: Using random iv vector to generate unique tokens for each login

Using random iv vector to generate unique tokens for each login


Project: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/commit/a6ceb384
Tree: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/tree/a6ceb384
Diff: http://git-wip-us.apache.org/repos/asf/archiva-redback-core/diff/a6ceb384

Branch: refs/heads/feature/header_check
Commit: a6ceb384697b06d361c3323e24501cc7f2475900
Parents: de5816a
Author: Martin Stockhammer <ma...@apache.org>
Authored: Sun Feb 12 17:09:15 2017 +0100
Committer: Martin Stockhammer <ma...@apache.org>
Committed: Sun Feb 12 17:09:15 2017 +0100

----------------------------------------------------------------------
 .../redback/authentication/TokenManager.java    | 115 +++++++++++++++----
 .../authentication/TokenManagerTest.java        |   7 +-
 2 files changed, 94 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/a6ceb384/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenManager.java
----------------------------------------------------------------------
diff --git a/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenManager.java b/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenManager.java
index 08538bd..e37c1d4 100644
--- a/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenManager.java
+++ b/redback-authentication/redback-authentication-api/src/main/java/org/apache/archiva/redback/authentication/TokenManager.java
@@ -20,6 +20,7 @@ package org.apache.archiva.redback.authentication;
  */
 
 import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.ArrayUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
@@ -57,12 +58,12 @@ import java.util.Arrays;
 @Service("tokenManager#jce")
 public class TokenManager {
 
-    private static final SecureRandom rd = new SecureRandom();
+    private final ThreadLocal<SecureRandom> rd = new ThreadLocal<SecureRandom>();
     private final Logger log = LoggerFactory.getLogger(getClass());
-    private String algorithm = "AES/ECB/PKCS5Padding";
+    private String algorithm = "AES/CBC/PKCS5Padding";
     private int keySize = -1;
-    private Cipher deCipher;
-    private Cipher enCipher;
+    private int ivSize = -1;
+    private SecretKey secretKey;
 
     boolean paddingUsed = true;
 
@@ -71,8 +72,8 @@ public class TokenManager {
     public void initialize() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, EncryptionFailedException, InvalidAlgorithmParameterException {
         log.debug("Initializing key for token generator");
         try {
-            enCipher = Cipher.getInstance(algorithm);
-            deCipher = Cipher.getInstance(algorithm);
+            rd.set(new SecureRandom());
+            Cipher enCipher = Cipher.getInstance(algorithm);
             String[] keyAlg = enCipher.getAlgorithm().split("/");
             if (keyAlg.length<1) {
                 throw new EncryptionFailedException("Initialization of key failed. Not algorithm found.");
@@ -85,14 +86,14 @@ public class TokenManager {
             if (keyAlg.length==3 && keyAlg[2].equals("NoPadding")) {
                 paddingUsed=false;
             }
-            SecretKey secretKey = keyGen.generateKey();
+            this.secretKey = keyGen.generateKey();
             enCipher.init(Cipher.ENCRYPT_MODE, secretKey);
             // We have to provide the IV depending on the algorithm used
             // CBC needs an IV, ECB not.
             if (enCipher.getIV()==null) {
-                deCipher.init(Cipher.DECRYPT_MODE, secretKey);
+                ivSize=-1;
             } else {
-                deCipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(enCipher.getIV()));
+                ivSize=enCipher.getIV().length;
             }
         } catch (NoSuchAlgorithmException e) {
             log.error("Error occurred during key initialization. Requested algorithm not available. "+e.getMessage());
@@ -103,14 +104,9 @@ public class TokenManager {
         } catch (InvalidKeyException e) {
             log.error("The key is not valid.");
             throw e;
-        } catch (InvalidAlgorithmParameterException e) {
-            log.error("Invalid encryption parameters.");
-            throw e;
         }
     }
 
-
-    @SuppressWarnings("SameParameterValue")
     public String encryptToken(String user, long lifetime) throws EncryptionFailedException {
         return encryptToken(new SimpleTokenData(user, lifetime, createNonce()));
     }
@@ -127,6 +123,18 @@ public class TokenManager {
         } catch (IllegalBlockSizeException e) {
             log.error("Block size invalid");
             throw new EncryptionFailedException(e);
+        } catch (NoSuchPaddingException e) {
+            log.error("Padding not available "+algorithm);
+            throw new EncryptionFailedException(e);
+        } catch (InvalidKeyException e) {
+            log.error("Bad encryption key");
+            throw new EncryptionFailedException(e);
+        } catch (NoSuchAlgorithmException e) {
+            log.error("Bad encryption algorithm "+algorithm);
+            throw new EncryptionFailedException(e);
+        } catch (InvalidAlgorithmParameterException e) {
+            log.error("Invalid encryption parameters");
+            throw new EncryptionFailedException(e);
         }
     }
 
@@ -145,29 +153,63 @@ public class TokenManager {
         } catch (IllegalBlockSizeException ex) {
             log.error("The encrypted token has the wrong block size.");
             throw new InvalidTokenException(ex);
+        } catch (NoSuchPaddingException e) {
+            log.error("Padding not available "+algorithm);
+            throw new InvalidTokenException(e);
+        } catch (InvalidKeyException e) {
+            log.error("Invalid decryption key");
+            throw new InvalidTokenException(e);
+        } catch (NoSuchAlgorithmException e) {
+            log.error("Encryption algorithm not available "+algorithm);
+            throw new InvalidTokenException(e);
+        } catch (InvalidAlgorithmParameterException e) {
+            log.error("Invalid encryption parameters");
+            throw new InvalidTokenException(e);
         }
     }
 
     private long createNonce() {
-        return rd.nextLong();
+        if (rd.get()==null) {
+            rd.set(new SecureRandom());
+        }
+        return rd.get().nextLong();
+    }
+
+    protected byte[] encrypt(TokenData info) throws IOException, BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException {
+        return doEncrypt(convertToByteArray(info), info.getNonce());
     }
 
-    protected byte[] encrypt(TokenData info) throws IOException, BadPaddingException, IllegalBlockSizeException {
-        return doEncrypt(convertToByteArray(info));
+    private byte[] getIv(long nonce) {
+        byte[] iv = new byte[ivSize];
+        SecureRandom sr = getRandomGenerator();
+        sr.setSeed(nonce);
+        sr.nextBytes(iv);
+        return iv;
     }
 
-    protected byte[] doEncrypt(byte[] data) throws BadPaddingException, IllegalBlockSizeException {
+    protected byte[] doEncrypt(byte[] data, long nonce) throws BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
+        Cipher cipher = getEnCipher();
         byte[] encData;
-        if (!paddingUsed && (data.length % enCipher.getBlockSize())!=0) {
-            int blocks = data.length / enCipher.getBlockSize();
-            encData = Arrays.copyOf(data, enCipher.getBlockSize()*(blocks+1));
+        byte[] iv;
+        if (ivSize>0) {
+            iv = getIv(nonce);
+            cipher.init(Cipher.ENCRYPT_MODE,this.secretKey,new IvParameterSpec(iv));
+        } else {
+            iv = new byte[0];
+            cipher.init(Cipher.ENCRYPT_MODE,this.secretKey);
+        }
+        if (!paddingUsed && (data.length % cipher.getBlockSize())!=0) {
+            int blocks = data.length / cipher.getBlockSize();
+            encData = Arrays.copyOf(data, cipher.getBlockSize()*(blocks+1));
         } else {
             encData = data;
         }
-        return enCipher.doFinal(encData);
+        byte[] encrypted = cipher.doFinal(encData);
+        // prepending the iv to the token
+        return ArrayUtils.addAll(iv,encrypted);
     }
 
-    protected TokenData decrypt(byte[] token) throws BadPaddingException, IllegalBlockSizeException, IOException, ClassNotFoundException {
+    protected TokenData decrypt(byte[] token) throws BadPaddingException, IllegalBlockSizeException, IOException, ClassNotFoundException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException {
         Object result = convertFromByteArray(doDecrypt(token));
         if (!(result instanceof TokenData)) {
             throw new InvalidClassException("No TokenData found in decrypted token");
@@ -175,8 +217,31 @@ public class TokenManager {
         return (TokenData)result;
     }
 
-    protected byte[] doDecrypt(byte[] encryptedData) throws BadPaddingException, IllegalBlockSizeException {
-        return deCipher.doFinal(encryptedData);
+    protected byte[] doDecrypt(byte[] encryptedData) throws BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
+        Cipher cipher = getDeCipher();
+        if (ivSize>0) {
+            byte[] iv = Arrays.copyOfRange(encryptedData,0,ivSize);
+            cipher.init(Cipher.DECRYPT_MODE,this.secretKey,new IvParameterSpec(iv));
+            return cipher.doFinal(encryptedData,ivSize,encryptedData.length-ivSize);
+        } else {
+            cipher.init(Cipher.DECRYPT_MODE,this.secretKey);
+            return cipher.doFinal(encryptedData);
+        }
+    }
+
+    private SecureRandom getRandomGenerator() {
+        if (rd.get()==null) {
+            rd.set(new SecureRandom());
+        }
+        return rd.get();
+    }
+
+    private Cipher getEnCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
+        return Cipher.getInstance(algorithm);
+    }
+
+    private Cipher getDeCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
+        return Cipher.getInstance(algorithm);
     }
 
     private String encode(byte[] token) {

http://git-wip-us.apache.org/repos/asf/archiva-redback-core/blob/a6ceb384/redback-authentication/redback-authentication-api/src/test/java/org/apache/archiva/redback/authentication/TokenManagerTest.java
----------------------------------------------------------------------
diff --git a/redback-authentication/redback-authentication-api/src/test/java/org/apache/archiva/redback/authentication/TokenManagerTest.java b/redback-authentication/redback-authentication-api/src/test/java/org/apache/archiva/redback/authentication/TokenManagerTest.java
index 5200956..1a07033 100644
--- a/redback-authentication/redback-authentication-api/src/test/java/org/apache/archiva/redback/authentication/TokenManagerTest.java
+++ b/redback-authentication/redback-authentication-api/src/test/java/org/apache/archiva/redback/authentication/TokenManagerTest.java
@@ -27,6 +27,7 @@ import javax.crypto.NoSuchPaddingException;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
 import java.util.Arrays;
 
 import static org.junit.Assert.*;
@@ -48,7 +49,7 @@ public class TokenManagerTest {
     public void encryptToken() throws Exception {
         TokenManager tokenManager = new TokenManager();
         tokenManager.initialize();
-        assertEquals(tokenManager.getAlgorithm(),"AES/ECB/PKCS5Padding");
+        assertEquals(tokenManager.getAlgorithm(),"AES/CBC/PKCS5Padding");
         assertEquals(tokenManager.getKeySize(), -1);
         String token = tokenManager.encryptToken("testuser01",1000);
         assertNotNull(token);
@@ -131,7 +132,7 @@ public class TokenManagerTest {
         tokenManager.setKeySize(56);
         tokenManager.initialize();
         byte[] data = { 1, 5, 12, 18, 124, 44, 88, -28, -44};
-        byte[] token = tokenManager.doEncrypt(data);
+        byte[] token = tokenManager.doEncrypt(data, new SecureRandom().nextLong());
         assertNotNull(token);
         byte[] tokenData = tokenManager.doDecrypt(token);
         assertNotNull(tokenData);
@@ -141,7 +142,7 @@ public class TokenManagerTest {
         tokenManager.setAlgorithm("AES/CBC/NoPadding");
         tokenManager.setKeySize(128);
         tokenManager.initialize();
-        token = tokenManager.doEncrypt(data);
+        token = tokenManager.doEncrypt(data, new SecureRandom().nextLong());
         assertNotNull(token);
         // Without padding the decrypted value is a multiple of the block size.
         tokenData = Arrays.copyOf(tokenManager.doDecrypt(token), data.length);