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 }