From a5445100050e49e83f73424198d73cd72d672a4d Mon Sep 17 00:00:00 2001 From: Michael Lando Date: Sun, 4 Mar 2018 14:53:33 +0200 Subject: Sync Integ to Master Change-Id: I71e3acc26fa612127756ac04073a522b9cc6cd74 Issue-ID: SDC-977 Signed-off-by: Gitelman, Tal (tg851x) --- .../java/org/openecomp/sdc/security/Passwords.java | 36 ++++- .../org/openecomp/sdc/security/SecurityUtil.java | 155 +++++++++++++++++++++ .../org/openecomp/sdc/security/PasswordTest.java | 48 ------- .../org/openecomp/sdc/security/PasswordsTest.java | 75 ++++++++++ .../openecomp/sdc/security/SecurityUtilTest.java | 27 ++++ 5 files changed, 288 insertions(+), 53 deletions(-) create mode 100644 security-utils/src/main/java/org/openecomp/sdc/security/SecurityUtil.java delete mode 100644 security-utils/src/test/java/org/openecomp/sdc/security/PasswordTest.java create mode 100644 security-utils/src/test/java/org/openecomp/sdc/security/PasswordsTest.java create mode 100644 security-utils/src/test/java/org/openecomp/sdc/security/SecurityUtilTest.java (limited to 'security-utils/src') diff --git a/security-utils/src/main/java/org/openecomp/sdc/security/Passwords.java b/security-utils/src/main/java/org/openecomp/sdc/security/Passwords.java index ef424b95de..5f5e00722e 100644 --- a/security-utils/src/main/java/org/openecomp/sdc/security/Passwords.java +++ b/security-utils/src/main/java/org/openecomp/sdc/security/Passwords.java @@ -20,6 +20,9 @@ package org.openecomp.sdc.security; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -27,8 +30,10 @@ import java.security.SecureRandom; import java.util.Arrays; import java.util.Random; + public class Passwords { + private static Logger log = LoggerFactory.getLogger( Passwords.class.getName()); private static final Random RANDOM = new SecureRandom(); private static final int SALT = 0; private static final int HASH = 1; @@ -47,13 +52,14 @@ public class Passwords { * @return a "salt:hash" value */ public static String hashPassword(String password) { - byte[] salt = getNextSalt(); - byte byteData[] = hash(salt, password.getBytes()); - if (byteData != null) { - return toHex(salt) + ":" + toHex(byteData); + if (password!=null){ + byte[] salt = getNextSalt(); + byte byteData[] = hash(salt, password.getBytes()); + if (byteData != null) { + return toHex(salt) + ":" + toHex(byteData); + } } return null; - } /** @@ -64,6 +70,15 @@ public class Passwords { * @return */ public static boolean isExpectedPassword(String password, String expectedHash) { + if (password==null && expectedHash==null) + return true; + if (password==null || expectedHash==null) //iff exactly 1 is null + return false; + if (!expectedHash.contains(":")){ + log.error("invalid password expecting hash at the prefix of the password (ex. e0277df331f4ff8f74752ac4a8fbe03b:6dfbad308cdf53c9ff2ee2dca811ee92f1b359586b33027580e2ff92578edbd0)\n" + + "\t\t\t"); + return false; + } String[] params = expectedHash.split(":"); return isExpectedPassword(password, params[SALT], params[HASH]); } @@ -78,6 +93,15 @@ public class Passwords { * @return true if the password matched the hash */ public static boolean isExpectedPassword(String password, String salt, String hash) { + if ( password == null && hash == null ) + return true; + if ( salt == null ){ + log.error("salt must be initialized"); + return false; + } + //unintialized params + if ( password == null || hash == null ) + return false; byte[] saltBytes = fromHex(salt); byte[] hashBytes = fromHex(hash); @@ -137,6 +161,8 @@ public class Passwords { * @return the hex string decoded into a byte array */ private static byte[] fromHex(String hex) { + if ( hex == null ) + return null; byte[] binary = new byte[hex.length() / 2]; for (int i = 0; i < binary.length; i++) { binary[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16); diff --git a/security-utils/src/main/java/org/openecomp/sdc/security/SecurityUtil.java b/security-utils/src/main/java/org/openecomp/sdc/security/SecurityUtil.java new file mode 100644 index 0000000000..892c29b88f --- /dev/null +++ b/security-utils/src/main/java/org/openecomp/sdc/security/SecurityUtil.java @@ -0,0 +1,155 @@ +package org.openecomp.sdc.security; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import fj.data.*; + +public class SecurityUtil { + + private static final Logger LOG = LoggerFactory.getLogger( SecurityUtil.class ); + private static final byte[] KEY = new byte[]{-64,5,-32 ,-117 ,-44,8,-39, 1, -9, 36,-46,-81, 62,-15,-63,-75}; + public static final SecurityUtil INSTANCE = new SecurityUtil(); + public static final String ALGORITHM = "AES" ; + public static final String CHARSET = StandardCharsets.UTF_8.name(); + + public static Key secKey = null ; + + /** + * + * cmd commands >$PROGRAM_NAME decrypt "$ENCRYPTED_MSG" + * >$PROGRAM_NAME encrypt "message" + **/ + public static void main(String[] args) throws Exception { + if ( args!=null && args.length>1){ + fj.data.Either res = null; + final String op = args[0].trim().toLowerCase(); + try{ + switch(op) { + case "decrypt": + res = INSTANCE.decrypt(Base64.getDecoder().decode(args[1]), true); + break; + case "encrypt": + res = INSTANCE.encrypt(args[1]); + break; + default: + LOG.warn("Unfamiliar command please use: \n>aes 'message to encrypt/decrypt' "); + } + }catch(Exception e){ + LOG.debug( "cannot perform {}:" ); + throw e; + } + LOG.debug( "output: {}", res!=null && res.isLeft() ? res.left().value() : "ERROR" ); + } + } + + private SecurityUtil(){ super(); } + + static { + try{ + secKey = generateKey( KEY, ALGORITHM ); + } + catch(Exception e){ + LOG.warn("cannot generate key for {}", ALGORITHM); + } + } + + + + public static Key generateKey(final byte[] KEY, String algorithm){ + return new SecretKeySpec(KEY, algorithm); + } + + //obfuscates key prefix -> ********** + public String obfuscateKey(String sensitiveData){ + + if (sensitiveData != null){ + int len = sensitiveData.length(); + StringBuilder builder = new StringBuilder(sensitiveData); + for (int i=0; i encrypt(String strDataToEncrypt){ + if (strDataToEncrypt != null ){ + try { + LOG.debug("Encrypt key -> {}", secKey); + Cipher aesCipherForEncryption = Cipher.getInstance("AES"); // Must specify the mode explicitly as most JCE providers default to ECB mode!! + aesCipherForEncryption.init(Cipher.ENCRYPT_MODE, secKey); + byte[] byteDataToEncrypt = strDataToEncrypt.getBytes(); + byte[] byteCipherText = aesCipherForEncryption.doFinal(byteDataToEncrypt); + String strCipherText = new String( java.util.Base64.getMimeEncoder().encode(byteCipherText), CHARSET ); + LOG.debug("Cipher Text generated using AES is {}", strCipherText); + return Either.left(strCipherText); + } catch( NoSuchAlgorithmException | UnsupportedEncodingException e){ + LOG.warn( "cannot encrypt data unknown algorithm or missing encoding for {}" ,secKey.getAlgorithm()); + } catch( InvalidKeyException e){ + LOG.warn( "invalid key recieved - > {} | {}" , java.util.Base64.getDecoder().decode( secKey.getEncoded() ), e.getMessage() ); + } catch( IllegalBlockSizeException | BadPaddingException | NoSuchPaddingException e){ + LOG.warn( "bad algorithm definition (Illegal Block Size or padding), please review you algorithm block&padding" , e.getMessage() ); + } + } + return Either.right("Cannot encrypt "+strDataToEncrypt); + } + + /** + * Decrypt the Data + * @param byteCipherText - should be valid bae64 input in the length of 16bytes + * @param isBase64Decoded - is data already base64 encoded&aligned to 16 bytes + * a. Initialize a new instance of Cipher for Decryption (normally don't reuse the same object) + * b. Decrypt the cipher bytes using doFinal method + */ + public Either decrypt(byte[] byteCipherText , boolean isBase64Decoded){ + if (byteCipherText != null){ + byte[] alignedCipherText = byteCipherText; + try{ + if (isBase64Decoded) + alignedCipherText = Base64.getDecoder().decode(byteCipherText); + LOG.debug("Decrypt key -> "+secKey.getEncoded()); + Cipher aesCipherForDecryption = Cipher.getInstance("AES"); // Must specify the mode explicitly as most JCE providers default to ECB mode!! + aesCipherForDecryption.init(Cipher.DECRYPT_MODE, secKey); + byte[] byteDecryptedText = aesCipherForDecryption.doFinal(alignedCipherText); + String strDecryptedText = new String(byteDecryptedText); + LOG.debug("Decrypted Text message is: {}" , obfuscateKey( strDecryptedText )); + return Either.left(strDecryptedText); + } catch( NoSuchAlgorithmException e){ + LOG.warn( "cannot encrypt data unknown algorithm or missing encoding for {}" ,secKey.getAlgorithm()); + } catch( InvalidKeyException e){ + LOG.warn( "invalid key recieved - > {} | {}" , java.util.Base64.getDecoder().decode( secKey.getEncoded() ), e.getMessage() ); + } catch( IllegalBlockSizeException | BadPaddingException | NoSuchPaddingException e){ + LOG.warn( "bad algorithm definition (Illegal Block Size or padding), please review you algorithm block&padding" , e.getMessage() ); + } + } + return Either.right("Decrypt FAILED"); + } + + public Either decrypt(String byteCipherText){ + try { + return decrypt(byteCipherText.getBytes(CHARSET),true); + } catch( UnsupportedEncodingException e ){ + LOG.warn( "Missing encoding for {} | {} " ,secKey.getAlgorithm() , e.getMessage()); + } + return Either.right("Decrypt FAILED"); + } +} diff --git a/security-utils/src/test/java/org/openecomp/sdc/security/PasswordTest.java b/security-utils/src/test/java/org/openecomp/sdc/security/PasswordTest.java deleted file mode 100644 index 895806d1b5..0000000000 --- a/security-utils/src/test/java/org/openecomp/sdc/security/PasswordTest.java +++ /dev/null @@ -1,48 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed 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. - * ============LICENSE_END========================================================= - */ - -package org.openecomp.sdc.security; - -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -public class PasswordTest { - - @Test - public void hashtest() { - String password = "123456"; - String hash = Passwords.hashPassword(password); - assertTrue(Passwords.isExpectedPassword(password, hash)); - password = "1sdfgsgd23456"; - hash = Passwords.hashPassword(password); - assertTrue(Passwords.isExpectedPassword(password, hash)); - password = "1sdfgsgd2345((*&%$%6"; - hash = Passwords.hashPassword(password); - assertTrue(Passwords.isExpectedPassword(password, hash)); - password = ""; - hash = Passwords.hashPassword(password); - assertTrue(Passwords.isExpectedPassword(password, hash)); - password = " "; - hash = Passwords.hashPassword(password); - assertTrue(Passwords.isExpectedPassword(password, hash)); - } - -} diff --git a/security-utils/src/test/java/org/openecomp/sdc/security/PasswordsTest.java b/security-utils/src/test/java/org/openecomp/sdc/security/PasswordsTest.java new file mode 100644 index 0000000000..26f04735e5 --- /dev/null +++ b/security-utils/src/test/java/org/openecomp/sdc/security/PasswordsTest.java @@ -0,0 +1,75 @@ +package org.openecomp.sdc.security; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class PasswordsTest { + + @Test + public void hashPassword() throws Exception { + String hash = Passwords.hashPassword("hello1234"); + assertEquals(true, Passwords.isExpectedPassword("hello1234", hash)); + + //test different salt-> result in different hash + String hash2 = Passwords.hashPassword("hello1234"); + assertEquals(false, hash.equals(hash2)); + + String hash3 = Passwords.hashPassword(""); + assertEquals(true, Passwords.isExpectedPassword("", hash3)); + + String hash4 = Passwords.hashPassword(null); + assertEquals(true, hash4 == null ); + } + + @Test + public void isExpectedPassword() throws Exception { + //region isExpectedPassword(String password, String salt, String hash) + assertEquals(true, Passwords.isExpectedPassword(null, null , null)); + //valid hash + assertEquals(true, Passwords.isExpectedPassword("hello1234", "e0277df331f4ff8f74752ac4a8fbe03b","6dfbad308cdf53c9ff2ee2dca811ee92f1b359586b33027580e2ff92578edbd0")); + //invalid salt + assertEquals(false, Passwords.isExpectedPassword("hello1234", "c0000df331f4ff8f74752ac4a00be03c","6dfbad308cdf53c9ff2ee2dca811ee92f1b359586b33027580e2ff92578edbd0")); + assertEquals(false, Passwords.isExpectedPassword("hello1234", null,"6dfbad308cdf53c9ff2ee2dca811ee92f1b359586b33027580e2ff92578edbd0")); + //exacly 1 param uninitialized + assertEquals(false,Passwords.isExpectedPassword("hello1234", "",null)); + assertEquals(false,Passwords.isExpectedPassword( null, "" , "hello1234")); + //no salt & no hash + assertEquals(false, Passwords.isExpectedPassword("hello1234", null ,"hello1234")); + //endregion + + //region isExpectedPassword(String password, String expectedHash) + assertEquals(true, Passwords.isExpectedPassword(null, null)); + //valid hash + assertEquals(true, Passwords.isExpectedPassword("hello1234", "e0277df331f4ff8f74752ac4a8fbe03b:6dfbad308cdf53c9ff2ee2dca811ee92f1b359586b33027580e2ff92578edbd0")); + //invalid salt + assertEquals(false, Passwords.isExpectedPassword("hello1234", "c0000df331f4ff8f74752ac4a00be03c:6dfbad308cdf53c9ff2ee2dca811ee92f1b359586b33027580e2ff92578edbd0")); + //exacly 1 param uninitialized + assertEquals(false,Passwords.isExpectedPassword("hello1234", null)); + assertEquals(false,Passwords.isExpectedPassword( null,"hello1234")); + //no salt & no hash + assertEquals(false, Passwords.isExpectedPassword("hello1234", "hello1234")); + //endregion + } + + @Test + public void hashtest() { + String password = "123456"; + String hash = Passwords.hashPassword(password); + assertTrue(Passwords.isExpectedPassword(password, hash)); + password = "1sdfgsgd23456"; + hash = Passwords.hashPassword(password); + assertTrue(Passwords.isExpectedPassword(password, hash)); + password = "1sdfgsgd2345((*&%$%6"; + hash = Passwords.hashPassword(password); + assertTrue(Passwords.isExpectedPassword(password, hash)); + password = ""; + hash = Passwords.hashPassword(password); + assertTrue(Passwords.isExpectedPassword(password, hash)); + password = " "; + hash = Passwords.hashPassword(password); + assertTrue(Passwords.isExpectedPassword(password, hash)); + } + + +} \ No newline at end of file diff --git a/security-utils/src/test/java/org/openecomp/sdc/security/SecurityUtilTest.java b/security-utils/src/test/java/org/openecomp/sdc/security/SecurityUtilTest.java new file mode 100644 index 0000000000..e23c864b77 --- /dev/null +++ b/security-utils/src/test/java/org/openecomp/sdc/security/SecurityUtilTest.java @@ -0,0 +1,27 @@ +package org.openecomp.sdc.security; + +import org.junit.Test; +import java.util.Base64; +import static org.junit.Assert.*; + +public class SecurityUtilTest { + + @Test + public void encryptDecryptAES128() throws Exception { + String data = "decrypt SUCCESS!!"; + String encrypted = SecurityUtil.INSTANCE.encrypt(data).left().value(); + assertNotEquals( data, encrypted ); + byte[] decryptMsg = Base64.getDecoder().decode(encrypted); + assertEquals( SecurityUtil.INSTANCE.decrypt( decryptMsg , false ).left().value() ,data ); + assertEquals( SecurityUtil.INSTANCE.decrypt( encrypted.getBytes() , true ).left().value() ,data ); + } + + @Test + public void obfuscateKey() throws Exception { + String key = "abcdefghij123456"; + String expectedkey = "********ij123456"; + String obfuscated = SecurityUtil.INSTANCE.obfuscateKey( key ); + System.out.println( obfuscated ); + assertEquals( obfuscated , expectedkey ); + } +} \ No newline at end of file -- cgit 1.2.3-korg