aws-sdk-android icon indicating copy to clipboard operation
aws-sdk-android copied to clipboard

Key's password should not have to be the same as the keystore's password

Open azabost opened this issue 2 years ago • 1 comments

Describe the bug This SDK contains a few pieces of code that assume the key's password and the keystore's password are exactly the same.

For example, both overloads of AWSIotKeystoreHelper.getIotKeystore accept a keyStorePassword parameter that is used for two different purposes:

  1. Passed to KeyStore.load
  2. Passed to getTempKeystore method as customerKeystorePassword parameter that is later used for KeyStore.getKey

The KeyStore.load usage is correct. According to load's Javadoc: "A password may be given to unlock the keystore (...) or to check the integrity of the keystore data". However, the Keystore.getKey usage is wrong because the password must match the key's password, not the keystore's password. It looks like the key's password and the keystore's passwords are confused and treated as if they were the same thing, which is a wrong assumption. As a result, to make the SDK work, one must use a keystore whose password is exactly the same as the key's password.

Another example is AWSIotKeystoreHelper.saveCertificateAndPrivateKey method that uses keystorePassword parameter for both KeyStore.setKeyEntry and for KeyStore.store.

To Reproduce

val keystore = KeyStore.getInstance(KeyStore.getDefaultType()).apply { load(null) }

val certFactory = CertificateFactory.getInstance("X.509")

val certs = listOf("removed to make this code shorter", "let me know if you need real examples").map {
    certFactory.generateCertificate(ByteArrayInputStream(Base64.Default.decode(it)))
}

val kpg = KeyPairGenerator.getInstance("RSA").apply { initialize(2048) }
val key = kpg.generateKeyPair().private
keystore.setKeyEntry("alias", key, "KeyPassword".toCharArray(), certs.toTypedArray())

val keystoreOutputStream = ByteArrayOutputStream()
keystore.store(keystoreOutputStream, "KeystorePassword".toCharArray())
val keystoreBytes = keystoreOutputStream.toByteArray()
keystoreOutputStream.close()
val keystoreInputStream = ByteArrayInputStream(keystoreBytes)

// Proper usage example:
val awsKeystore = KeyStore.getInstance(KeyStore.getDefaultType())
awsKeystore.load(keystoreInputStream, "KeystorePassword".toCharArray())
val recoveredKey = awsKeystore.getKey("alias", "KeyPassword".toCharArray())

println(recoveredKey.encoded.contentEquals(key.encoded))

// No way to pass both passwords:
AWSIotKeystoreHelper.getIotKeystore(
    "alias",
    keystoreInputStream,
    "KeystorePassword"
)

Which AWS service(s) are affected? AWS SDK IoT

Expected behavior AWS SDK should accept both the keystore's password and the key's password separately and not assume they are the same.

Screenshots n/a

Environment Information (please complete the following information):

  • AWS Android SDK Version: 2.73.0
  • Device: all
  • Android Version: all
  • Specific to simulators: no

Additional context n/a

azabost avatar Aug 28 '23 18:08 azabost

@azabost I've taken a look at the information you provided and agree with your assessment. I will mark this as feature-request so we can add overloaded methods to allow separate passwords for key and keystore. Thank you.

tylerjroach avatar Sep 11 '23 17:09 tylerjroach