/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.servoy.j2db.server.headlessclient;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.util.crypt.AbstractCrypt;
import org.apache.wicket.util.lang.Classes;
/**
* Provide some simple means to encrypt and decrypt strings such as passwords. The whole
* implementation is based around Sun's security providers and uses the <a
* href="http://www.semoa.org/docs/api/cdc/standard/pbe/PBEWithMD5AndDES.html">PBEWithMD5AndDES</a>
* method to encrypt and decrypt the data.
*
* @author Juergen Donnerstag
*/
@SuppressWarnings("nls")
public class CachingSunJceCrypt extends AbstractCrypt
{
/**
* Iteration count used in combination with the salt to create the encryption key.
*/
private final static int COUNT = 17;
/** Name of encryption method */
private static final String CRYPT_METHOD = "PBEWithMD5AndDES";
/** Salt */
private final static byte[] salt = { (byte)0x15, (byte)0x8c, (byte)0xa3, (byte)0x4a, (byte)0x66, (byte)0x51, (byte)0x2a, (byte)0xbc };
private final Cipher decryptCiph;
private final Cipher encryptCiph;
/**
* Constructor
* @throws GeneralSecurityException
*/
public CachingSunJceCrypt(String key) throws GeneralSecurityException
{
setKey(key);
if (Security.getProviders("Cipher." + CRYPT_METHOD).length == 0)
{
try
{
// Initialize and add a security provider required for encryption
final Class< ? > clazz = Classes.resolveClass("com.sun.crypto.provider.SunJCE");
Security.addProvider((Provider)clazz.newInstance());
}
catch (IllegalAccessException ex)
{
throw new WicketRuntimeException("Unable to load SunJCE service provider", ex);
}
catch (InstantiationException ex)
{
throw new WicketRuntimeException("Unable to load SunJCE service provider", ex);
}
}
SecretKey sKey = generateSecretKey();
PBEParameterSpec spec = new PBEParameterSpec(salt, COUNT);
decryptCiph = Cipher.getInstance(CRYPT_METHOD);
decryptCiph.init(Cipher.DECRYPT_MODE, sKey, spec);
encryptCiph = Cipher.getInstance(CRYPT_METHOD);
encryptCiph.init(Cipher.ENCRYPT_MODE, sKey, spec);
}
/**
* Crypts the given byte array
*
* @param input
* byte array to be crypted
* @param mode
* crypt mode
* @return the input crypted. Null in case of an error
* @throws GeneralSecurityException
*/
@Override
protected synchronized final byte[] crypt(final byte[] input, final int mode) throws GeneralSecurityException
{
if (mode == Cipher.DECRYPT_MODE)
{
try
{
return decryptCiph.doFinal(input);
}
catch (GeneralSecurityException e)
{
decryptCiph.doFinal();
throw e;
}
}
try
{
return encryptCiph.doFinal(input);
}
catch (GeneralSecurityException e)
{
encryptCiph.doFinal();
throw e;
}
}
/**
* Generate the de-/encryption key.
* <p>
* Note: if you don't provide your own encryption key, the implementation will use a default. Be
* aware that this is potential security risk. Thus make sure you always provide your own one.
*
* @return secretKey the security key generated
* @throws NoSuchAlgorithmException
* unable to find encryption algorithm specified
* @throws InvalidKeySpecException
* invalid encryption key
*/
private final SecretKey generateSecretKey() throws NoSuchAlgorithmException, InvalidKeySpecException
{
final PBEKeySpec spec = new PBEKeySpec(getKey().toCharArray());
return SecretKeyFactory.getInstance(CRYPT_METHOD).generateSecret(spec);
}
}