blob: b73a5240a7344cebdec998eb2e09a517293fba82 [file] [log] [blame]
margaretha6d61a552018-04-10 19:26:44 +02001package de.ids_mannheim.korap.encryption;
Michael Hanldeb3c212015-10-16 23:02:24 +02002
margaretha6d61a552018-04-10 19:26:44 +02003import java.io.UnsupportedEncodingException;
4import java.security.MessageDigest;
5import java.security.NoSuchAlgorithmException;
6import java.security.SecureRandom;
7
Michael Hanldeb3c212015-10-16 23:02:24 +02008import org.apache.commons.codec.EncoderException;
9import org.apache.commons.codec.binary.Base64;
Michael Hanlc0ed00f2016-06-23 14:33:10 +020010import org.apache.commons.lang.RandomStringUtils;
Michael Hanldeb3c212015-10-16 23:02:24 +020011import org.mindrot.jbcrypt.BCrypt;
Michael Hanldeb3c212015-10-16 23:02:24 +020012import org.slf4j.Logger;
Michael Hanlac113e52016-01-19 15:49:20 +010013import org.slf4j.LoggerFactory;
Michael Hanldeb3c212015-10-16 23:02:24 +020014
margaretha6d61a552018-04-10 19:26:44 +020015import de.ids_mannheim.korap.config.FullConfiguration;
16import de.ids_mannheim.korap.interfaces.EncryptionIface;
Michael Hanldeb3c212015-10-16 23:02:24 +020017
18public class KustvaktEncryption implements EncryptionIface {
19
20 private static final String ALGORITHM = "SHA-256";
Michael Hanlac113e52016-01-19 15:49:20 +010021 private static Logger jlog = LoggerFactory
Michael Hanldeb3c212015-10-16 23:02:24 +020022 .getLogger(KustvaktEncryption.class);
Michael Hanld8aa6212016-06-03 12:48:43 +020023
margaretha6d61a552018-04-10 19:26:44 +020024 private final FullConfiguration config;
Michael Hanldeb3c212015-10-16 23:02:24 +020025
Michael Hanl8abaf9e2016-05-23 16:46:35 +020026
margaretha6d61a552018-04-10 19:26:44 +020027 public KustvaktEncryption (FullConfiguration config) {
Michael Hanldeb3c212015-10-16 23:02:24 +020028 jlog.info("initializing KorAPEncryption implementation");
Michael Hanldeb3c212015-10-16 23:02:24 +020029 this.config = config;
30 }
31
Michael Hanl8abaf9e2016-05-23 16:46:35 +020032
33 public static boolean matchTokenByteCode (Object param) {
Michael Hanldeb3c212015-10-16 23:02:24 +020034 if (!(param instanceof String))
35 return false;
36 String token = (String) param;
37 byte[] bytes = token.getBytes();
38 return 64 == bytes.length;
39 }
40
Michael Hanl8abaf9e2016-05-23 16:46:35 +020041
42 private String encodeBase (byte[] bytes) throws EncoderException {
Michael Hanldeb3c212015-10-16 23:02:24 +020043 return Base64.encodeBase64String(bytes);
44 }
45
Michael Hanl8abaf9e2016-05-23 16:46:35 +020046
Michael Hanldeb3c212015-10-16 23:02:24 +020047 @Override
Michael Hanl8abaf9e2016-05-23 16:46:35 +020048 public String encodeBase () {
Michael Hanldeb3c212015-10-16 23:02:24 +020049 try {
50 return encodeBase(this.createSecureRandom(24));
Michael Hanl8abaf9e2016-05-23 16:46:35 +020051 }
52 catch (EncoderException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +020053 return "";
54 }
55 }
56
Michael Hanl8abaf9e2016-05-23 16:46:35 +020057
Michael Hanlcb2d3f92016-06-02 17:34:06 +020058 public String secureHash (String input) {
59 return secureHash(input, "");
Michael Hanldeb3c212015-10-16 23:02:24 +020060 }
61
Michael Hanl8abaf9e2016-05-23 16:46:35 +020062
Michael Hanldeb3c212015-10-16 23:02:24 +020063 @Override
Michael Hanlcb2d3f92016-06-02 17:34:06 +020064 public String secureHash (String input, String salt) {
Michael Hanldeb3c212015-10-16 23:02:24 +020065 String hashString = "";
66 switch (config.getEncryption()) {
67 case ESAPICYPHER:
Michael Hanldeb3c212015-10-16 23:02:24 +020068 break;
69 case SIMPLE:
70 try {
71 MessageDigest md = MessageDigest.getInstance("SHA-256");
72 md.update(input.getBytes("UTF-8"));
73 byte[] digest = md.digest();
74
75 for (byte b : digest)
76 hashString += String.format("%02x", b);
Michael Hanl8abaf9e2016-05-23 16:46:35 +020077 }
78 catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +020079 e.printStackTrace();
80 }
81 break;
82 case BCRYPT:
83 hashString = bcryptHash(input, salt);
84 break;
85 default:
86 jlog.warn("Invalid value: {}", config.getEncryption());
87 break;
88 }
89 return hashString;
90 }
91
Michael Hanl8abaf9e2016-05-23 16:46:35 +020092
Michael Hanlcb2d3f92016-06-02 17:34:06 +020093
Michael Hanl8abaf9e2016-05-23 16:46:35 +020094 public String hash (String input) {
Michael Hanldeb3c212015-10-16 23:02:24 +020095 String hashString = "";
96 MessageDigest md;
97 try {
Michael Hanlc0ed00f2016-06-23 14:33:10 +020098 md = MessageDigest.getInstance(ALGORITHM);
Michael Hanldeb3c212015-10-16 23:02:24 +020099 md.update(input.getBytes("UTF-8"));
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200100 }
101 catch (NoSuchAlgorithmException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200102 return "";
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200103 }
104 catch (UnsupportedEncodingException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200105 return "";
106 }
107
108 byte[] digest = md.digest();
109
110 for (byte b : digest) {
111 hashString += String.format("%02x", b);
112 }
113 return hashString;
114 }
115
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200116
Michael Hanldeb3c212015-10-16 23:02:24 +0200117 /**
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200118 * // some sort of algorithm to create token and isSystem
119 * regularly the integrity
Michael Hanldeb3c212015-10-16 23:02:24 +0200120 * // of the token
121 * public String createAuthToken() {
122 * final byte[] rNumber = SecureRGenerator
123 * .getNextSecureRandom(SecureRGenerator.TOKEN_RANDOM_SIZE);
124 * String hash;
125 * try {
126 * hash = produceSimpleHash(SecureRGenerator.toHex(rNumber));
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200127 * } catch (NoSuchAlgorithmException |
128 * UnsupportedEncodingException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200129 * return "";
130 * }
131 * return hash;
132 * }
133 */
134
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200135 private byte[] createSecureRandom (int size) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200136 return SecureRGenerator.getNextSecureRandom(size);
137 }
138
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200139
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200140 /**
141 * does this need to be equal for every iteration?!
142 * @param hash
143 * @param obj
144 * @return
145 */
Michael Hanldeb3c212015-10-16 23:02:24 +0200146 @Override
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200147 public String createToken (boolean hash, Object ... obj) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200148 StringBuffer b = new StringBuffer();
149 try {
150 for (Object o : obj) {
151 b.append(" | ");
152 b.append(o);
153 }
154 if (hash)
155 return encodeBase(hash(b.toString().trim()).getBytes());
156 else
157 return encodeBase(b.toString().trim().getBytes());
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200158 }
159 catch (EncoderException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200160 return "";
161 }
162
163 }
164
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200165
Michael Hanldeb3c212015-10-16 23:02:24 +0200166 @Override
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200167 public String createToken () {
margaretha6d61a552018-04-10 19:26:44 +0200168 return RandomStringUtils.randomAlphanumeric(64);
169
170 // EM: code from MH
171// String encoded;
172// String v = RandomStringUtils.randomAlphanumeric(64);
173// encoded = hash(v);
174// jlog.trace("creating new token {}", encoded);
175// return encoded;
Michael Hanldeb3c212015-10-16 23:02:24 +0200176 }
177
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200178
Michael Hanldeb3c212015-10-16 23:02:24 +0200179 @Override
Michael Hanl7ab5d102016-06-03 13:16:09 +0200180 public String createRandomNumber (Object ... obj) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200181 final byte[] rNumber = SecureRGenerator
182 .getNextSecureRandom(SecureRGenerator.CORPUS_RANDOM_SIZE);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200183 if (obj.length == 0) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200184 obj = new Object[1];
185 obj[0] = rNumber;
186 }
187 return createToken(false, obj);
188 }
189
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200190
Michael Hanldeb3c212015-10-16 23:02:24 +0200191 @Override
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200192 public boolean checkHash (String plain, String hash, String salt) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200193 String pw = "";
194 switch (config.getEncryption()) {
195 case ESAPICYPHER:
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200196 pw = secureHash(plain, salt);
Michael Hanldeb3c212015-10-16 23:02:24 +0200197 break;
198 case BCRYPT:
199 try {
200 return BCrypt.checkpw(plain, hash);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200201 }
202 catch (IllegalArgumentException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200203 return false;
204 }
205 case SIMPLE:
206 pw = hash(plain);
207 break;
208 }
209 return pw.equals(hash);
210 }
211
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200212
Michael Hanldeb3c212015-10-16 23:02:24 +0200213 @Override
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200214 public boolean checkHash (String plain, String hash) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200215 switch (config.getEncryption()) {
216 case ESAPICYPHER:
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200217 return secureHash(plain).equals(hash);
Michael Hanldeb3c212015-10-16 23:02:24 +0200218 case BCRYPT:
219 try {
220 return BCrypt.checkpw(plain, hash);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200221 }
222 catch (IllegalArgumentException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200223 return false;
224 }
225 case SIMPLE:
226 return hash(plain).equals(hash);
227 }
228 return false;
229 }
230
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200231
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200232 private String bcryptHash (String text, String salt) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200233 if (salt == null || salt.isEmpty())
234 salt = BCrypt.gensalt(config.getLoadFactor());
235 return BCrypt.hashpw(text, salt);
236 }
237
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200238
Michael Hanldeb3c212015-10-16 23:02:24 +0200239 @Override
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200240 public String toString () {
Michael Hanldeb3c212015-10-16 23:02:24 +0200241 return this.getClass().getCanonicalName();
242 }
243
244 public static class SecureRGenerator {
245 private static final String SHA1_PRNG = "SHA1PRNG";
246 protected static final int DEFAULT_RANDOM_SIZE = 128;
247 protected static final int TOKEN_RANDOM_SIZE = 128;
248 protected static final int USERID_RANDOM_SIZE = 64;
249 protected static final int CORPUS_RANDOM_SIZE = 48;
250 private static final char[] HEX_DIGIT = { '0', '1', '2', '3', '4', '5',
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200251 '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'z', 'x',
252 'h', 'q', 'w' };
Michael Hanldeb3c212015-10-16 23:02:24 +0200253 private static final SecureRandom sRandom__;
254
255 static {
256 try {
257 sRandom__ = SecureRandom.getInstance("SHA1PRNG");
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200258 }
259 catch (NoSuchAlgorithmException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200260 throw new Error(e);
261 }
262 }
263
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200264
265 public static byte[] getNextSecureRandom (int bits) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200266 if (bits % 8 != 0) {
267 throw new IllegalArgumentException(
268 "Size is not divisible by 8!");
269 }
270
271 byte[] bytes = new byte[bits / 8];
272
273 sRandom__.nextBytes(bytes);
274
275 return bytes;
276 }
277
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200278
279 public static String toHex (byte[] bytes) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200280 if (bytes == null) {
281 return null;
282 }
283
284 StringBuilder buffer = new StringBuilder(bytes.length * 2);
285 for (byte thisByte : bytes) {
286 buffer.append(byteToHex(thisByte));
287 }
288
289 return buffer.toString();
290 }
291
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200292
293 private static String byteToHex (byte b) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200294 char[] array = { HEX_DIGIT[(b >> 4 & 0xF)], HEX_DIGIT[(b & 0xF)] };
295 return new String(array);
296 }
297 }
298
299}