Coverage Summary for Class: PasswordEncoderDecoder (com.acciente.oacc.encryptor.jasypt)
Class | Class, % | Method, % | Line, % |
---|---|---|---|
PasswordEncoderDecoder | 100% (1/ 1) | 100% (4/ 4) | 92.6% (25/ 27) |
1 /*
2 * Copyright 2009-2018, Acciente LLC
3 *
4 * Acciente LLC licenses this file to you under the
5 * Apache License, Version 2.0 (the "License"); you
6 * may not use this file except in compliance with the
7 * License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
14 * OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License.
17 */
18
19 package com.acciente.oacc.encryptor.jasypt;
20
21 import org.jasypt.contrib.org.apache.commons.codec_1_3.binary.Base64;
22
23 import java.nio.charset.StandardCharsets;
24 import java.util.regex.Pattern;
25
26 class PasswordEncoderDecoder {
27 private static final String MARKER = JasyptPasswordEncryptor.NAME + ":";
28 private static final String PARAM_DELIMITER = "$";
29 private static final String QUOTED_PARAM_DELIMITER = Pattern.quote(PARAM_DELIMITER);
30
31 private static final int DECODED_PASSWORD_ARRAY_ALGORITHM = 0;
32 private static final int DECODED_PASSWORD_ARRAY_ITERATIONS = 1;
33 private static final int DECODED_PASSWORD_ARRAY_SALT_SIZE_BYTES = 2;
34 private static final int DECODED_PASSWORD_ARRAY_DIGEST = 3;
35 private static final int DECODED_PASSWORD_ARRAY_COUNT = 4;
36
37 private static final Base64 base64 = new Base64();
38
39 /**
40 * Encodes an identifying header, the parameters used to generate the Jasypt digest, and the Jasypt digest into a
41 * single "self-contained" password that contains enough information for future comparisons.
42 *
43 * @param algorithm the algorithm used to generate the digest.
44 * @param iterations the number of iterations used to generate the digest.
45 * @param saltSizeBytes the salt size bytes used to generate the digest.
46 * @param digest the digest itself.
47 * @return a fully-encoded password ready for persistent storage.
48 */
49 String encode(String algorithm,
50 int iterations,
51 int saltSizeBytes,
52 byte[] digest) {
53 // setup array of values to encode -- to help ensure same param sequence in encode and decode logic
54 final Object[] decodedPasswordArray = new Object[DECODED_PASSWORD_ARRAY_COUNT];
55 decodedPasswordArray[DECODED_PASSWORD_ARRAY_ALGORITHM] = algorithm;
56 decodedPasswordArray[DECODED_PASSWORD_ARRAY_ITERATIONS] = iterations;
57 decodedPasswordArray[DECODED_PASSWORD_ARRAY_SALT_SIZE_BYTES] = saltSizeBytes;
58 decodedPasswordArray[DECODED_PASSWORD_ARRAY_DIGEST] = new String(base64.encode(digest), StandardCharsets.US_ASCII);
59
60 final StringBuilder encodedPassword = new StringBuilder(128).append(MARKER);
61 for (int i = 0; i < DECODED_PASSWORD_ARRAY_COUNT - 1; i++) {
62 encodedPassword.append(decodedPasswordArray[i]);
63 encodedPassword.append(PARAM_DELIMITER);
64 }
65 // encode last param without the delimiter
66 encodedPassword.append(decodedPasswordArray[DECODED_PASSWORD_ARRAY_COUNT - 1]);
67
68 return encodedPassword.toString();
69 }
70
71 /**
72 * Decodes a previously encoded Jasypt password into its constituent parts (algorithm, iterations, salt size, digest).
73 *
74 * @param encodedPassword an encoded password that was previously returned by
75 * {{@link #encode(String, int, int, byte[])}}.
76 * @return an object containing the decoded parts of the password (algorithm, iterations, salt size, digest).
77 */
78 DecodedPassword decode(String encodedPassword) {
79 if (encodedPassword.startsWith(MARKER)) {
80 final String[] decodedPasswordArray = encodedPassword.substring(MARKER.length()).split(QUOTED_PARAM_DELIMITER);
81
82 if (decodedPasswordArray.length != DECODED_PASSWORD_ARRAY_COUNT) {
83 throw new IllegalArgumentException("Unexpected format for Jasypt password: " +
84 encodedPassword.substring(0, MARKER.length()));
85 }
86
87 final String algorithm;
88 final int iterations;
89 final int saltSizeBytes;
90 final byte[] digest;
91
92 algorithm = decodedPasswordArray[DECODED_PASSWORD_ARRAY_ALGORITHM];
93 try {
94 iterations = Integer.parseInt(decodedPasswordArray[DECODED_PASSWORD_ARRAY_ITERATIONS]);
95 saltSizeBytes = Integer.parseInt(decodedPasswordArray[DECODED_PASSWORD_ARRAY_SALT_SIZE_BYTES]);
96 }
97 catch (NumberFormatException e) {
98 throw new IllegalArgumentException("Unexpected value in Jasypt password header for iterations and/or salt size: " + encodedPassword);
99 }
100 digest = base64.decode(decodedPasswordArray[DECODED_PASSWORD_ARRAY_DIGEST].getBytes(StandardCharsets.US_ASCII));
101
102 return new DecodedPassword.Builder()
103 .algorithm(algorithm)
104 .iterations(iterations)
105 .saltSizeBytes(saltSizeBytes)
106 .digest(digest)
107 .build();
108 }
109 else {
110 throw new IllegalArgumentException("Unexpected marker for Jasypt password: " +
111 encodedPassword.substring(0, MARKER.length()));
112 }
113 }
114 }