edu.wisc.doit.tcrypt.BouncyCastleTokenDecrypter.java Source code

Java tutorial

Introduction

Here is the source code for edu.wisc.doit.tcrypt.BouncyCastleTokenDecrypter.java

Source

/**
 * Copyright 2012, Board of Regents of the University of
 * Wisconsin System. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Board of Regents of the University of Wisconsin
 * System 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 edu.wisc.doit.tcrypt;

import java.io.IOException;
import java.io.Reader;
import java.security.KeyPair;
import java.util.Arrays;
import java.util.regex.Matcher;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.GeneralDigest;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;

/**
 * @author Eric Dalquist
 */
public class BouncyCastleTokenDecrypter extends AbstractPublicKeyDecrypter implements TokenDecrypter {
    public BouncyCastleTokenDecrypter(AsymmetricKeyParameter publicKeyParam,
            AsymmetricKeyParameter privateKeyParam) {
        super(publicKeyParam, privateKeyParam);
    }

    public BouncyCastleTokenDecrypter(KeyPair keyPair) throws IOException {
        super(keyPair);
    }

    public BouncyCastleTokenDecrypter(PEMKeyPair keyPair) throws IOException {
        super(keyPair);
    }

    /**
     * Create a token encrypter and decrypter using the specified {@link Reader}, note the
     * caller is responsible for closing the Reader.
     * 
     * @param privateKeyReader Reader to load the {@link KeyPair} from
     */
    @SuppressWarnings("resource")
    public BouncyCastleTokenDecrypter(Reader privateKeyReader) throws IOException {
        this((PEMKeyPair) new PEMParser(privateKeyReader).readObject());
    }

    @Override
    public boolean isEncryptedToken(String ciphertext) {
        return TOKEN_PATTERN.matcher(ciphertext).matches();
    }

    @Override
    public String decrypt(String ciphertext) throws InvalidCipherTextException {
        final Matcher tokenMatcher = TOKEN_PATTERN.matcher(ciphertext);
        if (tokenMatcher.matches()) {
            ciphertext = tokenMatcher.group(1);
        } else {
            final Matcher base64Matcher = BASE64_PATTERN.matcher(ciphertext);
            if (!base64Matcher.matches()) {
                throw new IllegalArgumentException("Specified ciphertext is not valid");
            }
        }

        //Decode the cipher text
        final byte[] encryptedTokenWithHash = Base64.decodeBase64(ciphertext);

        final AsymmetricBlockCipher e = getDecryptCipher();
        final byte[] tokenWithHashBytes = e.processBlock(encryptedTokenWithHash, 0, encryptedTokenWithHash.length);

        //Split the decrypted text into the password and the hash
        final String tokenWithHash = new String(tokenWithHashBytes, BouncyCastleTokenEncrypter.CHARSET);
        final int seperatorIndex = tokenWithHash.lastIndexOf(BouncyCastleTokenEncrypter.SEPARATOR);
        if (seperatorIndex < 0) {
            throw new IllegalArgumentException(
                    "token/hash string doesn't contain seperator: " + BouncyCastleTokenEncrypter.SEPARATOR);
        }
        final byte[] passwordBytes = tokenWithHash.substring(0, seperatorIndex)
                .getBytes(BouncyCastleTokenEncrypter.CHARSET);
        final byte[] passwordHashBytes = Base64.decodeBase64(
                tokenWithHash.substring(seperatorIndex + 1).getBytes(BouncyCastleTokenEncrypter.CHARSET));

        //Generate hash of the decrypted password
        final GeneralDigest digest = this.createDigester();
        digest.update(passwordBytes, 0, passwordBytes.length);
        final byte[] expectedHashBytes = new byte[digest.getDigestSize()];
        digest.doFinal(expectedHashBytes, 0);

        //Verify the generated hash against the decrypted hash
        if (!Arrays.equals(expectedHashBytes, passwordHashBytes)) {
            throw new IllegalArgumentException("Hash of the decrypted token does not match the decrypted hash");
        }

        return new String(passwordBytes, BouncyCastleTokenEncrypter.CHARSET);
    }

    protected GeneralDigest createDigester() {
        return new MD5Digest();
    }
}