Refactoring password encryption (#147)

This commit is contained in:
Rafał Borcz 2018-07-18 21:13:57 +02:00 committed by Mikołaj Pich
parent 5dcd4f9b72
commit ef5d3aead9
16 changed files with 320 additions and 310 deletions

View File

@ -124,7 +124,7 @@ jobs:
- *attach_workspace
- run:
name: Setup emulator
command: sdkmanager "system-images;android-16;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-16;default;armeabi-v7a"
command: sdkmanager "system-images;android-19;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-19;default;armeabi-v7a"
- run:
name: Launch emulator
command: export LD_LIBRARY_PATH=${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/qt/lib && emulator64-arm -avd test -noaudio -no-boot-anim -no-window -accel on
@ -140,12 +140,9 @@ jobs:
# unlock the emulator screen
sleep 30
adb shell input keyevent 82
- run:
name: Clean project
command: ./gradlew clean --no-daemon --stacktrace --console=plain -PdisablePreDex
- run:
name: Run instrumented tests
command: ./gradlew createDebugCoverageReport --no-daemon --stacktrace --console=plain -PdisablePreDex
command: ./gradlew clean createDebugCoverageReport jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex
- run:
name: Collect logs from emulator
command: adb logcat -d > ./app/build/reports/logcat_emulator.txt

View File

@ -141,4 +141,5 @@ dependencies {
androidTestImplementation "com.android.support.test:runner:$testRunner"
androidTestImplementation "org.mockito:mockito-android:$mockito"
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
}

View File

@ -1,25 +0,0 @@
package io.github.wulkanowy.utils.security;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SdkSuppress;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ScramblerTest {
@Test
@SdkSuppress(minSdkVersion = 18)
public void encryptDecryptTest() throws Exception {
Context targetContext = InstrumentationRegistry.getTargetContext();
Assert.assertEquals("PASS", Scrambler.decrypt("TEST",
Scrambler.encrypt("TEST", "PASS", targetContext)));
}
}

View File

@ -0,0 +1,48 @@
package io.github.wulkanowy.utils.security
import android.support.test.InstrumentationRegistry
import android.support.test.filters.SdkSuppress
import android.support.test.filters.SmallTest
import android.support.test.runner.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import java.security.KeyStore
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class ScramblerTest {
@Test
fun encryptDecryptTest() {
assertEquals("TEST", Scrambler.decrypt(Scrambler.encrypt("TEST",
InstrumentationRegistry.getTargetContext())))
}
@Test
fun emptyTextEncryptTest() {
assertFailsWith<ScramblerException> {
Scrambler.decrypt("")
}
assertFailsWith<ScramblerException> {
Scrambler.encrypt("", InstrumentationRegistry.getTargetContext())
}
}
@Test
@SdkSuppress(minSdkVersion = 18)
fun emptyKeyStoreTest() {
val text = Scrambler.encrypt("test", InstrumentationRegistry.getTargetContext())
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
keyStore.deleteEntry("USER_PASSWORD")
assertFailsWith<ScramblerException> {
Scrambler.decrypt(text)
}
}
}

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.github.wulkanowy"
android:installLocation="internalOnly">
<uses-permission android:name="android.permission.INTERNET" />
@ -36,8 +35,7 @@
android:name=".ui.main.MainActivity"
android:configChanges="orientation|screenSize"
android:label="@string/activity_dashboard_text"
android:launchMode="singleTop"
/>
android:launchMode="singleTop" />
<activity
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
android:theme="@style/WulkanowyTheme.DarkActionBar" />

View File

@ -1,74 +0,0 @@
package io.github.wulkanowy;
import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.answers.Answers;
import com.crashlytics.android.core.CrashlyticsCore;
import com.jakewharton.threetenabp.AndroidThreeTen;
import org.greenrobot.greendao.query.QueryBuilder;
import javax.inject.Inject;
import dagger.android.AndroidInjector;
import dagger.android.support.DaggerApplication;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import io.fabric.sdk.android.Fabric;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.di.DaggerAppComponent;
import io.github.wulkanowy.utils.FabricUtils;
import io.github.wulkanowy.utils.LoggerUtils;
import timber.log.Timber;
public class WulkanowyApp extends DaggerApplication {
@Inject
RepositoryContract repository;
@Override
public void onCreate() {
super.onCreate();
AndroidThreeTen.init(this);
if (BuildConfig.DEBUG) {
enableDebugLog();
}
initializeFabric();
initializeUserSession();
}
private void initializeUserSession() {
if (repository.getSharedRepo().isUserLoggedIn()) {
try {
repository.getSyncRepo().initLastUser();
FabricUtils.logLogin("Open app", true);
} catch (Exception e) {
FabricUtils.logLogin("Open app", false);
Timber.e(e, "An error occurred when the application was started");
}
}
}
private void enableDebugLog() {
QueryBuilder.LOG_VALUES = true;
FlexibleAdapter.enableLogs(eu.davidea.flexibleadapter.utils.Log.Level.DEBUG);
Timber.plant(new LoggerUtils.DebugLogTree());
}
private void initializeFabric() {
Fabric.with(new Fabric.Builder(this)
.kits(
new Crashlytics.Builder()
.core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
.build(),
new Answers()
)
.debuggable(BuildConfig.DEBUG)
.build());
Timber.plant(new LoggerUtils.CrashlyticsTree());
}
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.builder().create(this);
}
}

View File

@ -0,0 +1,74 @@
package io.github.wulkanowy
import com.crashlytics.android.Crashlytics
import com.crashlytics.android.answers.Answers
import com.crashlytics.android.core.CrashlyticsCore
import com.jakewharton.threetenabp.AndroidThreeTen
import dagger.android.AndroidInjector
import dagger.android.support.DaggerApplication
import eu.davidea.flexibleadapter.FlexibleAdapter
import io.fabric.sdk.android.Fabric
import io.github.wulkanowy.data.RepositoryContract
import io.github.wulkanowy.di.DaggerAppComponent
import io.github.wulkanowy.utils.FabricUtils
import io.github.wulkanowy.utils.LoggerUtils
import io.github.wulkanowy.utils.security.ScramblerException
import org.greenrobot.greendao.query.QueryBuilder
import timber.log.Timber
import javax.inject.Inject
class WulkanowyApp : DaggerApplication() {
@Inject
internal lateinit var repository: RepositoryContract
override fun onCreate() {
super.onCreate()
AndroidThreeTen.init(this)
if (BuildConfig.DEBUG) {
enableDebugLog()
}
initializeFabric()
initializeUserSession()
}
private fun initializeUserSession() {
if (repository.sharedRepo.isUserLoggedIn) {
try {
repository.syncRepo.initLastUser()
FabricUtils.logLogin("Open app", true)
} catch (e: Exception) {
FabricUtils.logLogin("Open app", false)
Timber.e(e, "An error occurred when the application was started")
} catch (e: ScramblerException) {
FabricUtils.logLogin("Open app", false)
Timber.e(e, "A security error has occurred")
repository.cleanAllData()
}
}
}
private fun enableDebugLog() {
QueryBuilder.LOG_VALUES = true
FlexibleAdapter.enableLogs(eu.davidea.flexibleadapter.utils.Log.Level.DEBUG)
Timber.plant(LoggerUtils.DebugLogTree())
}
private fun initializeFabric() {
Fabric.with(Fabric.Builder(this)
.kits(
Crashlytics.Builder()
.core(CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
.build(),
Answers()
)
.debuggable(BuildConfig.DEBUG)
.build())
Timber.plant(LoggerUtils.CrashlyticsTree())
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> =
DaggerAppComponent.builder().create(this)
}

View File

@ -30,7 +30,7 @@ public class Migration23 implements DbHelper.Migration {
final Map<String, String> user = getAccountData(db);
vulcan.setCredentials(
user.get("email"),
Scrambler.decrypt(user.get("email"), user.get("password")),
Scrambler.decrypt(user.get("password")),
user.get("symbol"),
user.get("school_id"),
"", // inserted in code bellow

View File

@ -14,7 +14,6 @@ import io.github.wulkanowy.R;
import io.github.wulkanowy.api.NotLoggedInErrorException;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.utils.AppConstant;
import io.github.wulkanowy.utils.security.CryptoException;
import timber.log.Timber;
@Singleton
@ -39,11 +38,9 @@ public class ResourcesRepository implements ResourcesContract {
@Override
public String getErrorLoginMessage(Exception exception) {
Timber.e(exception,"%s encountered a error", AppConstant.APP_NAME);
Timber.e(exception, "%s encountered a error", AppConstant.APP_NAME);
if (exception instanceof CryptoException) {
return resources.getString(R.string.encrypt_failed_text);
} else if (exception instanceof UnknownHostException) {
if (exception instanceof UnknownHostException) {
return resources.getString(R.string.noInternet_text);
} else if (exception instanceof SocketTimeoutException) {
return resources.getString(R.string.generic_timeout_error);

View File

@ -26,8 +26,8 @@ import io.github.wulkanowy.data.db.dao.entities.Symbol;
import io.github.wulkanowy.data.db.dao.entities.SymbolDao;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.security.CryptoException;
import io.github.wulkanowy.utils.security.Scrambler;
import io.github.wulkanowy.utils.security.ScramblerException;
import timber.log.Timber;
@Singleton
@ -51,7 +51,7 @@ public class AccountSync {
}
public void registerUser(String email, String password, String symbol)
throws VulcanException, IOException, CryptoException {
throws VulcanException, IOException, ScramblerException {
clearUserData();
@ -79,11 +79,11 @@ public class AccountSync {
Timber.i("Register end");
}
private Account insertAccount(String email, String password) throws CryptoException {
private Account insertAccount(String email, String password) throws ScramblerException {
Timber.d("Register account");
Account account = new Account()
.setEmail(email)
.setPassword(Scrambler.encrypt(email, password, context));
.setPassword(Scrambler.encrypt(password, context));
daoSession.getAccountDao().insert(account);
return account;
}
@ -150,7 +150,7 @@ public class AccountSync {
daoSession.getSemesterDao().insertInTx(semesterList);
}
public void initLastUser() throws CryptoException {
public void initLastUser() throws ScramblerException {
long userId = sharedPref.getCurrentUserId();
@ -180,7 +180,7 @@ public class AccountSync {
vulcan.setCredentials(
account.getEmail(),
Scrambler.decrypt(account.getEmail(), account.getPassword()),
Scrambler.decrypt(account.getPassword()),
symbol.getSymbol(),
school.getRealId(),
student.getRealId(),

View File

@ -6,15 +6,16 @@ import java.text.ParseException;
import javax.inject.Singleton;
import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.utils.security.CryptoException;
import io.github.wulkanowy.utils.security.ScramblerException;
@Singleton
public interface SyncContract {
void registerUser(String email, String password, String symbol) throws VulcanException,
IOException, CryptoException;
IOException, ScramblerException;
void initLastUser() throws IOException, ScramblerException;
void initLastUser() throws IOException, CryptoException;
void syncGrades(int semesterName) throws VulcanException, IOException, ParseException;

View File

@ -7,7 +7,7 @@ import javax.inject.Singleton;
import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.data.db.dao.DbContract;
import io.github.wulkanowy.utils.security.CryptoException;
import io.github.wulkanowy.utils.security.ScramblerException;
@Singleton
public class SyncRepository implements SyncContract {
@ -41,12 +41,12 @@ public class SyncRepository implements SyncContract {
@Override
public void registerUser(String email, String password, String symbol) throws VulcanException,
IOException, CryptoException {
IOException, ScramblerException {
accountSync.registerUser(email, password, symbol);
}
@Override
public void initLastUser() throws CryptoException {
public void initLastUser() throws ScramblerException {
accountSync.initLastUser();
}

View File

@ -1,9 +0,0 @@
package io.github.wulkanowy.utils.security;
public class CryptoException extends Exception {
public CryptoException(String message) {
super(message);
}
}

View File

@ -1,176 +0,0 @@
package io.github.wulkanowy.utils.security;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.security.KeyPairGeneratorSpec;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Base64;
import org.apache.commons.lang3.ArrayUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.util.ArrayList;
import java.util.Calendar;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.security.auth.x500.X500Principal;
import timber.log.Timber;
public final class Scrambler {
private static final String ANDROID_KEYSTORE = "AndroidKeyStore";
private static KeyStore keyStore;
private Scrambler() {
throw new IllegalStateException("Utility class");
}
public static String encrypt(String email, String plainText, Context context)
throws CryptoException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
loadKeyStore();
generateNewKey(email, context);
return encryptString(email, plainText);
}
return new String(Base64.encode(plainText.getBytes(), Base64.DEFAULT));
}
public static String decrypt(String email, String encryptedText) throws CryptoException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
loadKeyStore();
return decryptString(email, encryptedText);
}
return new String(Base64.decode(encryptedText, Base64.DEFAULT));
}
private static void loadKeyStore() throws CryptoException {
try {
keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
keyStore.load(null);
} catch (Exception e) {
throw new CryptoException(e.getMessage());
}
}
@SuppressWarnings("deprecation")
@TargetApi(18)
private static void generateNewKey(String alias, Context context) throws CryptoException {
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
AlgorithmParameterSpec spec;
end.add(Calendar.YEAR, 10);
if (!"".equals(alias)) {
try {
if (!keyStore.containsAlias(alias)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
spec = new KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setDigests(KeyProperties.DIGEST_SHA256)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
.setCertificateNotBefore(start.getTime())
.setCertificateNotAfter(end.getTime())
.build();
} else {
spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(alias)
.setSubject(new X500Principal("CN=" + alias))
.setSerialNumber(BigInteger.TEN)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
}
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA",
ANDROID_KEYSTORE);
keyPairGenerator.initialize(spec);
keyPairGenerator.generateKeyPair();
}
} catch (Exception e) {
throw new CryptoException(e.getMessage());
}
} else {
throw new CryptoException("GenerateNewKey - String is empty");
}
Timber.d("Key pair are create");
}
private static String encryptString(String alias, String text) throws CryptoException {
if (!alias.isEmpty() && !text.isEmpty()) {
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();
Cipher input = Cipher.getInstance("RSA/ECB/PKCS1Padding");
input.init(Cipher.ENCRYPT_MODE, publicKey);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(
outputStream, input);
cipherOutputStream.write(text.getBytes("UTF-8"));
cipherOutputStream.close();
byte[] values = outputStream.toByteArray();
return Base64.encodeToString(values, Base64.DEFAULT);
} catch (Exception e) {
throw new CryptoException(e.getMessage());
}
} else {
throw new CryptoException("EncryptString - String is empty");
}
}
private static String decryptString(String alias, String text) throws CryptoException {
if (!alias.isEmpty() && !text.isEmpty()) {
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(Base64.decode(text, Base64.DEFAULT)), output);
ArrayList<Byte> values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte) nextByte);
}
Byte[] bytes = values.toArray(new Byte[values.size()]);
return new String(ArrayUtils.toPrimitive(bytes), 0, bytes.length, "UTF-8");
} catch (Exception e) {
throw new CryptoException(e.getMessage());
}
} else {
throw new CryptoException("EncryptString - String is empty");
}
}
}

View File

@ -0,0 +1,172 @@
@file:Suppress("DEPRECATION")
package io.github.wulkanowy.utils.security
import android.annotation.TargetApi
import android.content.Context
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.JELLY_BEAN_MR2
import android.os.Build.VERSION_CODES.M
import android.security.KeyPairGeneratorSpec
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties.*
import android.util.Base64
import android.util.Base64.DEFAULT
import org.apache.commons.lang3.StringUtils
import timber.log.Timber
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.math.BigInteger
import java.nio.charset.Charset
import java.security.KeyPairGenerator
import java.security.KeyStore
import java.security.PrivateKey
import java.security.PublicKey
import java.util.*
import javax.crypto.Cipher
import javax.crypto.Cipher.DECRYPT_MODE
import javax.crypto.Cipher.ENCRYPT_MODE
import javax.crypto.CipherInputStream
import javax.crypto.CipherOutputStream
import javax.security.auth.x500.X500Principal
import kotlin.collections.ArrayList
object Scrambler {
private const val KEY_ALIAS = "USER_PASSWORD"
private const val ALGORITHM_RSA = "RSA"
private const val KEYSTORE_NAME = "AndroidKeyStore"
private const val KEY_TRANSFORMATION_ALGORITHM = "RSA/ECB/PKCS1Padding"
private const val KEY_CIPHER_JELLY_PROVIDER = "AndroidOpenSSL"
private const val KEY_CIPHER_M_PROVIDER = "AndroidKeyStoreBCWorkaround"
private val KEY_CHARSET = Charset.forName("UTF-8")
@JvmStatic
fun encrypt(plainText: String, context: Context): String {
if (StringUtils.isEmpty(plainText)) {
throw ScramblerException("Text to be encrypted is empty")
}
if (SDK_INT < JELLY_BEAN_MR2) {
return String(Base64.encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET)
}
try {
if (!isKeyPairExist()) {
generateKeyPair(context)
}
val cipher = getCipher()
cipher.init(ENCRYPT_MODE, getPublicKey())
val outputStream = ByteArrayOutputStream()
val cipherOutputStream = CipherOutputStream(outputStream, cipher)
cipherOutputStream.write(plainText.toByteArray(KEY_CHARSET))
cipherOutputStream.close()
return Base64.encodeToString(outputStream.toByteArray(), DEFAULT)
} catch (e: Exception) {
throw ScramblerException("An error occurred while encrypting text", e)
}
}
@JvmStatic
fun decrypt(cipherText: String): String {
if (StringUtils.isEmpty(cipherText)) {
throw ScramblerException("Text to be encrypted is empty")
}
if (SDK_INT < JELLY_BEAN_MR2) {
return String(Base64.decode(cipherText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET)
}
if (!isKeyPairExist()) {
throw ScramblerException("KeyPair doesn't exist")
}
try {
val cipher = getCipher()
cipher.init(DECRYPT_MODE, getPrivateKey())
val input = CipherInputStream(ByteArrayInputStream(Base64.decode(cipherText, DEFAULT)), cipher)
val values = ArrayList<Byte>()
var nextByte = 0
while ({ nextByte = input.read(); nextByte }() != -1) {
values.add(nextByte.toByte())
}
val bytes = ByteArray(values.size)
for (i in bytes.indices) {
bytes[i] = values[i]
}
return String(bytes, 0, bytes.size, KEY_CHARSET)
} catch (e: Exception) {
throw ScramblerException("An error occurred while decrypting text", e)
}
}
private fun getKeyStoreInstance(): KeyStore {
val keyStore = KeyStore.getInstance(KEYSTORE_NAME)
keyStore.load(null)
return keyStore
}
private fun getPublicKey(): PublicKey =
(getKeyStoreInstance().getEntry(KEY_ALIAS, null) as KeyStore.PrivateKeyEntry)
.certificate.publicKey
private fun getPrivateKey(): PrivateKey =
(getKeyStoreInstance().getEntry(KEY_ALIAS, null) as KeyStore.PrivateKeyEntry).privateKey
private fun getCipher(): Cipher {
if (SDK_INT >= M) {
return Cipher.getInstance(KEY_TRANSFORMATION_ALGORITHM, KEY_CIPHER_M_PROVIDER)
}
return Cipher.getInstance(KEY_TRANSFORMATION_ALGORITHM, KEY_CIPHER_JELLY_PROVIDER)
}
@TargetApi(JELLY_BEAN_MR2)
private fun generateKeyPair(context: Context) {
val spec = if (SDK_INT >= M) {
KeyGenParameterSpec.Builder(KEY_ALIAS, PURPOSE_DECRYPT or PURPOSE_ENCRYPT)
.setDigests(DIGEST_SHA256, DIGEST_SHA512)
.setCertificateSubject(X500Principal("CN=Wulkanowy"))
.setEncryptionPaddings(ENCRYPTION_PADDING_RSA_PKCS1)
.setSignaturePaddings(SIGNATURE_PADDING_RSA_PKCS1)
.setCertificateSerialNumber(BigInteger.TEN)
.build()
} else {
val start = Calendar.getInstance()
val end = Calendar.getInstance()
end.add(Calendar.YEAR, 99)
KeyPairGeneratorSpec.Builder(context)
.setAlias(KEY_ALIAS)
.setSubject(X500Principal("CN=Wulkanowy"))
.setSerialNumber(BigInteger.TEN)
.setStartDate(start.time)
.setEndDate(end.time)
.build()
}
val generator = KeyPairGenerator.getInstance(ALGORITHM_RSA, KEYSTORE_NAME)
generator.initialize(spec)
generator.generateKeyPair()
Timber.i("A new KeyPair has been generated")
}
private fun isKeyPairExist(): Boolean = getKeyStoreInstance().getKey(KEY_ALIAS, null) != null
}

View File

@ -0,0 +1,6 @@
package io.github.wulkanowy.utils.security
class ScramblerException : Exception {
constructor(message: String, cause: Throwable) : super(message, cause)
constructor(message: String) : super(message)
}