001package co.codewizards.cloudstore.core.auth;
002
003import static co.codewizards.cloudstore.core.util.Util.*;
004
005import java.security.KeyFactory;
006import java.security.PublicKey;
007import java.security.SecureRandom;
008import java.security.spec.EncodedKeySpec;
009import java.security.spec.X509EncodedKeySpec;
010
011import javax.crypto.Cipher;
012import javax.crypto.spec.SecretKeySpec;
013
014import org.slf4j.Logger;
015import org.slf4j.LoggerFactory;
016
017import co.codewizards.cloudstore.core.config.Config;
018
019public class SignedAuthTokenEncrypter {
020        private static final Logger logger = LoggerFactory.getLogger(SignedAuthTokenEncrypter.class);
021
022        public static final int DEFAULT_KEY_SIZE = 128;
023        public static final String CONFIG_KEY_KEY_SIZE = "authTokenEncryption.keySize";
024
025        private static SecureRandom random = new SecureRandom();
026
027        private PublicKey publicKey;
028
029        public SignedAuthTokenEncrypter(byte[] publicKeyData) {
030                assertNotNull("publicKeyData", publicKeyData);
031                BouncyCastleRegistrationUtil.registerBouncyCastleIfNeeded();
032                try {
033                        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
034                        EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyData);
035                        this.publicKey = keyFactory.generatePublic(publicKeySpec);
036                } catch (RuntimeException e) {
037                        throw e;
038                } catch (Exception e) {
039                        throw new RuntimeException(e);
040                }
041        }
042
043        public EncryptedSignedAuthToken encrypt(byte[] signedAuthTokenData) {
044                try {
045                        byte[] symKey = new byte[getKeySize() / 8];
046                        random.nextBytes(symKey);
047
048                        Cipher asymCipher = Cipher.getInstance("RSA/None/OAEPWITHSHA1ANDMGF1PADDING");
049                        asymCipher.init(Cipher.ENCRYPT_MODE, publicKey);
050                        byte[] symKeyEncrypted = asymCipher.doFinal(symKey);
051
052                        Cipher symCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
053//                      symCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(symKey, "AES"), new IvParameterSpec(new byte[symKey.length]));
054                        symCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(symKey, "AES"));
055                        // We do not really need an IV, because we use a random key ONCE.
056                        // An IV is essentially important for security, if the key is used multiple times.
057                        // However, it doesn't cause us much trouble to transmit the IV and it may add
058                        // additional security due to the added complexity if it is not 0. Maybe the NSA
059                        // can attack easier, if the IV is 0. Very unlikely, but still. Hence we do not
060                        // enforce it to be 0 (which we could to save a few bytes in the transfer).
061                        // Marco :-)
062                        byte[] symIV = symCipher.getIV();
063                        byte[] signedAuthTokenDataEncrypted = symCipher.doFinal(signedAuthTokenData);
064
065                        EncryptedSignedAuthToken result = new EncryptedSignedAuthToken();
066                        result.setEncryptedSignedAuthTokenData(signedAuthTokenDataEncrypted);
067                        result.setEncryptedSignedAuthTokenDataIV(symIV);
068                        result.setEncryptedSymmetricKey(symKeyEncrypted);
069                        return result;
070                } catch (RuntimeException e) {
071                        throw e;
072                } catch (Exception e) {
073                        throw new RuntimeException(e);
074                }
075        }
076
077        protected int getKeySize() {
078                int keySize = Config.getInstance().getPropertyAsInt(CONFIG_KEY_KEY_SIZE, DEFAULT_KEY_SIZE);
079                if (keySize < 64) {
080                        logger.warn("Config key '{}': keySize {} is out of range! Using default {} instead!", CONFIG_KEY_KEY_SIZE, keySize, DEFAULT_KEY_SIZE);
081                        return DEFAULT_KEY_SIZE;
082                }
083                return keySize;
084        }
085}