首页 > 解决方案 > Android:Kotlin 使用邮递员中的客户端证书发出 http 请求

问题描述

到目前为止我做了什么

我已经使用网络安全配置完成了证书固定

我创建了一个 .p12 文件,其中包含客户端证书、私钥、根 CA、中间 CA

在 readKeyStore 中,我尝试加载证书,以便向 API 发出请求

使用邮递员时一切正常,但在 Android 中却是一场彻底的灾难

我知道问题出在那些客户端证书和私钥中,但我只是不知道如何解决它

我的网络配置文件如下

网络安全配置

<network-security-config xmlns:android="http://schemas.android.com/apk/res/android">
<!--    <base-config cleartextTrafficPermitted="false" >-->
<!--    </base-config>-->
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">sandbox.api.visa.com</domain>
        <pin-set >
            <pin digest="SHA-256">OAR3GMlRJTTXus2cwgBASDRg6xwxzO/WSZ/+6vnFhJA=</pin>
            <pin digest="SHA-256">zUIraRNo+4JoAYA7ROeWjARtIoN4rIEbCpfCRQT6N6A=</pin>
            <pin digest="SHA-256">r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=</pin>
        </pin-set>
        </trust-anchors>
    </domain-config>
</network-security-config>

API

package com.okujajoshua.reha.network.reha


import android.app.Application
import android.content.Context
import android.content.res.Resources
import com.okujajoshua.reha.R
import com.okujajoshua.reha.RehaApplication
import com.okujajoshua.reha.network.reha.card.CardActivationBody
import com.okujajoshua.reha.network.reha.card.CreateCardIdResponse
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import okhttp3.Credentials
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.Body
import retrofit2.http.POST
import java.io.BufferedInputStream
import java.io.FileInputStream
import java.io.IOException
import java.io.InputStream
import java.security.KeyManagementException
import java.security.KeyStore
import java.security.KeyStoreException
import java.security.NoSuchAlgorithmException
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import java.util.*
import javax.net.ssl.*
import kotlin.collections.ArrayList


private const val BASE_URL =
    "https://sandbox.api.visa.com/"

interface RehaApiService {

    @POST("dcas/cardservices/v1/cards")
    suspend fun createCardId(@Body body: CardActivationBody): CreateCardIdResponse


    //here we shall implement the calls to the reha api
//    @POST("/card/{cardId}/cardactivation")
//    fun cardActivation(@Path("cardId") cardId: String, @Body user: User?): Call<User?>?

}


object RehaApi {

    fun createApi(application: Application): RehaApiService {
        val okHttpClient = getSSLClient(application.applicationContext)

        val moshi = Moshi.Builder()
            .add(KotlinJsonAdapterFactory())
            .build()

        val retrofit = Retrofit.Builder()
            .client(okHttpClient)
            .baseUrl(BASE_URL)
            .addConverterFactory(MoshiConverterFactory.create(moshi))
            .build()

        val rehaApiService = retrofit.create(RehaApiService::class.java)
        return rehaApiService

    }


}

@Throws(
    NoSuchAlgorithmException::class,
    KeyStoreException::class,
    KeyManagementException::class,
    CertificateException::class,
    IOException::class
)
fun getSSLClient(context: Context): OkHttpClient? {
    val client: OkHttpClient
    val sslSocketFactory: SSLSocketFactory
    val trustManagers: Array<TrustManager>
    val trustManager: X509TrustManager

    val trustManagerFactory: TrustManagerFactory =
        TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
    trustManagerFactory.init(readKeyStore(context))
    trustManagers = trustManagerFactory.trustManagers
    check(!(trustManagers.size != 1 || trustManagers[0] !is X509TrustManager)) {
        "Unexpected default trust managers:" + Arrays.toString(
            trustManagers
        )
    }
    trustManager = trustManagers[0] as X509TrustManager

    val sslContext: SSLContext = SSLContext.getInstance("TLS")
    sslContext.init(null, arrayOf<TrustManager>(trustManager), null)
    sslSocketFactory = sslContext.socketFactory
    client = OkHttpClient.Builder()
        .sslSocketFactory(sslSocketFactory, trustManager)
        .addInterceptor(SupportInterceptor())
        .addInterceptor(
            AuthenticationInterceptor(
                "<base64_encoded_username>",
                "<base64_encoded_password>"
            )
        )
        .build()
    return client
}

/**
 * Get keys store. Key file should be encrypted with pkcs12 standard. It    can be done with standalone encrypting java applications like "keytool". File password is also required.
 *
 * @param context Activity or some other context.
 * @return Keys store.
 * @throws KeyStoreException
 * @throws CertificateException
 * @throws NoSuchAlgorithmException
 * @throws IOException
 */
@Throws(
    KeyStoreException::class,
    CertificateException::class,
    NoSuchAlgorithmException::class,
    IOException::class
)

private fun readKeyStore(context: Context): KeyStore? {   

    val privateKeyClientCertificateBundleInputStream: InputStream =
        BufferedInputStream(context.resources.openRawResource(R.raw.rehacertkeybundle1))

    val keyStore: KeyStore = KeyStore.getInstance("pkcs12")

    try {

        keyStore.load(privateKeyClientCertificateBundleInputStream,<p12_passwd>.toCharArray())

    } catch (e: Exception) {
        var x = e
        e.printStackTrace()
    } finally {
        if (privateKeyClientCertificateBundleInputStream != null) {
            privateKeyClientCertificateBundleInputStream.close()
        }
    }

    return keyStore
}


class SupportInterceptor : Interceptor {

    /**
     * Interceptor class for setting of the headers for every request
     */
    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        request = request.newBuilder()
            .addHeader("Accept", "application/json")
            .build()
        return chain.proceed(request)
    }
}

class AuthenticationInterceptor(user: String, password: String) : Interceptor {

    private val credentials: String = Credentials.basic(user, password)

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val authenticatedRequest = request.newBuilder()
            .header("Authorization", credentials).build()
        return chain.proceed(authenticatedRequest)
    }
}

标签: androidsslkotlinokhttp

解决方案


推荐阅读