android - 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)
}
}
解决方案
推荐阅读
- ios - 尝试从文本字段中获取纬度和经度时无法转换类型的值
- mysql - 我们如何在 MySQL 中加入公用表?
- python - 对所用参数的 lambda 解释
- go - 发生了什么,我的代码在 Go Lang 中解析 XML 后无法显示结果?
- javascript - 来自json http请求的离子显示chartjs数据
- python-3.x - TensorFlow,Keras - 类型错误:传递给参数“标签”的值的数据类型 float32 不在允许值列表中:int32、int64
- qt - 如果在 QT CLI 应用程序中使用 GUI 类怎么样?
- java - 使用有限数量的连接和 Camel SFTP 传输文件
- python - Python 通过 txt 文件存储和访问信息
- python - 在标记推文时忽略“️”