在 API 调用中使用 AndroidKeyStore 创建 SSLContext KeyManagerFactory 时出错

发布于 2025-01-11 04:32:54 字数 2376 浏览 0 评论 0原文

我正在尝试使用 AndroidKeyStore 调用具有客户端身份验证的 API,但收到以下错误

W/CryptoUpcalls:找不到算法的提供者:RSA/ECB/NoPadding

android.security.KeyStoreException:不兼容的填充模式

fun createSSLContextAndroid(context: Context): SSLContext? {

    val certificateFactory = CertificateFactory.getInstance("X.509")
    val caCertificate =
        certificateFactory.generateCertificate(context.resources.openRawResource(R.raw.ca))

    val clientCertificate = certificateFactory.generateCertificate(context.resources.openRawResource(R.raw.android_new))

    val keyStoreType = KeyStore.getDefaultType()
    val keyStoreTrust = KeyStore.getInstance(keyStoreType)
    keyStoreTrust.load(null, null)
    keyStoreTrust.setCertificateEntry("mt", caCertificate)

    val tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
    val tmf = TrustManagerFactory.getInstance(tmfAlgorithm)
    tmf.init(keyStoreTrust)

    val ks = KeyStore.getInstance("AndroidKeyStore")
    ks.load(null, null)
    val certificateAlias = "certificate"
    ks.setCertificateEntry(certificateAlias, clientCertificate)

    val privateKeyEntry = ks.getEntry(Config.KEYSTORE_ALIAS, null) as KeyStore.PrivateKeyEntry
    
    ks.setKeyEntry(Config.KEYSTORE_ALIAS, privateKeyEntry.privateKey, null, arrayOf(clientCertificate))
    val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
    kmf.init(ks, null)

    val sslContext = SSLContext.getInstance("TLS")
    sslContext.init(kmf.keyManagers, tmf.trustManagers, null)
    return sslContext
}

使用以下代码生成密钥

fun initKeyGeneratorPair(): KeyPairGenerator {
    val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_RSA,
        "AndroidKeyStore"
    )

    kpg.initialize(
        KeyGenParameterSpec.Builder(
            Config.KEYSTORE_ALIAS,
            KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY or KeyProperties.PURPOSE_ENCRYPT or
                    KeyProperties.PURPOSE_DECRYPT
        )
            .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
            .setDigests(
                KeyProperties.DIGEST_NONE,
                KeyProperties.DIGEST_SHA256,
                KeyProperties.DIGEST_SHA512
            )
            .setKeySize(4096)
            .build()
    )
    return kpg
}

I am trying to call API with client authentication using AndroidKeyStore but I am getting the below error

W/CryptoUpcalls: Could not find provider for algorithm: RSA/ECB/NoPadding

android.security.KeyStoreException: Incompatible padding mode

fun createSSLContextAndroid(context: Context): SSLContext? {

    val certificateFactory = CertificateFactory.getInstance("X.509")
    val caCertificate =
        certificateFactory.generateCertificate(context.resources.openRawResource(R.raw.ca))

    val clientCertificate = certificateFactory.generateCertificate(context.resources.openRawResource(R.raw.android_new))

    val keyStoreType = KeyStore.getDefaultType()
    val keyStoreTrust = KeyStore.getInstance(keyStoreType)
    keyStoreTrust.load(null, null)
    keyStoreTrust.setCertificateEntry("mt", caCertificate)

    val tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
    val tmf = TrustManagerFactory.getInstance(tmfAlgorithm)
    tmf.init(keyStoreTrust)

    val ks = KeyStore.getInstance("AndroidKeyStore")
    ks.load(null, null)
    val certificateAlias = "certificate"
    ks.setCertificateEntry(certificateAlias, clientCertificate)

    val privateKeyEntry = ks.getEntry(Config.KEYSTORE_ALIAS, null) as KeyStore.PrivateKeyEntry
    
    ks.setKeyEntry(Config.KEYSTORE_ALIAS, privateKeyEntry.privateKey, null, arrayOf(clientCertificate))
    val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
    kmf.init(ks, null)

    val sslContext = SSLContext.getInstance("TLS")
    sslContext.init(kmf.keyManagers, tmf.trustManagers, null)
    return sslContext
}

generate the key using the below code

fun initKeyGeneratorPair(): KeyPairGenerator {
    val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_RSA,
        "AndroidKeyStore"
    )

    kpg.initialize(
        KeyGenParameterSpec.Builder(
            Config.KEYSTORE_ALIAS,
            KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY or KeyProperties.PURPOSE_ENCRYPT or
                    KeyProperties.PURPOSE_DECRYPT
        )
            .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
            .setDigests(
                KeyProperties.DIGEST_NONE,
                KeyProperties.DIGEST_SHA256,
                KeyProperties.DIGEST_SHA512
            )
            .setKeySize(4096)
            .build()
    )
    return kpg
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

独孤求败 2025-01-18 04:32:54

我还没有发现此问题的确切原因,但对我来说,通过添加 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) 来稍微更改 KeyGenParameterSpec 有所帮助。 .


我仍在寻找确切的原因以及这是否是合适的解决方案。

更新:
经过更多研究和阅读 KeyGenParameterSpec 的文档后,它指出如果密钥要用于 TLS/SSL,通常需要执行以下操作:

