package co.codewizards.cloudstore.core.auth;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import co.codewizards.cloudstore.core.config.ConfigImpl;
import co.codewizards.cloudstore.core.util.AssertUtil;
public class SignedAuthTokenEncrypter {
private static final Logger logger = LoggerFactory.getLogger(SignedAuthTokenEncrypter.class);
public static final int DEFAULT_KEY_SIZE = 128;
public static final String CONFIG_KEY_KEY_SIZE = "authTokenEncryption.keySize";
private static SecureRandom random = new SecureRandom();
private PublicKey publicKey;
public SignedAuthTokenEncrypter(final byte[] publicKeyData) {
AssertUtil.assertNotNull(publicKeyData, "publicKeyData");
BouncyCastleRegistrationUtil.registerBouncyCastleIfNeeded();
try {
final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
final EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyData);
this.publicKey = keyFactory.generatePublic(publicKeySpec);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
public EncryptedSignedAuthToken encrypt(final byte[] signedAuthTokenData) {
try {
final byte[] symKey = new byte[getKeySize() / 8];
random.nextBytes(symKey);
final Cipher asymCipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING");
asymCipher.init(Cipher.ENCRYPT_MODE, publicKey);
final byte[] symKeyEncrypted = asymCipher.doFinal(symKey);
final Cipher symCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// symCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(symKey, "AES"), new IvParameterSpec(new byte[symKey.length]));
symCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(symKey, "AES"));
// We do not really need an IV, because we use a random key ONCE.
// An IV is essentially important for security, if the key is used multiple times.
// However, it doesn't cause us much trouble to transmit the IV and it may add
// additional security due to the added complexity if it is not 0. Maybe the NSA
// can attack easier, if the IV is 0. Very unlikely, but still. Hence we do not
// enforce it to be 0 (which we could to save a few bytes in the transfer).
// Marco :-)
final byte[] symIV = symCipher.getIV();
final byte[] signedAuthTokenDataEncrypted = symCipher.doFinal(signedAuthTokenData);
final EncryptedSignedAuthToken result = new EncryptedSignedAuthToken();
result.setEncryptedSignedAuthTokenData(signedAuthTokenDataEncrypted);
result.setEncryptedSignedAuthTokenDataIV(symIV);
result.setEncryptedSymmetricKey(symKeyEncrypted);
return result;
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
protected int getKeySize() {
final int keySize = ConfigImpl.getInstance().getPropertyAsInt(CONFIG_KEY_KEY_SIZE, DEFAULT_KEY_SIZE);
if (keySize < 64) {
logger.warn("Config key '{}': keySize {} is out of range! Using default {} instead!", CONFIG_KEY_KEY_SIZE, keySize, DEFAULT_KEY_SIZE);
return DEFAULT_KEY_SIZE;
}
return keySize;
}
}