001/** 002 * Copyright 2010-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.common.util.encrypt.openssl; 017 018import static com.google.common.base.Preconditions.checkArgument; 019import static com.google.common.collect.Lists.newArrayList; 020import static java.lang.System.arraycopy; 021import static org.kuali.common.util.Ascii.isDigit; 022import static org.kuali.common.util.Ascii.isLetter; 023import static org.kuali.common.util.base.Exceptions.illegalArgument; 024import static org.kuali.common.util.base.Precondition.checkNotNull; 025import static org.kuali.common.util.encrypt.openssl.OpenSSLContext.buildOpenSSLContext; 026 027import java.security.MessageDigest; 028import java.security.NoSuchAlgorithmException; 029import java.security.SecureRandom; 030import java.util.List; 031import java.util.Random; 032 033import org.kuali.common.util.encrypt.EncryptionContext; 034 035import com.google.common.collect.ImmutableList; 036 037public class OpenSSL { 038 039 private static final Random RANDOM = new SecureRandom(); 040 private static final int DEFAULT_SALT_SIZE = 8; 041 // When using all 62 alphanumeric characters, 22 is the minimum length required for producing more combinations than what is possible with 128 bits 042 // In other words, 62^22 is greater than 2^128 043 private static final int DEFAULT_PASSWORD_LENGTH = 22; 044 private static final List<Character> DEFAULT_PASSWORD_CHARS = getAlphaNumericCharacters(); 045 046 /** 047 * Uses SecureRandom to generate a random 22 character alphanumeric string 048 */ 049 public static String generatePassword() { 050 return generatePassword(DEFAULT_PASSWORD_LENGTH); 051 } 052 053 /** 054 * Uses SecureRandom to generate a random alphanumeric string of the specified length 055 */ 056 public static String generatePassword(int length) { 057 return generatePassword(DEFAULT_PASSWORD_CHARS, length); 058 } 059 060 public static String generatePassword(List<Character> chars, int length) { 061 return generatePassword(chars, length, RANDOM); 062 } 063 064 public static String generatePassword(List<Character> chars, int length, Random random) { 065 char[] buffer = new char[length]; 066 int size = chars.size(); 067 for (int i = 0; i < length; i++) { 068 buffer[i] = chars.get(random.nextInt(size)); 069 } 070 return new String(buffer); 071 } 072 073 public static OpenSSLEncryptor buildOpenSSLEncryptor(EncryptionContext context) { 074 OpenSSLContext osc = buildOpenSSLContext(context.getStrength()); 075 return new OpenSSLEncryptor(osc, context.getPassword()); 076 } 077 078 public static byte[] combineByteArrays(byte[]... arrays) { 079 byte[] bytes = allocateByteArray(arrays); 080 int offset = 0; 081 for (byte[] array : arrays) { 082 offset = addByteArray(bytes, array, offset); 083 } 084 return bytes; 085 } 086 087 public static int addByteArray(byte[] dst, byte[] src, int offset) { 088 arraycopy(src, 0, dst, offset, src.length); 089 return offset + src.length; 090 } 091 092 public static byte[] allocateByteArray(byte[]... arrays) { 093 int length = 0; 094 for (byte[] array : arrays) { 095 length += array.length; 096 } 097 return new byte[length]; 098 } 099 100 /** 101 * Creates an 8 byte salt 102 */ 103 public static byte[] createSalt() { 104 return createSalt(DEFAULT_SALT_SIZE); 105 } 106 107 /** 108 * Creates a salt of the indicated length 109 */ 110 public static byte[] createSalt(int length) { 111 byte[] salt = new byte[length]; 112 RANDOM.nextBytes(salt); 113 return salt; 114 } 115 116 public static final byte[] toByteArray(List<Byte> bytes) { 117 byte[] array = new byte[bytes.size()]; 118 for (int i = 0; i < array.length; i++) { 119 array[i] = bytes.get(i); 120 } 121 return array; 122 } 123 124 public static ImmutableList<Byte> toByteList(byte[] original) { 125 return toByteList(original, 0, original.length); 126 } 127 128 public static ImmutableList<Byte> toByteList(byte[] original, int offset, int length) { 129 List<Byte> list = newArrayList(); 130 for (int i = offset; i < length; i++) { 131 list.add(original[i]); 132 } 133 return ImmutableList.copyOf(list); 134 } 135 136 public static String checkBase64(String text) { 137 checkNotNull(text, "text"); 138 for (char c : text.toCharArray()) { 139 checkArgument(isBase64(c), "'%s' is not a base 64 character", c); 140 } 141 return text; 142 } 143 144 public static boolean isBase64(char c) { 145 if (isLetter(c) || isDigit(c)) { 146 return true; 147 } else { 148 return c == '/' || c == '+' || c == '='; 149 } 150 } 151 152 public static MessageDigest getMessageDigest(String algorithm) { 153 try { 154 return MessageDigest.getInstance(algorithm); 155 } catch (NoSuchAlgorithmException e) { 156 throw illegalArgument(e); 157 } 158 159 } 160 161 public static OpenSSLEncryptedContext buildEncryptedContext(OpenSSLContext context, int initVectorLength, byte[] salt, byte[] data) { 162 MessageDigest md = getMessageDigest(context.getDigestAlgorithm()); 163 int keyLength = context.getKeySizeBits() / Byte.SIZE; 164 byte[] key = new byte[keyLength]; 165 int keyIndex = 0; 166 byte[] initVector = new byte[initVectorLength]; 167 int initVectorIndex = 0; 168 byte[] md_buf = null; 169 int nkey = keyLength; 170 int niv = initVectorLength; 171 int i = 0; 172 int addmd = 0; 173 for (;;) { 174 md.reset(); 175 if (addmd++ > 0) { 176 md.update(md_buf); 177 } 178 md.update(data); 179 if (null != salt) { 180 md.update(salt, 0, 8); 181 } 182 md_buf = md.digest(); 183 for (i = 1; i < context.getIterations(); i++) { 184 md.reset(); 185 md.update(md_buf); 186 md_buf = md.digest(); 187 } 188 i = 0; 189 if (nkey > 0) { 190 for (;;) { 191 if (nkey == 0) 192 break; 193 if (i == md_buf.length) 194 break; 195 key[keyIndex++] = md_buf[i]; 196 nkey--; 197 i++; 198 } 199 } 200 if (niv > 0 && i != md_buf.length) { 201 for (;;) { 202 if (niv == 0) 203 break; 204 if (i == md_buf.length) 205 break; 206 initVector[initVectorIndex++] = md_buf[i]; 207 niv--; 208 i++; 209 } 210 } 211 if (nkey == 0 && niv == 0) { 212 break; 213 } 214 } 215 for (i = 0; i < md_buf.length; i++) { 216 md_buf[i] = 0; 217 } 218 219 OpenSSLEncryptedContext.Builder builder = OpenSSLEncryptedContext.builder(); 220 builder.withSalt(toByteList(salt)); 221 builder.withKey(toByteList(key)); 222 builder.withInitVector(toByteList(initVector)); 223 return builder.build(); 224 } 225 226 protected static List<Character> getAlphaNumericCharacters() { 227 List<Character> chars = newArrayList(); 228 for (char c = 'A'; c < 'Z'; c++) { 229 chars.add(c); 230 } 231 for (char c = 'a'; c < 'z'; c++) { 232 chars.add(c); 233 } 234 for (char c = '0'; c < '9'; c++) { 235 chars.add(c); 236 } 237 return ImmutableList.copyOf(chars); 238 } 239 240}