All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.org.jets3t.service.security.EncryptionUtil Maven / Gradle / Ivy

/*
 * jets3t : Java Extra-Tasty S3 Toolkit (for Amazon S3 online storage service)
 * This is a java.net project, see https://jets3t.dev.java.net/
 * 
 * Copyright 2006 James Murty
 * 
 * Licensed 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 org.jets3t.service.security;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jets3t.service.Constants;
import org.jets3t.service.Jets3tProperties;

/**
 * Utility class to handle encryption and decryption in the JetS3t suite.
 * 

* The encryption method used by JetS3t has changed after version 0.4.0, from standard triple DES * to the proper password-based encryption method PBEWithMD5AndDES. For backwards compatibility this * class retains the ability to decrypt items encrypted with the original 0.4.0 algorithm, however * these mechanisms are deprecated and could be removed from JetS3t at any time. *

*

* This class uses properties obtained through {@link Jets3tProperties}. For more information on * these properties please refer to * JetS3t Configuration *

* * @author James Murty */ public class EncryptionUtil { private static final Log log = LogFactory.getLog(EncryptionUtil.class); public static final String DEFAULT_VERSION = "2"; private String algorithm = null; private String version = null; private SecretKey key = null; private AlgorithmParameterSpec algParamSpec = null; int ITERATION_COUNT = 5000; byte[] salt = { (byte)0xA4, (byte)0x0B, (byte)0xC8, (byte)0x34, (byte)0xD6, (byte)0x95, (byte)0xF3, (byte)0x13 }; static { try { Class bouncyCastleProviderClass = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider"); if (bouncyCastleProviderClass != null) { Provider bouncyCastleProvider = (Provider) bouncyCastleProviderClass .getConstructor(new Class[] {}).newInstance(new Object[] {}); Security.addProvider(bouncyCastleProvider); } if (log.isDebugEnabled()) { log.debug("Loaded security provider BouncyCastleProvider"); } } catch (Exception e) { if (log.isDebugEnabled()) { log.debug("Unable to load security provider BouncyCastleProvider"); } } } /** * Constructs class configured with the provided password, and set up to use the encryption * method specified. * * @param encryptionKey * the password to use for encryption/decryption. * @param algorithm * the Java name of an encryption algorithm to use, eg PBEWithMD5AndDES * @param version * the version of encyption to use, for historic and future compatibility. * Unless using an historic version, this should always be * {@link #DEFAULT_VERSION} * * @throws InvalidKeyException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException * @throws InvalidKeySpecException */ public EncryptionUtil(String encryptionKey, String algorithm, String version) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException { this.algorithm = algorithm; this.version = version; if (log.isDebugEnabled()) { log.debug("Cryptographic properties: algorithm=" + this.algorithm + ", version=" + this.version); } if (!DEFAULT_VERSION.equals(version)) { throw new RuntimeException("Unrecognised crypto version setting: " + version); } PBEKeySpec keyspec = new PBEKeySpec(encryptionKey.toCharArray(), salt, ITERATION_COUNT, 32); SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm); key = skf.generateSecret(keyspec); algParamSpec = new PBEParameterSpec(salt, ITERATION_COUNT); } /** * Constructs class configured with the provided password, and set up to use the default encryption * algorith PBEWithMD5AndDES. * * @param encryptionKey * the password to use for encryption/decryption. * * @throws InvalidKeyException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException * @throws InvalidKeySpecException */ public EncryptionUtil(String encryptionKey) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException { this(encryptionKey, "PBEWithMD5AndDES", DEFAULT_VERSION); } /** * Constructs the class using the obsolete methodology from 0.4.0, which used the * DESede algorithm instead of a PBE version. * * @param encryptionKey * @param encryptionScheme * @param blockMode * @param paddingMode * @throws InvalidKeyException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException * @throws InvalidKeySpecException * @throws UnsupportedEncodingException * @throws * * @deprecated */ private EncryptionUtil(String encryptionKey, String encryptionScheme, String blockMode, String paddingMode) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, UnsupportedEncodingException { byte[] KEY_BASE_BYTES = new byte[] { (byte)0xC2, (byte)0xAB, (byte)0xE2, (byte)0x80, (byte)0xA0, (byte)0x0E, (byte)0xC2, (byte)0xBB, (byte)0xC3, (byte)0xA6, (byte)0xE2, (byte)0x80, (byte)0x94, (byte)0x72, (byte)0x3D, (byte)0xC3, (byte)0xB4, (byte)0x18, (byte)0x14, (byte)0x51, (byte)0xC5, (byte)0x92, (byte)0x20, (byte)0x79, (byte)0xE2, (byte)0x84, (byte)0xA2, (byte)0x53, (byte)0x34, (byte)0x43, (byte)0x2E, (byte)0x24, (byte)0x53, (byte)0xC3, (byte)0xBF, (byte)0xC3, (byte)0x92, (byte)0x74, (byte)0xCB, (byte)0x9A, (byte)0xC3, (byte)0xA0, (byte)0xC3, (byte)0xA1, (byte)0x49, (byte)0xC3, (byte)0x8C, (byte)0x5B, (byte)0xE2, (byte)0x88, (byte)0x86, (byte)0x4F, (byte)0xC3, (byte)0x8B, (byte)0xC3, (byte)0x96, (byte)0xC3, (byte)0x84, (byte)0x75, (byte)0x40, (byte)0xE2, (byte)0x80, (byte)0x9D, (byte)0x1C, (byte)0xC2, (byte)0xA9, (byte)0x64, (byte)0x46, (byte)0x16, (byte)0x54, (byte)0x17, (byte)0x03, (byte)0xC2, (byte)0xB4, (byte)0xC3, (byte)0xA4, (byte)0xE2, (byte)0x81, (byte)0x84, (byte)0xC3, (byte)0xA1, (byte)0x4E, (byte)0x68, (byte)0xC3, (byte)0x8B, (byte)0xC3, (byte)0xAA, (byte)0x76, (byte)0xC2, (byte)0xAB, (byte)0x1D, (byte)0xE2, (byte)0x80, (byte)0x94, (byte)0xC2, (byte)0xA3, (byte)0x6C, (byte)0xC3, (byte)0xBB, (byte)0x5E, (byte)0x75, (byte)0xE2, (byte)0x80, (byte)0x9D, (byte)0xC3, (byte)0x83, (byte)0x10, (byte)0xCF, (byte)0x80, (byte)0x2B, (byte)0x74, (byte)0xC5, (byte)0x93, (byte)0x3A, (byte)0xC3, (byte)0x80, (byte)0x4B, (byte)0x37, (byte)0x51, (byte)0xC2, (byte)0xA7, (byte)0xE2, (byte)0x88, (byte)0x9E, (byte)0x48, (byte)0x3E, (byte)0xE2, (byte)0x80, (byte)0x9E, (byte)0x3A, (byte)0x69, (byte)0xC3, (byte)0x8A, (byte)0x75, (byte)0x11, (byte)0xE2, (byte)0x80, (byte)0xB0, (byte)0xC3, (byte)0x94, (byte)0xC3, (byte)0xBC, (byte)0x51, (byte)0xC3, (byte)0x93, (byte)0x23, (byte)0xE2, (byte)0x80, (byte)0xBA, (byte)0xC2, (byte)0xA5, (byte)0x31, (byte)0xE2, (byte)0x80, (byte)0x94, (byte)0x7A, (byte)0x6A, (byte)0xC2, (byte)0xB5, (byte)0xE2, (byte)0x81, (byte)0x84, (byte)0xE2, (byte)0x80, (byte)0xB9, (byte)0x29, (byte)0x31, (byte)0x6F, (byte)0xE2, (byte)0x80, (byte)0xB0, (byte)0xC3, (byte)0xB7, (byte)0x4D, (byte)0xC3, (byte)0x98, (byte)0x35, (byte)0x44, (byte)0x46, (byte)0xC3, (byte)0xAD, (byte)0xC2, (byte)0xAB, (byte)0xC5, (byte)0xB8, (byte)0x2E, (byte)0x23, (byte)0x63, (byte)0x3B, (byte)0xC2, (byte)0xAF, (byte)0xC2, (byte)0xB7, (byte)0xEF, (byte)0xA3, (byte)0xBF, (byte)0xCB, (byte)0x86, (byte)0xC3, (byte)0x8C, (byte)0x42, (byte)0xCE, (byte)0xA9, (byte)0xE2, (byte)0x88, (byte)0x86, (byte)0x76, (byte)0xE2, (byte)0x84, (byte)0xA2 }; algorithm = "DESede/CBC/PKCS5Padding"; encryptionKey = encryptionKey + new String(KEY_BASE_BYTES, Constants.DEFAULT_ENCODING); int keyOffset = 0; byte spec[] = new byte[8]; for (int specOffset = 0; specOffset < spec.length; specOffset++) { keyOffset = (keyOffset + 7) % encryptionKey.length(); spec[specOffset] = encryptionKey.getBytes()[keyOffset]; } KeySpec keySpec = new DESedeKeySpec(encryptionKey.getBytes()); algParamSpec = new IvParameterSpec(spec); key = SecretKeyFactory.getInstance(encryptionScheme).generateSecret(keySpec); } /** * Creates an EncryptionUtil initialised using the original algorithm configuration as used * in JetS3t releases prior to version 0.5.0. This method is deprecated as the crypto * configuration used has changed, and this method is provided for backwards compatibility only. * This method could be removed at any time. * * @param encryptionKey * the key (password) to use when encrypting data. * @return * an EncryptionUtil object configured in a way compatible with the crypto mechanisms used in * the JetS3t release version 0.4.0. * * @throws InvalidKeyException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException * @throws InvalidKeySpecException * @throws UnsupportedEncodingException * * @deprecated as of JetS3t version 0.5.0 */ public static EncryptionUtil getObsoleteEncryptionUtil(String encryptionKey) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, UnsupportedEncodingException { return new EncryptionUtil(encryptionKey, "DESede", "CBC", "PKCS5Padding"); } protected Cipher initEncryptModeCipher() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.ENCRYPT_MODE, key, algParamSpec); return cipher; } protected Cipher initDecryptModeCipher() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.DECRYPT_MODE, key, algParamSpec); return cipher; } /** * Encrypts a UTF-8 string to byte data. * * @param data * data to encrypt. * @return * encrypted data. * * @throws IllegalStateException * @throws IllegalBlockSizeException * @throws BadPaddingException * @throws UnsupportedEncodingException * @throws InvalidKeySpecException * @throws InvalidKeyException * @throws InvalidAlgorithmParameterException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException */ public byte[] encrypt(String data) throws IllegalStateException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidKeySpecException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException { Cipher cipher = initEncryptModeCipher(); return cipher.doFinal(data.getBytes(Constants.DEFAULT_ENCODING)); } /** * Decrypts byte data to a UTF-8 string. * * @param data * data to decrypt. * @return * UTF-8 string of decrypted data. * * @throws InvalidKeyException * @throws InvalidAlgorithmParameterException * @throws UnsupportedEncodingException * @throws IllegalStateException * @throws IllegalBlockSizeException * @throws BadPaddingException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException */ public String decryptString(byte[] data) throws InvalidKeyException, InvalidAlgorithmParameterException, UnsupportedEncodingException, IllegalStateException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { Cipher cipher = initEncryptModeCipher(); return new String(cipher.doFinal(data), Constants.DEFAULT_ENCODING); } /** * Decrypts a UTF-8 string. * * @param data * data to decrypt. * @param startIndex * start index of data to decrypt. * @param endIndex * end index of data to decrypt. * @return * UTF-8 string of decrypted data. * * @throws InvalidKeyException * @throws InvalidAlgorithmParameterException * @throws UnsupportedEncodingException * @throws IllegalStateException * @throws IllegalBlockSizeException * @throws BadPaddingException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException */ public String decryptString(byte[] data, int startIndex, int endIndex) throws InvalidKeyException, InvalidAlgorithmParameterException, UnsupportedEncodingException, IllegalStateException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { Cipher cipher = initDecryptModeCipher(); return new String(cipher.doFinal(data, startIndex, endIndex), Constants.DEFAULT_ENCODING); } /** * Encrypts byte data to bytes. * * @param data * data to encrypt. * @return * encrypted data. * * @throws IllegalStateException * @throws IllegalBlockSizeException * @throws BadPaddingException * @throws InvalidKeyException * @throws InvalidAlgorithmParameterException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException */ public byte[] encrypt(byte[] data) throws IllegalStateException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException { Cipher cipher = initEncryptModeCipher(); return cipher.doFinal(data); } /** * Decrypts byte data to bytes. * * @param data * data to decrypt * @return * decrypted data. * * @throws InvalidKeyException * @throws InvalidAlgorithmParameterException * @throws IllegalStateException * @throws IllegalBlockSizeException * @throws BadPaddingException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException */ public byte[] decrypt(byte[] data) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalStateException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { Cipher cipher = initDecryptModeCipher(); return cipher.doFinal(data); } /** * Decrypts a byte data range to bytes. * * @param data * @param startIndex * @param endIndex * @return * decrypted data. * @throws InvalidKeyException * @throws InvalidAlgorithmParameterException * @throws IllegalStateException * @throws IllegalBlockSizeException * @throws BadPaddingException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException */ public byte[] decrypt(byte[] data, int startIndex, int endIndex) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalStateException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { Cipher cipher = initDecryptModeCipher(); return cipher.doFinal(data, startIndex, endIndex); } /** * Wraps an input stream in an encrypting cipher stream. * * @param is * @return * encrypting cipher input stream. * @throws InvalidKeyException * @throws InvalidAlgorithmParameterException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException */ public CipherInputStream encrypt(InputStream is) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException { Cipher cipher = initEncryptModeCipher(); return new CipherInputStream(is, cipher); } /** * Wraps an input stream in an decrypting cipher stream. * * @param is * @return * decrypting cipher input stream. * @throws InvalidKeyException * @throws InvalidAlgorithmParameterException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException */ public CipherInputStream decrypt(InputStream is) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException { Cipher cipher = initDecryptModeCipher(); return new CipherInputStream(is, cipher); } /** * Wraps an output stream in an encrypting cipher stream. * * @param os * @return * encrypting cipher output stream. * @throws InvalidKeyException * @throws InvalidAlgorithmParameterException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException */ public CipherOutputStream encrypt(OutputStream os) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException { Cipher cipher = initEncryptModeCipher(); return new CipherOutputStream(os, cipher); } /** * Wraps an output stream in a decrypting cipher stream. * * @param os * @return * decrypting cipher output stream. * @throws InvalidKeyException * @throws InvalidAlgorithmParameterException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException */ public CipherOutputStream decrypt(OutputStream os) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException { Cipher cipher = initDecryptModeCipher(); return new CipherOutputStream(os, cipher); } /** * Returns an estimate of the number of bytes that will result when data * of the given length is encrypted. The accuracy of this estimate may * depend on the cipher you are using, so be wary of trusting this estimate * without supporting evidence. * * @param inputSize * The number of bytes you intend to encrypt. * * @return * an estimate of the number of bytes that will be generated by the * encryption cipher for the given number of bytes of input. * * @throws InvalidKeyException * @throws InvalidAlgorithmParameterException * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException */ public long getEncryptedOutputSize(long inputSize) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException { Cipher cipher = initEncryptModeCipher(); long outputSize = 0; // Break input size into integer-sized chunks so we can estimate values // for large inputs. int maxChunk = Integer.MAX_VALUE - 8192; while (inputSize >= maxChunk) { outputSize += cipher.getOutputSize(maxChunk); inputSize -= maxChunk; } outputSize += cipher.getOutputSize((int)inputSize); return outputSize; } /** * @return * the Java name of the cipher algorithm being used by this class. */ public String getAlgorithm() { return algorithm; } /** * Returns true if the given cipher is available and can be used by this encryption * utility. To determine whether the cipher can actually be used a test string is * encrypted using the cipher. * * @param cipher * @return * true if the cipher is available and can be used, false otherwise. */ public static boolean isCipherAvailableForUse(String cipher) { try { EncryptionUtil encryptionUtil = new EncryptionUtil("Sample Key", cipher, EncryptionUtil.DEFAULT_VERSION); encryptionUtil.encrypt("Testing encryption..."); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug("Availability test failed for encryption cipher " + cipher); } return false; } return true; } /** * Lists the PBE ciphers available on the system, optionally eliminating those * ciphers that are apparently available but cannot actually be used (perhaps due to * the lack of export-grade JCE settings). * * @param testAvailability * if true each apparently available cipher is tested and only those that pass * {@link #isCipherAvailableForUse(String)} are returned. * * @return * a list of all the available PBE cipher names on the system. */ public static String[] listAvailablePbeCiphers(boolean testAvailability) { Set ciphers = Security.getAlgorithms("Cipher"); Set pbeCiphers = new HashSet(); for (Iterator iter = ciphers.iterator(); iter.hasNext(); ) { String cipher = (String) iter.next(); if (cipher.toLowerCase().startsWith("pbe")) { if (!testAvailability || isCipherAvailableForUse(cipher)) { pbeCiphers.add(cipher); } } } return (String[]) pbeCiphers.toArray(new String[pbeCiphers.size()]); } public static Provider[] listAvailableProviders() { return Security.getProviders(); } public static void main(String[] args) throws Exception { Provider[] providers = EncryptionUtil.listAvailableProviders(); System.out.println("Providers:"); for (int i = 0; i < providers.length; i++) { System.out.println(" - " + providers[i]); } String[] ciphers = EncryptionUtil.listAvailablePbeCiphers(false); System.out.println("PBE Ciphers available (untested):"); for (int i = 0; i < ciphers.length; i++) { System.out.println(" - " + ciphers[i]); } ciphers = EncryptionUtil.listAvailablePbeCiphers(true); System.out.println("PBE Ciphers available (tested):"); for (int i = 0; i < ciphers.length; i++) { System.out.println(" - " + ciphers[i]); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy