001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.util.crypt; 018 019import java.security.GeneralSecurityException; 020import java.security.NoSuchAlgorithmException; 021import java.security.spec.AlgorithmParameterSpec; 022import java.security.spec.InvalidKeySpecException; 023import java.security.spec.KeySpec; 024import java.util.Random; 025 026import javax.crypto.Cipher; 027import javax.crypto.SecretKey; 028import javax.crypto.SecretKeyFactory; 029import javax.crypto.spec.PBEKeySpec; 030import javax.crypto.spec.PBEParameterSpec; 031 032import org.apache.wicket.util.lang.Args; 033 034 035/** 036 * Provide some simple means to encrypt and decrypt strings such as passwords. The whole 037 * implementation is based around Sun's security providers and uses the <a 038 * href="http://www.ietf.org/rfc/rfc2898.txt">PBEWithMD5AndDES</a> method to encrypt and decrypt the 039 * data. 040 * 041 * @author Juergen Donnerstag 042 */ 043public class SunJceCrypt extends AbstractCrypt 044{ 045 /** Name of the default encryption method */ 046 public static final String DEFAULT_CRYPT_METHOD = "PBEWithMD5AndDES"; 047 048 /** The name of encryption method (cipher) */ 049 private final String cryptMethod; 050 051 private final int iterationCount; 052 053 private final byte[] salt; 054 055 /** 056 * Constructor. 057 * 058 * @param salt 059 * salt for encryption 060 * @param iterationCount 061 * iteration count 062 */ 063 public SunJceCrypt(byte[] salt, int iterationCount) 064 { 065 this(DEFAULT_CRYPT_METHOD, salt, iterationCount); 066 } 067 068 /** 069 * Constructor that uses a custom encryption method (cipher). 070 * You may need to override {@link #createKeySpec()} and/or 071 * {@link #createParameterSpec()} for the custom cipher. 072 * 073 * @param cryptMethod 074 * the name of encryption method (the cipher) 075 * @param salt 076 * salt for encryption 077 * @param iterationCount 078 * iteration count 079 */ 080 public SunJceCrypt(String cryptMethod, byte[] salt, int iterationCount) 081 { 082 this.cryptMethod = Args.notNull(cryptMethod, "Crypt method"); 083 this.salt = Args.notNull(salt, "salt"); 084 this.iterationCount = Args.withinRange(1, Integer.MAX_VALUE, iterationCount, "iterationCount"); 085 } 086 087 /** 088 * Crypts the given byte array 089 * 090 * @param input 091 * byte array to be encrypted 092 * @param mode 093 * crypt mode 094 * @return the input crypted. Null in case of an error 095 * @throws GeneralSecurityException 096 */ 097 @Override 098 protected byte[] crypt(final byte[] input, final int mode) 099 throws GeneralSecurityException 100 { 101 SecretKey key = generateSecretKey(); 102 AlgorithmParameterSpec spec = createParameterSpec(); 103 Cipher ciph = createCipher(key, spec, mode); 104 return ciph.doFinal(input); 105 } 106 107 /** 108 * Creates the {@link javax.crypto.Cipher} that will do the de-/encryption. 109 * 110 * @param key 111 * the secret key to use 112 * @param spec 113 * the parameters spec to use 114 * @param mode 115 * the mode ({@link javax.crypto.Cipher#ENCRYPT_MODE} or {@link javax.crypto.Cipher#DECRYPT_MODE}) 116 * @return the cipher that will do the de-/encryption 117 * @throws GeneralSecurityException 118 */ 119 protected Cipher createCipher(SecretKey key, AlgorithmParameterSpec spec, int mode) throws GeneralSecurityException 120 { 121 Cipher cipher = Cipher.getInstance(cryptMethod); 122 cipher.init(mode, key, spec); 123 return cipher; 124 } 125 126 /** 127 * Generate the de-/encryption key. 128 * <p> 129 * Note: if you don't provide your own encryption key, the implementation will use a default. Be 130 * aware that this is potential security risk. Thus make sure you always provide your own one. 131 * 132 * @return secretKey the security key generated 133 * @throws NoSuchAlgorithmException 134 * unable to find encryption algorithm specified 135 * @throws InvalidKeySpecException 136 * invalid encryption key 137 */ 138 protected SecretKey generateSecretKey() throws NoSuchAlgorithmException, 139 InvalidKeySpecException 140 { 141 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(cryptMethod); 142 KeySpec spec = createKeySpec(); 143 return keyFactory.generateSecret(spec); 144 } 145 146 /** 147 * @return the parameter spec to be used for the configured crypt method 148 */ 149 protected AlgorithmParameterSpec createParameterSpec() 150 { 151 return new PBEParameterSpec(salt, iterationCount); 152 } 153 154 /** 155 * @return the key spec to be used for the configured crypt method 156 */ 157 protected KeySpec createKeySpec() 158 { 159 return new PBEKeySpec(getKey().toCharArray()); 160 } 161 162 /** 163 * Create a random salt to be used for this crypt. 164 * 165 * @return salt, always 8 bytes long 166 */ 167 public static byte[] randomSalt() 168 { 169 // must be 8 bytes - for anything else PBES1Core throws 170 // InvalidAlgorithmParameterException: Salt must be 8 bytes long 171 byte[] salt = new byte[8]; 172 new Random().nextBytes(salt); 173 return salt; 174 } 175}