Wednesday, September 23, 2020

How to store encryption keys safely in Android 19+?

We have to encrypt our data while saving or sending over the Internet. We have to use shared key algorithms for encryption because they are fast. But what to do with the keys, how to generate and keep them safe?

Developers usually use some of the approaches mentioned below:

1. Generate shared key in the app with shared logic in app and server.

This can be used for encrypting/decrypting data locally and for sending data over Internet. Problem with this approach is, app can be decompiled and logic can reconstructed. Once the logic is reconstructed hacker can keep making the keys as and when required.

2. Get key from Server and use it.

Security of the key depends on how the key is transported to Mobile app. If someone can grab it, then it is compromised and can be used to decrypt data and can even be used to modify it. 

Recommended approach for this is to use HTTPS connection and send key on it. Ideally new key should be used with each request, as this gives very little time to the hacker to identify the key and decrypt the contents.

3. Generate key using random no generator and save it safely.

This can be used for encrypting/decrypting data locally, but where to save the keys. Following post describes how to use Android keystore and store keys safely. This code is tested on devices having OS version 4.4 onwards.

Using Android Keystore to save AES key

The Android Keystore system lets you store cryptographic keys in a container to make it more difficult to extract from the device. Once keys are in the keystore, they can be used for cryptographic operations with the key material remaining non-exportable. Moreover, it offers facilities to restrict when and how keys can be used, such as requiring user authentication for key use or restricting keys to be used only in certain cryptographic modes.[1]

Use the Android Keystore provider to let an individual app store its own credentials that only the app itself can access. This provides a way for apps to manage credentials that are usable only by itself while providing the same security benefits that the KeyChain API provides for system-wide credentials. This method requires no user interaction to select the credentials.[1]

For using Keystore we will generate RSA key pair and then use it to encrypt AES key.

private val ALIAS = "Alias"

private val RSA_ALGO = "RSA"
private val KEY_STORE_NAME = "AndroidKeyStore"
private val AES_TRANSFORMATION = "AES/GCM/NoPadding"

private val RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding"
@Throws(GeneralSecurityException::class)
fun generateRSAKeyPair(context: Context) {
val start = Calendar.getInstance()
val end = Calendar.getInstance()
end.add(Calendar.YEAR, 30)
val spec = KeyPairGeneratorSpec.Builder(context)
.setAlias(ALIAS)
.setSubject(X500Principal("CN=" + ALIAS))
.setSerialNumber(BigInteger.TEN)
.setStartDate(start.time)
.setEndDate(end.time)
.build()

val gen = KeyPairGenerator.getInstance(RSA_ALGO, KEY_STORE_NAME)
gen.initialize(spec)
gen.generateKeyPair()
}

//We can grab the public key using

@Throws(Exception::class)
fun getRsaPublicKey(): PublicKey {
val keyStore = KeyStore.getInstance(KEY_STORE_NAME)
keyStore.load(null)

return keyStore.getCertificate(ALIAS).publicKey
}

//Private key can be retrieved using
@Throws(Exception::class)
fun getRsaPrivateKey(): PrivateKey? {
val keyStore = KeyStore.getInstance(KEY_STORE_NAME)
keyStore.load(null)
return (keyStore.getEntry(ALIAS, null) as? KeyStore.PrivateKeyEntry)?.privateKey
}

//Generate AES Key and IV using
fun generateAesKey(): ByteArray {
    val secureRandom = SecureRandom()
val key = ByteArray(32)
secureRandom.nextBytes(key)
return key
}
fun getIv(): ByteArray {
val secureRandom = SecureRandom()
val iv = ByteArray(12)
secureRandom.nextBytes(iv)
return iv
}
//Encrypt the AES key using RSA public key
fun encryptUsingRsa(plain: ByteArray, publicKey1: PublicKey): String {
val cipher = Cipher.getInstance(RSA_TRANSFORMATION)
cipher.init(Cipher.ENCRYPT_MODE, publicKey1)
val encryptedBytes = cipher.doFinal(plain)

val encrypted = bytesToString(encryptedBytes)
return encrypted
}
//Decrypt the AES key using RSA private key
fun decryptUsingRsa(result: String, privateKey1: PrivateKey?): ByteArray {
if(privateKey1==null) return ByteArray(0)

val cipher1 = Cipher.getInstance(RSA_TRANSFORMATION)
cipher1.init(Cipher.DECRYPT_MODE, privateKey1)
return cipher1.doFinal(stringToBytes(result))
}

fun bytesToString(b: ByteArray): String {
return Base64.encodeToString(b, Base64.NO_WRAP)
}


fun stringToBytes(s: String): ByteArray {
return Base64.decode(s, Base64.NO_WRAP)
}

References:
[1] https://developer.android.com/training/articles/keystore

Saturday, July 18, 2020

Curious case of MissingMethodInvocationException and Mockito.when

Have you tried to mock a method in Kotlin? As mentioned in this guide we can simply use following code to make it work.

//You can mock concrete classes, not just interfaces 
LinkedList mockedList = mock(LinkedList.class); /
/stubbing 
Mockito.when(mockedList.get(0)).thenReturn("first");


