001package com.gigya.android.sdk.encryption; 002 003import android.content.Context; 004import android.os.Build; 005import android.security.KeyPairGeneratorSpec; 006import android.security.keystore.KeyGenParameterSpec; 007import android.security.keystore.KeyProperties; 008import android.support.annotation.RequiresApi; 009 010import com.gigya.android.sdk.persistence.IPersistenceService; 011import com.gigya.android.sdk.utils.CipherUtils; 012 013import java.math.BigInteger; 014import java.security.Key; 015import java.security.KeyPairGenerator; 016import java.security.KeyStore; 017import java.security.PrivateKey; 018import java.security.PublicKey; 019import java.util.Calendar; 020import java.util.TimeZone; 021 022import javax.crypto.Cipher; 023import javax.crypto.KeyGenerator; 024import javax.crypto.SecretKey; 025import javax.crypto.spec.SecretKeySpec; 026import javax.security.auth.x500.X500Principal; 027 028@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 029public class SessionKey implements ISecureKey { 030 031 private String TYPE = "AndroidKeyStore"; 032 033 private final Context _context; 034 private final IPersistenceService _psService; 035 036 final static String SECRET_PREFERENCE_KEY = "GS_PREFA"; 037 038 public SessionKey(Context context, IPersistenceService psService) { 039 _context = context; 040 _psService = psService; 041 } 042 043 @Override 044 public String getAlias() { 045 return "GS_ALIAS"; 046 } 047 048 @Override 049 public String getTransformation() { 050 return "RSA/ECB/PKCS1Padding"; // Not using constants because they are only available from >=23. 051 } 052 053 @Override 054 public Cipher getEncryptionCipher(Key key) throws EncryptionException { 055 try { 056 final Cipher cipher = Cipher.getInstance(getTransformation()); 057 cipher.init(Cipher.ENCRYPT_MODE, key); 058 return cipher; 059 } catch (Exception ex) { 060 ex.printStackTrace(); 061 throw new EncryptionException("getDecryptionCipher: exception" + ex.getMessage(), ex.getCause()); 062 } 063 } 064 065 @Override 066 public Cipher getDecryptionCipher(Key key) throws EncryptionException { 067 try { 068 final Cipher cipher = Cipher.getInstance(getTransformation()); 069 cipher.init(Cipher.DECRYPT_MODE, key); 070 return cipher; 071 } catch (Exception ex) { 072 ex.printStackTrace(); 073 throw new EncryptionException("getDecryptionCipher: exception" + ex.getMessage(), ex.getCause()); 074 } 075 } 076 077 078 @Override 079 public SecretKey getKey() throws EncryptionException { 080 try { 081 KeyStore keyStore = KeyStore.getInstance(TYPE); 082 keyStore.load(null); 083 if (!keyStore.containsAlias(getAlias())) { // Keystore not available. 084 // Generate certificate for this new alias. 085 generateCertificateForAlias(); 086 final PublicKey publicKey = keyStore.getCertificate(getAlias()).getPublicKey(); 087 // Generate AES secret key 088 final KeyGenerator generator = KeyGenerator.getInstance("AES"); 089 generator.init(128); 090 final SecretKey secretKey = generator.generateKey(); 091 // Encrypt secret key and save it. 092 final Cipher cipher = getEncryptionCipher(publicKey); 093 byte[] encryptedAES = cipher.doFinal(secretKey.getEncoded()); 094 final String newEncryptedSecret = CipherUtils.bytesToString(encryptedAES); 095 _psService.add(SECRET_PREFERENCE_KEY, newEncryptedSecret); 096 return secretKey; 097 } else if (!keyStore.entryInstanceOf(getAlias(), KeyStore.PrivateKeyEntry.class)) { 098 // Determines if the keystore for the specified entry is an instance or subclass of the alias specified. 099 // If not delete the entry and create a new one. 100 keyStore.deleteEntry(getAlias()); 101 return getKey(); // Recursive. 102 } else { 103 // Keystore instance exist. Get private key from KeyStore instance and decrypt & generate the secret key. 104 105 final String aesKey = _psService.getString(SECRET_PREFERENCE_KEY, null); 106 if (aesKey != null && keyStore.containsAlias(getAlias()) && keyStore.entryInstanceOf(getAlias(), KeyStore.PrivateKeyEntry.class)) { 107 // In (v3) secret key is ciphered and saved in the shared preferences. 108 final PrivateKey privateKey = (PrivateKey) keyStore.getKey(getAlias(), null); 109 final Cipher cipher = getDecryptionCipher(privateKey); 110 byte[] decrypted = cipher.doFinal(CipherUtils.stringToBytes(aesKey)); 111 final String ALGORITHM_KEY = "AES"; 112 return new SecretKeySpec(decrypted, 0, decrypted.length, ALGORITHM_KEY); 113 } 114 return null; 115 } 116 } catch (Exception ex) { 117 ex.printStackTrace(); 118 throw new EncryptionException("GetKey: exception" + ex.getMessage(), ex.getCause()); 119 } 120 } 121 122 //region CERTIFICATE 123 124 private void generateCertificateForAlias() throws Exception { 125 final String ALGORITHM_KEYSTORE = "RSA"; 126 final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM_KEYSTORE, TYPE); 127 final TimeZone timeZone = TimeZone.getTimeZone("UTC"); 128 final Calendar start = Calendar.getInstance(timeZone); 129 final Calendar end = Calendar.getInstance(timeZone); 130 end.add(Calendar.YEAR, 25); 131 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 132 final KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder( 133 getAlias(), 134 KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) 135 .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) 136 .setBlockModes(KeyProperties.BLOCK_MODE_ECB) 137 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) 138 .build(); 139 keyGen.initialize(spec); 140 keyGen.generateKeyPair(); 141 } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 142 final KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(_context) 143 .setAlias(getAlias()) 144 .setKeySize(2048) 145 .setEndDate(end.getTime()) 146 .setStartDate(start.getTime()) 147 .setSerialNumber(BigInteger.ONE) 148 .setSubject(new X500Principal("CN = Secured Preference Store, O = Devliving Online")) 149 .build(); 150 keyGen.initialize(spec); 151 keyGen.generateKeyPair(); 152 } else { 153 final KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(_context) 154 .setAlias(getAlias()) 155 .setEndDate(end.getTime()) 156 .setStartDate(start.getTime()) 157 .setSerialNumber(BigInteger.ONE) 158 .setSubject(new X500Principal("CN = Secured Preference Store, O = Devliving Online")) 159 .build(); 160 keyGen.initialize(spec); 161 keyGen.generateKeyPair(); 162 } 163 } 164 165 //endregion 166 167}