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 /** 046 * Iteration count used in combination with the salt to create the encryption key. 047 */ 048 private final static int DEFAULT_ITERATION_COUNT = 17; 049 050 /** Name of the default encryption method */ 051 public static final String DEFAULT_CRYPT_METHOD = "PBEWithMD5AndDES"; 052 053 /** 054 * Default salt. 055 * 056 * @deprecated TODO remove in Wicket 10 057 */ 058 @Deprecated 059 public final static byte[] SALT = { (byte)0x15, (byte)0x8c, (byte)0xa3, (byte)0x4a, 060 (byte)0x66, (byte)0x51, (byte)0x2a, (byte)0xbc }; 061 062 /** The name of encryption method (cipher) */ 063 private final String cryptMethod; 064 065 private final int iterationCount; 066 067 private final byte[] salt; 068 069 /** 070 * Constructor 071 * 072 * @deprecated TODO remove in Wicket 10 073 */ 074 @Deprecated(forRemoval = true) 075 public SunJceCrypt() 076 { 077 this(DEFAULT_CRYPT_METHOD); 078 } 079 080 /** 081 * Constructor. 082 * 083 * @param salt 084 * salt for encryption 085 * @param iterationCount 086 * iteration count 087 */ 088 public SunJceCrypt(byte[] salt, int iterationCount) 089 { 090 this(DEFAULT_CRYPT_METHOD, salt, iterationCount); 091 } 092 093 /** 094 * Constructor 095 * 096 * @deprecated TODO remove in Wicket 10 097 */ 098 @Deprecated(forRemoval = true) 099 public SunJceCrypt(String cryptMethod) 100 { 101 this(cryptMethod, SALT, DEFAULT_ITERATION_COUNT); 102 } 103 104 /** 105 * Constructor that uses a custom encryption method (cipher). 106 * You may need to override {@link #createKeySpec()} and/or 107 * {@link #createParameterSpec()} for the custom cipher. 108 * 109 * @param cryptMethod 110 * the name of encryption method (the cipher) 111 * @param salt 112 * salt for encryption 113 * @param iterationCount 114 * iteration count 115 */ 116 public SunJceCrypt(String cryptMethod, byte[] salt, int iterationCount) 117 { 118 this.cryptMethod = Args.notNull(cryptMethod, "Crypt method"); 119 this.salt = Args.notNull(salt, "salt"); 120 this.iterationCount = Args.withinRange(1, Integer.MAX_VALUE, iterationCount, "iterationCount"); 121 } 122 123 /** 124 * Crypts the given byte array 125 * 126 * @param input 127 * byte array to be encrypted 128 * @param mode 129 * crypt mode 130 * @return the input crypted. Null in case of an error 131 * @throws GeneralSecurityException 132 */ 133 @Override 134 protected byte[] crypt(final byte[] input, final int mode) 135 throws GeneralSecurityException 136 { 137 SecretKey key = generateSecretKey(); 138 AlgorithmParameterSpec spec = createParameterSpec(); 139 Cipher ciph = createCipher(key, spec, mode); 140 return ciph.doFinal(input); 141 } 142 143 /** 144 * Creates the {@link javax.crypto.Cipher} that will do the de-/encryption. 145 * 146 * @param key 147 * the secret key to use 148 * @param spec 149 * the parameters spec to use 150 * @param mode 151 * the mode ({@link javax.crypto.Cipher#ENCRYPT_MODE} or {@link javax.crypto.Cipher#DECRYPT_MODE}) 152 * @return the cipher that will do the de-/encryption 153 * @throws GeneralSecurityException 154 */ 155 protected Cipher createCipher(SecretKey key, AlgorithmParameterSpec spec, int mode) throws GeneralSecurityException 156 { 157 Cipher cipher = Cipher.getInstance(cryptMethod); 158 cipher.init(mode, key, spec); 159 return cipher; 160 } 161 162 /** 163 * Generate the de-/encryption key. 164 * <p> 165 * Note: if you don't provide your own encryption key, the implementation will use a default. Be 166 * aware that this is potential security risk. Thus make sure you always provide your own one. 167 * 168 * @return secretKey the security key generated 169 * @throws NoSuchAlgorithmException 170 * unable to find encryption algorithm specified 171 * @throws InvalidKeySpecException 172 * invalid encryption key 173 */ 174 protected SecretKey generateSecretKey() throws NoSuchAlgorithmException, 175 InvalidKeySpecException 176 { 177 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(cryptMethod); 178 KeySpec spec = createKeySpec(); 179 return keyFactory.generateSecret(spec); 180 } 181 182 /** 183 * @return the parameter spec to be used for the configured crypt method 184 */ 185 protected AlgorithmParameterSpec createParameterSpec() 186 { 187 return new PBEParameterSpec(salt, iterationCount); 188 } 189 190 /** 191 * @return the key spec to be used for the configured crypt method 192 */ 193 protected KeySpec createKeySpec() 194 { 195 return new PBEKeySpec(getKey().toCharArray()); 196 } 197 198 /** 199 * Create a random salt to be used for this crypt. 200 * 201 * @return salt, always 8 bytes long 202 */ 203 public static byte[] randomSalt() 204 { 205 // must be 8 bytes - for anything else PBES1Core throws 206 // InvalidAlgorithmParameterException: Salt must be 8 bytes long 207 byte[] salt = new byte[8]; 208 new Random().nextBytes(salt); 209 return salt; 210 } 211}