1 package org.kuali.common.jute.enc.openssl;
2
3 import static com.google.common.io.ByteSource.concat;
4 import static com.google.common.io.ByteSource.wrap;
5 import static org.kuali.common.jute.base.Exceptions.illegalState;
6 import static org.kuali.common.jute.base.Precondition.checkNotNull;
7 import static org.kuali.common.jute.enc.cipher.CipherMode.DECRYPT;
8 import static org.kuali.common.jute.enc.cipher.CipherMode.ENCRYPT;
9 import static org.kuali.common.jute.enc.cipher.Ciphers.cipheredCopy;
10
11 import java.io.ByteArrayOutputStream;
12 import java.io.IOException;
13 import java.security.Key;
14 import java.security.MessageDigest;
15 import java.security.SecureRandom;
16 import java.security.spec.AlgorithmParameterSpec;
17 import java.util.Random;
18
19 import javax.crypto.Cipher;
20 import javax.crypto.spec.IvParameterSpec;
21 import javax.crypto.spec.SecretKeySpec;
22 import javax.inject.Inject;
23
24 import org.kuali.common.jute.enc.Encryptor;
25 import org.kuali.common.jute.enc.cipher.CipherMode;
26
27 import com.google.common.io.ByteSource;
28
29
30
31
32
33
34
35
36
37
38
39
40 public final class OpenSSLEncryptor implements Encryptor {
41
42 @Inject
43 public OpenSSLEncryptor(OpenSSLContext context) {
44 this.context = checkNotNull(context, "context");
45 }
46
47 private final OpenSSLContext context;
48
49 @Override
50 public String encrypt(String plaintext) {
51 ByteSource prefix = wrap(context.getSalt().getPrefix().getBytes(context.getCharset()));
52 ByteSource salt = buildSalt(context.getSalt());
53 ByteSource secret = wrap(context.getPassword().getBytes(context.getCharset()));
54 Cipher cipher = buildCipher(context, ENCRYPT, secret, salt);
55 try {
56 ByteSource source = wrap(plaintext.getBytes(context.getCharset()));
57 ByteArrayOutputStream out = new ByteArrayOutputStream();
58 cipheredCopy(source, out, cipher);
59 ByteSource encrypted = wrap(out.toByteArray());
60 ByteSource combined = concat(prefix, salt, encrypted);
61 return context.getEncoder().encode(combined.read());
62 } catch (IOException e) {
63 throw illegalState(e);
64 }
65 }
66
67 @Override
68 public String decrypt(String encrypted) {
69
70 byte[] bytes = context.getEncoder().decode(encrypted);
71
72
73 int prefixLength = context.getSalt().getPrefix().length();
74 int saltBytes = context.getSalt().getBytes();
75 int encryptedOffset = prefixLength + saltBytes;
76 int encryptedLength = bytes.length - encryptedOffset;
77
78
79 ByteSource all = wrap(bytes);
80 ByteSource salt = all.slice(prefixLength, saltBytes);
81 ByteSource source = all.slice(encryptedLength, encryptedOffset);
82
83
84 ByteSource secret = wrap(context.getPassword().getBytes(context.getCharset()));
85 Cipher cipher = buildCipher(context, DECRYPT, secret, salt);
86 try {
87
88 ByteArrayOutputStream out = new ByteArrayOutputStream();
89 cipheredCopy(source, out, cipher);
90 byte[] decrypted = out.toByteArray();
91 return new String(decrypted, context.getCharset());
92 } catch (IOException e) {
93 throw illegalState(e);
94 }
95 }
96
97 public OpenSSLContext getContext() {
98 return context;
99 }
100
101 private static ByteSource buildSalt(OpenSSLSaltContext context) {
102 byte[] bytes = new byte[context.getBytes()];
103 Random random = (context.isSecure()) ? new SecureRandom() : new Random();
104 random.nextBytes(bytes);
105 return wrap(bytes);
106 }
107
108 private static Cipher buildCipher(OpenSSLContext context, CipherMode mode, ByteSource data, ByteSource salt) {
109 try {
110 Cipher cipher = Cipher.getInstance(context.getTransformation());
111 int initVectorLength = cipher.getBlockSize();
112 MessageDigest md = MessageDigest.getInstance(context.getDigest());
113 int keyLength = context.getKeyBits() / Byte.SIZE;
114 int keyIndex = 0;
115 int initVectorIndex = 0;
116 byte[] key = new byte[keyLength];
117 byte[] initVector = new byte[initVectorLength];
118 byte[] digestBuffer = null;
119 int i = 0;
120 int addDigest = 0;
121 for (;;) {
122 md.reset();
123 if (addDigest++ > 0) {
124 md.update(digestBuffer);
125 }
126 md.update(data.read());
127 if (salt != null) {
128 md.update(salt.read());
129 }
130 digestBuffer = md.digest();
131 for (i = 1; i < context.getIterations(); i++) {
132 md.reset();
133 md.update(digestBuffer);
134 digestBuffer = md.digest();
135 }
136 i = 0;
137 if (keyLength > 0) {
138 for (;;) {
139 if (keyLength == 0) {
140 break;
141 }
142 if (i == digestBuffer.length) {
143 break;
144 }
145 key[keyIndex++] = digestBuffer[i];
146 keyLength--;
147 i++;
148 }
149 }
150 if (initVectorLength > 0 && i != digestBuffer.length) {
151 for (;;) {
152 if (initVectorLength == 0) {
153 break;
154 }
155 if (i == digestBuffer.length) {
156 break;
157 }
158 initVector[initVectorIndex++] = digestBuffer[i];
159 initVectorLength--;
160 i++;
161 }
162 }
163 if (keyLength == 0 && initVectorLength == 0) {
164 break;
165 }
166 }
167 for (i = 0; i < digestBuffer.length; i++) {
168 digestBuffer[i] = 0;
169 }
170
171 Key initKey = new SecretKeySpec(key, context.getEncryption());
172 AlgorithmParameterSpec initParams = new IvParameterSpec(initVector);
173 cipher.init(mode.getValue(), initKey, initParams);
174 return cipher;
175 } catch (Exception e) {
176 throw illegalState(e);
177 }
178 }
179
180 }