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