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}