If you try it on kotlin class, it will fail with exception MissingMethodInvocationException.
e.g.
open class DummyPresenter {

fun getSomeValue():String{
return "Some Value"
}
}
@RunWith(MockitoJUnitRunner::class)
class ExampleUnitTest {
@Test
fun testCallGetSomeValue() {
    val presenter: DummyPresenter = mock(DummyPresenter::class.java)
    `when`(presenter.getSomeValue()).thenReturn("Some other value")
    val result = TestClass().callGetSomeValue(presenter)
    println("Test Result:$result")
    assertEquals("Some other value",result)
}
}
org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'.
For example:
    when(mock.getArticles()).thenReturn(articles);

Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
   Those methods *cannot* be stubbed/verified.
   Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.


This exception is misleading and does not explain the cause. It occurs because DummyPresenter.getSomeValue() is not open. 

Obvious solution to this problem is to make the method open, but, is it a good solution? No, why should we mark method open when it is not required.

There are 2 more solutions for unit tests:
1. add a gradle plugin which will do this job for you. Details mentioned in this post.
2. add a file named org.mockito.plugins.MockMaker under test/resources/mockito-extensions. Save the file with mock-maker-inline text.


Mockmaker does not work with Instrumentation tests. For androidTest you have to use gradle plugin specified in option 1 as mentioned below.

    a. add dependancy in project level build.gradle under 
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
}
}
 
    b. create annotation class in some package e.g. com.package as
annotation class AllOpen4Testing
 
    c. add following in module level build.gradle
    apply plugin: "kotlin-allopen"
android {
allOpen {
 annotation("com.package.AllOpen4Testing")
}
}
 
I hope this solves the problem. Thanks for reading.


Friday, May 29, 2020

Flutter: How to get device details like UUID, deviceName?

We will use device_info package for finding these details. Use this link to check latest version of the package. 

Add package_info: ^0.4.0+3 in pubspec.yaml file of your project.
below 
dependencies:
  flutter:
    sdk: flutter
    package_info: ^0.4.0+3


Using next code sample you can retrieve the details.

Future<List<String>> getDeviceDetails() async {
    String deviceName;
    String deviceVersion;
    String identifier;
    final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
    try {
      if (Platform.isAndroid) {
        
        var build = await deviceInfoPlugin.androidInfo;
        deviceName = build.model;
        deviceVersion = build.version.toString();
        identifier = build.androidId; //UUID for Android
      } else if (Platform.isIOS) {
        var data = await deviceInfoPlugin.iosInfo;
        deviceName = data.name;
        deviceVersion = data.systemVersion;
        identifier = data.identifierForVendor; //UUID for iOS
      }
    } on Exception catch (e) {
      print('Failed to get platform version ${e.toString()}');
    }
    return [deviceName, deviceVersion, identifier];
  }
For Iphone it returns
[iPhone Xʀ, 12.2, 0EE6BD4D-FB32-4283-B9C2-BD235DD421CF]

New to Flutter? Good resources to begin with.

Coming from Android background

https://flutter.dev/docs/get-started/flutter-for/android-devs
https://codelabs.developers.google.com/codelabs/from-java-to-dart/#0

Coming from iOS background

https://flutter.dev/docs/get-started/flutter-for/ios-devs

Find out which widget to choose:

https://flutter.dev/docs/development/ui/widgets/material

https://flutter.dev/docs/development/ui/layout

https://flutter.dev/docs/development/ui/widgets


How to use Assets?

https://flutter.dev/docs/development/ui/assets-and-images

Create flavours?

https://flutter.dev/docs/deployment/flavors

Building widget based on api Response

https://medium.com/nonstopio/flutter-future-builder-with-list-view-builder-d7212314e8c9

Building widget events of stream

https://codingwithjoe.com/flutter-building-a-widget-with-streambuilder/

Saturday, February 29, 2020

Using AndroidViewModel for accessing application context

If you want to use context in view model, you can't use ViewModel as  it does not provide you context needed for creating Database. So Android architecture components introduced AndroidViewModel. If we use ViewModelProvider(this).get(ProductListingVM::class.java), it will cause error "java.lang.Class<com.example.swapnil.sat.models.ProductListingVM> has no zero argument constructor" thrown at runtime. So, how to use it? We shall pass AndroidViewModelFactory instance while creating the ViewModelProvider. 

Let's have a look.

MainActivity.kt

import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider

class MainActivity : AppCompatActivity() {

lateinit var viewModel: ProductListingVM

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    viewModel=ViewModelProvider(this,
        ViewModelProvider.AndroidViewModelFactory(application))
        .get(ProductListingVM::class.java)

........
  }
}


ProductListingVM.kt

class ProductListingVM(private val applicationInstance: Application) 
  : AndroidViewModel(applicationInstance) {
  
  private var appDatabase = SatDatabase.getDatabase(applicationInstance)
}

Android aar deployment in Maven - 2022

Introduction If you are working on android library project, you might be wondering how to publish it on Maven like this . Earl...