.setDigests(KeyProperties.DIGEST_NONE)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE, KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)

如 setEncryptionPaddings 的文档中所示:

/**
 * Sets the set of padding schemes (e.g., {@code PKCS7Padding}, {@code OAEPPadding},
 * {@code PKCS1Padding}, {@code NoPadding}) with which the key can be used when
 * encrypting/decrypting. Attempts to use the key with any other padding scheme will be
 * rejected.
 *
 * <p>This must be specified for keys which are used for encryption/decryption.
 *
 * <p>For RSA private keys used by TLS/SSL servers to authenticate themselves to clients it
 * is usually necessary to authorize the use of no/any padding
 * ({@link KeyProperties#ENCRYPTION_PADDING_NONE}) and/or PKCS#1 encryption padding
 * ({@link KeyProperties#ENCRYPTION_PADDING_RSA_PKCS1}). This is because RSA decryption is
 * required by some cipher suites, and some stacks request decryption using no padding
 * whereas others request PKCS#1 padding.
 *
 * <p>See {@link KeyProperties}.{@code ENCRYPTION_PADDING} constants.
 */
@NonNull
public Builder setEncryptionPaddings(
@KeyProperties.EncryptionPaddingEnum String... paddings) {
    mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
    return this;
}

以及 setDigests:

/**
 * Sets the set of digests algorithms (e.g., {@code SHA-256}, {@code SHA-384}) with which
 * the key can be used. Attempts to use the key with any other digest algorithm will be
 * rejected.
 *
 * <p>This must be specified for signing/verification keys and RSA encryption/decryption
 * keys used with RSA OAEP padding scheme because these operations involve a digest. For
 * HMAC keys, the default is the digest associated with the key algorithm (e.g.,
 * {@code SHA-256} for key algorithm {@code HmacSHA256}). HMAC keys cannot be authorized
 * for more than one digest.
 *
 * <p>For private keys used for TLS/SSL client or server authentication it is usually
 * necessary to authorize the use of no digest ({@link KeyProperties#DIGEST_NONE}). This is
 * because TLS/SSL stacks typically generate the necessary digest(s) themselves and then use
 * a private key to sign it.
 *
 * <p>See {@link KeyProperties}.{@code DIGEST} constants.
 */
@NonNull
public Builder setDigests(@KeyProperties.DigestEnum String... digests) {
    mDigests = ArrayUtils.cloneIfNotEmpty(digests);
    return this;
}

I haven't discovered the exact cause of this issue yet, but for me it helped to change the KeyGenParameterSpec a bit by adding .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE).

Im still searching for the exact cause and whether or not this is an appropriate solution.

Update:
After more research and reading the documentation for KeyGenParameterSpec, it states that if the key is to be used for TLS/SSL, its usually neccesary to do the following:

.setDigests(KeyProperties.DIGEST_NONE)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE, KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)

As seen in the documentation for setEncryptionPaddings:

/**
 * Sets the set of padding schemes (e.g., {@code PKCS7Padding}, {@code OAEPPadding},
 * {@code PKCS1Padding}, {@code NoPadding}) with which the key can be used when
 * encrypting/decrypting. Attempts to use the key with any other padding scheme will be
 * rejected.
 *
 * <p>This must be specified for keys which are used for encryption/decryption.
 *
 * <p>For RSA private keys used by TLS/SSL servers to authenticate themselves to clients it
 * is usually necessary to authorize the use of no/any padding
 * ({@link KeyProperties#ENCRYPTION_PADDING_NONE}) and/or PKCS#1 encryption padding
 * ({@link KeyProperties#ENCRYPTION_PADDING_RSA_PKCS1}). This is because RSA decryption is
 * required by some cipher suites, and some stacks request decryption using no padding
 * whereas others request PKCS#1 padding.
 *
 * <p>See {@link KeyProperties}.{@code ENCRYPTION_PADDING} constants.
 */
@NonNull
public Builder setEncryptionPaddings(
@KeyProperties.EncryptionPaddingEnum String... paddings) {
    mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
    return this;
}

And for setDigests:

/**
 * Sets the set of digests algorithms (e.g., {@code SHA-256}, {@code SHA-384}) with which
 * the key can be used. Attempts to use the key with any other digest algorithm will be
 * rejected.
 *
 * <p>This must be specified for signing/verification keys and RSA encryption/decryption
 * keys used with RSA OAEP padding scheme because these operations involve a digest. For
 * HMAC keys, the default is the digest associated with the key algorithm (e.g.,
 * {@code SHA-256} for key algorithm {@code HmacSHA256}). HMAC keys cannot be authorized
 * for more than one digest.
 *
 * <p>For private keys used for TLS/SSL client or server authentication it is usually
 * necessary to authorize the use of no digest ({@link KeyProperties#DIGEST_NONE}). This is
 * because TLS/SSL stacks typically generate the necessary digest(s) themselves and then use
 * a private key to sign it.
 *
 * <p>See {@link KeyProperties}.{@code DIGEST} constants.
 */
@NonNull
public Builder setDigests(@KeyProperties.DigestEnum String... digests) {
    mDigests = ArrayUtils.cloneIfNotEmpty(digests);
    return this;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文