com.acciente.oacc.encryptor.jasypt.PasswordEncoderDecoder.java Source code

Java tutorial

Introduction

Here is the source code for com.acciente.oacc.encryptor.jasypt.PasswordEncoderDecoder.java

Source

/*
 * Copyright 2009-2018, Acciente LLC
 *
 * Acciente LLC licenses this file to you under the
 * Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
 * OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 */

package com.acciente.oacc.encryptor.jasypt;

import org.jasypt.contrib.org.apache.commons.codec_1_3.binary.Base64;

import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;

class PasswordEncoderDecoder {
    private static final String MARKER = JasyptPasswordEncryptor.NAME + ":";
    private static final String PARAM_DELIMITER = "$";
    private static final String QUOTED_PARAM_DELIMITER = Pattern.quote(PARAM_DELIMITER);

    private static final int DECODED_PASSWORD_ARRAY_ALGORITHM = 0;
    private static final int DECODED_PASSWORD_ARRAY_ITERATIONS = 1;
    private static final int DECODED_PASSWORD_ARRAY_SALT_SIZE_BYTES = 2;
    private static final int DECODED_PASSWORD_ARRAY_DIGEST = 3;
    private static final int DECODED_PASSWORD_ARRAY_COUNT = 4;

    private static final Base64 base64 = new Base64();

    /**
     * Encodes an identifying header, the parameters used to generate the Jasypt digest, and the Jasypt digest into a
     * single "self-contained" password that contains enough information for future comparisons.
     *
     * @param algorithm     the algorithm used to generate the digest.
     * @param iterations    the number of iterations used to generate the digest.
     * @param saltSizeBytes the salt size bytes used to generate the digest.
     * @param digest        the digest itself.
     * @return a fully-encoded password ready for persistent storage.
     */
    String encode(String algorithm, int iterations, int saltSizeBytes, byte[] digest) {
        // setup array of values to encode -- to help ensure same param sequence in encode and decode logic
        final Object[] decodedPasswordArray = new Object[DECODED_PASSWORD_ARRAY_COUNT];
        decodedPasswordArray[DECODED_PASSWORD_ARRAY_ALGORITHM] = algorithm;
        decodedPasswordArray[DECODED_PASSWORD_ARRAY_ITERATIONS] = iterations;
        decodedPasswordArray[DECODED_PASSWORD_ARRAY_SALT_SIZE_BYTES] = saltSizeBytes;
        decodedPasswordArray[DECODED_PASSWORD_ARRAY_DIGEST] = new String(base64.encode(digest),
                StandardCharsets.US_ASCII);

        final StringBuilder encodedPassword = new StringBuilder(128).append(MARKER);
        for (int i = 0; i < DECODED_PASSWORD_ARRAY_COUNT - 1; i++) {
            encodedPassword.append(decodedPasswordArray[i]);
            encodedPassword.append(PARAM_DELIMITER);
        }
        // encode last param without the delimiter
        encodedPassword.append(decodedPasswordArray[DECODED_PASSWORD_ARRAY_COUNT - 1]);

        return encodedPassword.toString();
    }

    /**
     * Decodes a previously encoded Jasypt password into its constituent parts (algorithm, iterations, salt size, digest).
     *
     * @param encodedPassword an encoded password that was previously returned by
     *                        {{@link #encode(String, int, int, byte[])}}.
     * @return an object containing the decoded parts of the password (algorithm, iterations, salt size, digest).
     */
    DecodedPassword decode(String encodedPassword) {
        if (encodedPassword.startsWith(MARKER)) {
            final String[] decodedPasswordArray = encodedPassword.substring(MARKER.length())
                    .split(QUOTED_PARAM_DELIMITER);

            if (decodedPasswordArray.length != DECODED_PASSWORD_ARRAY_COUNT) {
                throw new IllegalArgumentException(
                        "Unexpected format for Jasypt password: " + encodedPassword.substring(0, MARKER.length()));
            }

            final String algorithm;
            final int iterations;
            final int saltSizeBytes;
            final byte[] digest;

            algorithm = decodedPasswordArray[DECODED_PASSWORD_ARRAY_ALGORITHM];
            try {
                iterations = Integer.parseInt(decodedPasswordArray[DECODED_PASSWORD_ARRAY_ITERATIONS]);
                saltSizeBytes = Integer.parseInt(decodedPasswordArray[DECODED_PASSWORD_ARRAY_SALT_SIZE_BYTES]);
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException(
                        "Unexpected value in Jasypt password header for iterations and/or salt size: "
                                + encodedPassword);
            }
            digest = base64.decode(
                    decodedPasswordArray[DECODED_PASSWORD_ARRAY_DIGEST].getBytes(StandardCharsets.US_ASCII));

            return new DecodedPassword.Builder().algorithm(algorithm).iterations(iterations)
                    .saltSizeBytes(saltSizeBytes).digest(digest).build();
        } else {
            throw new IllegalArgumentException(
                    "Unexpected marker for Jasypt password: " + encodedPassword.substring(0, MARKER.length()));
        }
    }
}