blob: 610f69c2b23e5b05170205b15e2296f2cafd4562 [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;
margaretha49cb6882018-07-04 04:19:54 +020011import org.apache.logging.log4j.LogManager;
12import org.apache.logging.log4j.Logger;
Michael Hanldeb3c212015-10-16 23:02:24 +020013import org.mindrot.jbcrypt.BCrypt;
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";
margaretha35e1ca22023-11-16 22:00:01 +010021 private static Logger jlog = LogManager.getLogger(KustvaktEncryption.class);
Michael Hanld8aa6212016-06-03 12:48:43 +020022
margaretha6d61a552018-04-10 19:26:44 +020023 private final FullConfiguration config;
Michael Hanldeb3c212015-10-16 23:02:24 +020024
margaretha6d61a552018-04-10 19:26:44 +020025 public KustvaktEncryption (FullConfiguration config) {
Michael Hanldeb3c212015-10-16 23:02:24 +020026 jlog.info("initializing KorAPEncryption implementation");
Michael Hanldeb3c212015-10-16 23:02:24 +020027 this.config = config;
28 }
29
Michael Hanl8abaf9e2016-05-23 16:46:35 +020030 public static boolean matchTokenByteCode (Object param) {
Michael Hanldeb3c212015-10-16 23:02:24 +020031 if (!(param instanceof String))
32 return false;
33 String token = (String) param;
34 byte[] bytes = token.getBytes();
35 return 64 == bytes.length;
36 }
37
Michael Hanl8abaf9e2016-05-23 16:46:35 +020038 private String encodeBase (byte[] bytes) throws EncoderException {
Michael Hanldeb3c212015-10-16 23:02:24 +020039 return Base64.encodeBase64String(bytes);
40 }
41
42 @Override
Michael Hanl8abaf9e2016-05-23 16:46:35 +020043 public String encodeBase () {
Michael Hanldeb3c212015-10-16 23:02:24 +020044 try {
45 return encodeBase(this.createSecureRandom(24));
Michael Hanl8abaf9e2016-05-23 16:46:35 +020046 }
47 catch (EncoderException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +020048 return "";
49 }
50 }
51
Michael Hanlcb2d3f92016-06-02 17:34:06 +020052 public String secureHash (String input) {
53 return secureHash(input, "");
Michael Hanldeb3c212015-10-16 23:02:24 +020054 }
55
56 @Override
Michael Hanlcb2d3f92016-06-02 17:34:06 +020057 public String secureHash (String input, String salt) {
Michael Hanldeb3c212015-10-16 23:02:24 +020058 String hashString = "";
margaretha33fa3d92018-07-26 13:50:17 +020059 switch (config.getSecureHashAlgorithm()) {
Michael Hanldeb3c212015-10-16 23:02:24 +020060 case ESAPICYPHER:
Michael Hanldeb3c212015-10-16 23:02:24 +020061 break;
62 case SIMPLE:
63 try {
64 MessageDigest md = MessageDigest.getInstance("SHA-256");
65 md.update(input.getBytes("UTF-8"));
66 byte[] digest = md.digest();
67
68 for (byte b : digest)
69 hashString += String.format("%02x", b);
Michael Hanl8abaf9e2016-05-23 16:46:35 +020070 }
margaretha35e1ca22023-11-16 22:00:01 +010071 catch (UnsupportedEncodingException
72 | NoSuchAlgorithmException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +020073 e.printStackTrace();
74 }
75 break;
76 case BCRYPT:
77 hashString = bcryptHash(input, salt);
78 break;
79 default:
margaretha35e1ca22023-11-16 22:00:01 +010080 jlog.warn("Invalid value: " + config.getSecureHashAlgorithm());
Michael Hanldeb3c212015-10-16 23:02:24 +020081 break;
82 }
83 return hashString;
84 }
85
Michael Hanl8abaf9e2016-05-23 16:46:35 +020086 public String hash (String input) {
Michael Hanldeb3c212015-10-16 23:02:24 +020087 String hashString = "";
88 MessageDigest md;
89 try {
Michael Hanlc0ed00f2016-06-23 14:33:10 +020090 md = MessageDigest.getInstance(ALGORITHM);
Michael Hanldeb3c212015-10-16 23:02:24 +020091 md.update(input.getBytes("UTF-8"));
Michael Hanl8abaf9e2016-05-23 16:46:35 +020092 }
93 catch (NoSuchAlgorithmException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +020094 return "";
Michael Hanl8abaf9e2016-05-23 16:46:35 +020095 }
96 catch (UnsupportedEncodingException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +020097 return "";
98 }
99
100 byte[] digest = md.digest();
101
102 for (byte b : digest) {
103 hashString += String.format("%02x", b);
104 }
105 return hashString;
106 }
107
108 /**
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200109 * // some sort of algorithm to create token and isSystem
110 * regularly the integrity
Michael Hanldeb3c212015-10-16 23:02:24 +0200111 * // of the token
112 * public String createAuthToken() {
113 * final byte[] rNumber = SecureRGenerator
114 * .getNextSecureRandom(SecureRGenerator.TOKEN_RANDOM_SIZE);
115 * String hash;
116 * try {
117 * hash = produceSimpleHash(SecureRGenerator.toHex(rNumber));
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200118 * } catch (NoSuchAlgorithmException |
119 * UnsupportedEncodingException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200120 * return "";
121 * }
122 * return hash;
123 * }
124 */
125
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200126 private byte[] createSecureRandom (int size) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200127 return SecureRGenerator.getNextSecureRandom(size);
128 }
129
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200130 /**
131 * does this need to be equal for every iteration?!
margaretha35e1ca22023-11-16 22:00:01 +0100132 *
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200133 * @param hash
134 * @param obj
135 * @return
136 */
Michael Hanldeb3c212015-10-16 23:02:24 +0200137 @Override
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200138 public String createToken (boolean hash, Object ... obj) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200139 StringBuffer b = new StringBuffer();
140 try {
141 for (Object o : obj) {
142 b.append(" | ");
143 b.append(o);
144 }
145 if (hash)
146 return encodeBase(hash(b.toString().trim()).getBytes());
147 else
148 return encodeBase(b.toString().trim().getBytes());
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200149 }
150 catch (EncoderException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200151 return "";
152 }
153
154 }
155
156 @Override
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200157 public String createToken () {
margaretha6d61a552018-04-10 19:26:44 +0200158 return RandomStringUtils.randomAlphanumeric(64);
Michael Hanldeb3c212015-10-16 23:02:24 +0200159
margaretha35e1ca22023-11-16 22:00:01 +0100160 // EM: code from MH
161 // String encoded;
162 // String v = RandomStringUtils.randomAlphanumeric(64);
163 // encoded = hash(v);
164 // jlog.trace("creating new token {}", encoded);
165 // return encoded;
166 }
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200167
Michael Hanldeb3c212015-10-16 23:02:24 +0200168 @Override
Michael Hanl7ab5d102016-06-03 13:16:09 +0200169 public String createRandomNumber (Object ... obj) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200170 final byte[] rNumber = SecureRGenerator
margaretha03b82862018-07-12 20:09:26 +0200171 .getNextSecureRandom(SecureRGenerator.ID_RANDOM_SIZE);
Michael Hanlc0ed00f2016-06-23 14:33:10 +0200172 if (obj.length == 0) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200173 obj = new Object[1];
174 obj[0] = rNumber;
175 }
176 return createToken(false, obj);
177 }
178
179 @Override
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200180 public boolean checkHash (String plain, String hash, String salt) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200181 String pw = "";
margaretha33fa3d92018-07-26 13:50:17 +0200182 switch (config.getSecureHashAlgorithm()) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200183 case ESAPICYPHER:
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200184 pw = secureHash(plain, salt);
Michael Hanldeb3c212015-10-16 23:02:24 +0200185 break;
186 case BCRYPT:
187 try {
188 return BCrypt.checkpw(plain, hash);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200189 }
190 catch (IllegalArgumentException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200191 return false;
192 }
193 case SIMPLE:
194 pw = hash(plain);
195 break;
196 }
197 return pw.equals(hash);
198 }
199
200 @Override
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200201 public boolean checkHash (String plain, String hash) {
margaretha33fa3d92018-07-26 13:50:17 +0200202 switch (config.getSecureHashAlgorithm()) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200203 case ESAPICYPHER:
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200204 return secureHash(plain).equals(hash);
Michael Hanldeb3c212015-10-16 23:02:24 +0200205 case BCRYPT:
206 try {
207 return BCrypt.checkpw(plain, hash);
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200208 }
209 catch (IllegalArgumentException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200210 return false;
211 }
212 case SIMPLE:
213 return hash(plain).equals(hash);
214 }
215 return false;
216 }
217
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200218 private String bcryptHash (String text, String salt) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200219 if (salt == null || salt.isEmpty())
220 salt = BCrypt.gensalt(config.getLoadFactor());
221 return BCrypt.hashpw(text, salt);
222 }
223
224 @Override
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200225 public String toString () {
Michael Hanldeb3c212015-10-16 23:02:24 +0200226 return this.getClass().getCanonicalName();
227 }
228
229 public static class SecureRGenerator {
230 private static final String SHA1_PRNG = "SHA1PRNG";
231 protected static final int DEFAULT_RANDOM_SIZE = 128;
232 protected static final int TOKEN_RANDOM_SIZE = 128;
margaretha03b82862018-07-12 20:09:26 +0200233 protected static final int ID_RANDOM_SIZE = 128;
234 protected static final int CORPUS_RANDOM_SIZE = 64;
Michael Hanldeb3c212015-10-16 23:02:24 +0200235 private static final char[] HEX_DIGIT = { '0', '1', '2', '3', '4', '5',
margaretha35e1ca22023-11-16 22:00:01 +0100236 '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'z', 'x', 'h',
237 'q', 'w' };
Michael Hanldeb3c212015-10-16 23:02:24 +0200238 private static final SecureRandom sRandom__;
239
240 static {
241 try {
242 sRandom__ = SecureRandom.getInstance("SHA1PRNG");
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200243 }
244 catch (NoSuchAlgorithmException e) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200245 throw new Error(e);
246 }
247 }
248
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200249 public static byte[] getNextSecureRandom (int bits) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200250 if (bits % 8 != 0) {
251 throw new IllegalArgumentException(
252 "Size is not divisible by 8!");
253 }
254
255 byte[] bytes = new byte[bits / 8];
256
257 sRandom__.nextBytes(bytes);
258
259 return bytes;
260 }
261
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200262 public static String toHex (byte[] bytes) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200263 if (bytes == null) {
264 return null;
265 }
266
267 StringBuilder buffer = new StringBuilder(bytes.length * 2);
268 for (byte thisByte : bytes) {
269 buffer.append(byteToHex(thisByte));
270 }
271
272 return buffer.toString();
273 }
274
Michael Hanl8abaf9e2016-05-23 16:46:35 +0200275 private static String byteToHex (byte b) {
Michael Hanldeb3c212015-10-16 23:02:24 +0200276 char[] array = { HEX_DIGIT[(b >> 4 & 0xF)], HEX_DIGIT[(b & 0xF)] };
277 return new String(array);
278 }
279 }
280
281}