-
Notifications
You must be signed in to change notification settings - Fork 1
/
EncryptionManagerImpl.kt
172 lines (144 loc) · 5.66 KB
/
EncryptionManagerImpl.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package com.onelogin.mfa.data.encryption
import android.content.Context
import android.content.SharedPreferences
import android.os.Build
import android.security.KeyPairGeneratorSpec
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.util.Base64
import timber.log.Timber
import java.math.BigInteger
import java.security.*
import java.util.*
import javax.crypto.Cipher
import javax.crypto.NoSuchPaddingException
import javax.security.auth.x500.X500Principal
internal class EncryptionManagerImpl(private val context: Context): EncryptionManager {
private var decryptCipher: Cipher? = null
private var encryptCipher: Cipher? = null
private var masterKey: KeyPair? = null
private var sharedPreferences: SharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE)
init {
try {
if (isSupported())
generateKeys()
} catch (e: Exception) {
Timber.e(e, "Error in generating keys")
setIsEncryptionSupported(false)
}
}
@Synchronized
fun initialize() {
if (masterKey == null)
masterKey = getAndroidKeyStoreAsymmetricKeyPair()
if (encryptCipher == null) {
encryptCipher = prepareCipher()
encryptCipher?.init(Cipher.ENCRYPT_MODE, masterKey?.public)
}
if (decryptCipher == null) {
decryptCipher = prepareCipher()
decryptCipher?.init(Cipher.DECRYPT_MODE, masterKey?.private)
}
}
@Synchronized
override fun encrypt(plainData: String): String {
initialize()
val bytes = encryptCipher?.doFinal(plainData.toByteArray())
return Base64.encodeToString(bytes, Base64.DEFAULT)
}
@Synchronized
override fun decrypt(encryptedData: String): String {
initialize()
val encryptedDataDecoded = Base64.decode(encryptedData, Base64.DEFAULT)
val decodedData = decryptCipher?.doFinal(encryptedDataDecoded)
return String(decodedData!!)
}
override fun isSupported(): Boolean {
if (isEncryptionSupportChecked())
return isEncryptionSupported()
val result = try {
generateKeys()
setIsEncryptionSupported(true)
true
} catch (e: Exception) {
setIsEncryptionSupported(false)
false
}
setIsEncryptionSupportChecked(true)
return result
}
@Suppress("DEPRECATION")
private fun generateKeys() {
if (getAndroidKeyStoreAsymmetricKeyPair() != null)
return
val generator = KeyPairGenerator.getInstance(RSA_ENCRYPTION, PROVIDER)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val builder = KeyGenParameterSpec.Builder(ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_ECB)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
generator.initialize(builder.build())
} else {
val start = Calendar.getInstance()
val end = Calendar.getInstance()
end.add(Calendar.YEAR, 20)
val builder = KeyPairGeneratorSpec.Builder(context)
.setAlias(ALIAS)
.setSubject(X500Principal("CN=" + ALIAS + "CA Certificate"))
.setSerialNumber(BigInteger.ONE)
.setStartDate(start.time)
.setEndDate(end.time)
generator.initialize(builder.build())
}
generator.generateKeyPair()
}
private fun prepareKeyStore(): KeyStore? {
return try {
val keyStore = KeyStore.getInstance(PROVIDER)
keyStore.load(null)
keyStore
} catch (e: Exception) {
Timber.e(e, "KeyStore not supported")
null
}
}
private fun prepareCipher(): Cipher {
val cipher: Cipher
try {
cipher = Cipher.getInstance(TRANSFORMATION)
} catch (e: NoSuchAlgorithmException) {
throw RuntimeException(e)
} catch (e: NoSuchPaddingException) {
throw RuntimeException(e)
}
return cipher
}
private fun getAndroidKeyStoreAsymmetricKeyPair(): KeyPair? {
val keyStore: KeyStore? = prepareKeyStore()
val privateKey = keyStore?.getKey(ALIAS, null) as PrivateKey?
val publicKey = keyStore?.getCertificate(ALIAS)?.publicKey
return if (privateKey != null && publicKey != null) {
KeyPair(publicKey, privateKey)
} else {
null
}
}
private fun isEncryptionSupported() = sharedPreferences.getBoolean(ENCRYPTION_SUPPORTED, false)
private fun setIsEncryptionSupported(isSupported: Boolean) = with(sharedPreferences.edit()) {
putBoolean(ENCRYPTION_SUPPORTED, isSupported)
commit()
}
private fun isEncryptionSupportChecked() = sharedPreferences.getBoolean(ENCRYPTION_SUPPORT_CHECKED, false)
private fun setIsEncryptionSupportChecked(isChecked: Boolean) = with(sharedPreferences.edit()) {
putBoolean(ENCRYPTION_SUPPORT_CHECKED, isChecked)
commit()
}
companion object {
private const val TRANSFORMATION = "RSA/ECB/PKCS1Padding"
private const val PROVIDER = "AndroidKeyStore"
private const val RSA_ENCRYPTION = "RSA"
private const val ALIAS = "ONELOGIN"
private const val SHARED_PREFERENCES = "oneloginMfaPreferences"
private const val ENCRYPTION_SUPPORTED = "oneloginMfaEncryptionSupported"
private const val ENCRYPTION_SUPPORT_CHECKED = "oneloginMfaEncryptionSupportChecked"
}
}