在机器人测试中使用Android密钥库

rqdpfwrv  于 2023-03-21  发布在  Android
关注(0)|答案(2)|浏览(93)

我试图编写一些针对Android Keystore的测试用例,但是,当我编写以下测试用例时:

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
public class FancyPantsUnitTest {
   @Test
   public void buildKey() {
        keyPairGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
        keyPairGenerator.initialize(4096);
        final KeyPair keyPair = keyPairGenerator.generateKeyPair();
   }
}

此操作失败,但出现以下异常:

org.junit.ComparisonFailure: expected:<null> but was:<java.security.KeyStoreException: AndroidKeyStore not found>

我的目标是API级别23,如果有帮助的话。

jhiyze9q

jhiyze9q1#

https://github.com/robolectric/robolectric/issues/1518上已经有关于这个问题的讨论。
简而言之:
来自java.security.安全javadoc:
安全属性的默认值是从特定于实现的位置读取的,该位置通常是Java安装目录中的属性文件lib/security/java.security。
......我们可能不想鼓励人们去胡闹。
看起来这需要是一个方法拦截规则...
尝试PowerMockito时也会发生同样的情况。

aemubtdh

aemubtdh2#

Matthew Dolan在他的article中提出了一个出色的解决方案。
下面是创建FakeAndroidKeyStore的代码

import java.io.InputStream
import java.io.OutputStream
import java.security.Key
import java.security.KeyStore
import java.security.KeyStoreSpi
import java.security.Provider
import java.security.SecureRandom
import java.security.Security
import java.security.cert.Certificate
import java.security.spec.AlgorithmParameterSpec
import java.util.Date
import java.util.Enumeration
import javax.crypto.KeyGenerator
import javax.crypto.KeyGeneratorSpi
import javax.crypto.SecretKey

object FakeAndroidKeyStore {

    val setup by lazy {
        Security.addProvider(object : Provider("AndroidKeyStore", 1.0, "") {
            init {
                put("KeyStore.AndroidKeyStore", FakeKeyStore::class.java.name)
                put("KeyGenerator.AES", FakeAesKeyGenerator::class.java.name)
            }
        })
    }

    @Suppress("unused")
    class FakeKeyStore : KeyStoreSpi() {
        private val wrapped = KeyStore.getInstance(KeyStore.getDefaultType())

        override fun engineIsKeyEntry(alias: String?): Boolean = wrapped.isKeyEntry(alias)
        override fun engineIsCertificateEntry(alias: String?): Boolean = wrapped.isCertificateEntry(alias)
        override fun engineGetCertificate(alias: String?): Certificate = wrapped.getCertificate(alias)
        override fun engineGetCreationDate(alias: String?): Date = wrapped.getCreationDate(alias)
        override fun engineDeleteEntry(alias: String?) = wrapped.deleteEntry(alias)
        override fun engineSetKeyEntry(alias: String?, key: Key?, password: CharArray?, chain: Array<out Certificate>?) =
            wrapped.setKeyEntry(alias, key, password, chain)

        override fun engineSetKeyEntry(alias: String?, key: ByteArray?, chain: Array<out Certificate>?) = wrapped.setKeyEntry(alias, key, chain)
        override fun engineStore(stream: OutputStream?, password: CharArray?) = wrapped.store(stream, password)
        override fun engineSize(): Int = wrapped.size()
        override fun engineAliases(): Enumeration<String> = wrapped.aliases()
        override fun engineContainsAlias(alias: String?): Boolean = wrapped.containsAlias(alias)
        override fun engineLoad(stream: InputStream?, password: CharArray?) = wrapped.load(stream, password)
        override fun engineGetCertificateChain(alias: String?): Array<Certificate> = wrapped.getCertificateChain(alias)
        override fun engineSetCertificateEntry(alias: String?, cert: Certificate?) = wrapped.setCertificateEntry(alias, cert)
        override fun engineGetCertificateAlias(cert: Certificate?): String = wrapped.getCertificateAlias(cert)
        override fun engineGetKey(alias: String?, password: CharArray?): Key = wrapped.getKey(alias, password)
    }

    @Suppress("unused")
    class FakeAesKeyGenerator : KeyGeneratorSpi() {
        private val wrapped = KeyGenerator.getInstance("AES")

        override fun engineInit(random: SecureRandom?) = Unit
        override fun engineInit(params: AlgorithmParameterSpec?, random: SecureRandom?) = Unit
        override fun engineInit(keysize: Int, random: SecureRandom?) = Unit
        override fun engineGenerateKey(): SecretKey = wrapped.generateKey()
    }
}

在调用java.security.KeyStore之前使用。
例如:

@Before
fun setup() {
  FakeAndroidKeyStore.setup
  KeyStore.getInstance(...)
  ...
}

相关问题