是否可以将 DownloadManager.Request 限制为特定的服务器证书?
Is it possible to restrict DownloadManager.Request to a specific server certificate?
作为提高应用程序安全性工作的一部分,我想保护我的客户端免受“中间人”攻击。
我有一个常见的用例,我的应用程序从 CDN 服务器下载大文件(10-50 兆)。为此 - 我正在使用系统的 DownloadMnager
有没有办法通过 API 设置任何特定的 TrustManager
或特定的服务器证书密钥?有没有其他方法可以将请求固定到特定的受信任服务器?
看起来没有这样的 API,但如果真的是这样的话我会感到惊讶,因为 GooglePlay 使用系统的下载管理器来下载 apk 然后安装它们...
作为全系统可用的 API,我怀疑是否有任何可能的方法可以将 DownloadManager
限制为特定的服务器证书。针对您提到的示例,Google-Play 很可能正在通过观察下载完成来安装下载的 APK。
但如果我的理解是正确的,你可以通过使用Retrofit library's file-download method as discussed in 来实现你的目标,而证书固定可以通过使用以下SelfSigningClientBuilder
class来构建改装客户端:
SelfSigningClientBuilder.kt
import android.content.Context
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.IOException
import java.security.*
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import java.util.*
import java.util.concurrent.TimeUnit
import javax.net.ssl.*
object SelfSigningClientBuilder {
private const val NET_TIMEOUT_READ = 80L
private const val NET_TIMEOUT_WRITE = 120L
private const val NET_TIMEOUT_CONNECT = 75L
@JvmStatic
fun createClient(context: Context, isCertificateNeeded: Boolean = true): OkHttpClient {
val interceptor = getInterceptor()
if (isCertificateNeeded)
try {
val cf = CertificateFactory.getInstance("X.509")
// assuming the CA certificate is put inside res/raw folder named as ca_cert.pem
val cert = context.resources.openRawResource(R.raw.ca_cert)
val ca = cf?.generateCertificate(cert)
cert.close()
val keyStoreType = KeyStore.getDefaultType()
val keyStore = KeyStore.getInstance(keyStoreType)
keyStore.load(null, null)
keyStore.setCertificateEntry("ca", ca)
val tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
val tmf = TrustManagerFactory.getInstance(tmfAlgorithm)
tmf.init(keyStore)
val trustManagers = tmf.trustManagers
if (trustManagers.size != 1 || trustManagers[0] !is X509TrustManager) {
throw IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers))
}
val trustManager = trustManagers[0] as X509TrustManager
val sslContext = SSLContext.getInstance("SSL")
sslContext!!.init(null, trustManagers, null)
return OkHttpClient.Builder()
.sslSocketFactory(sslContext.socketFactory, trustManager)
.readTimeout(NET_TIMEOUT_READ, TimeUnit.SECONDS)
.writeTimeout(NET_TIMEOUT_WRITE, TimeUnit.SECONDS)
.connectTimeout(NET_TIMEOUT_CONNECT, TimeUnit.SECONDS)
// .retryOnConnectionFailure(true)
.addInterceptor(interceptor)
.build()
} catch (e: KeyStoreException) {
e.printStackTrace()
} catch (e: CertificateException) {
e.printStackTrace()
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: KeyManagementException) {
e.printStackTrace()
}
return OkHttpClient.Builder()
.readTimeout(NET_TIMEOUT_READ, TimeUnit.SECONDS)
.writeTimeout(NET_TIMEOUT_WRITE, TimeUnit.SECONDS)
.connectTimeout(NET_TIMEOUT_CONNECT, TimeUnit.SECONDS)
.addInterceptor(interceptor)
.build()
}
private fun getInterceptor(): Interceptor {
return Interceptor { chain ->
val originalRequest = chain.request()
val request: Request = originalRequest.newBuilder()
.header("custom-header", "my-header-value")
.method(originalRequest.method(), originalRequest.body())
.build()
chain.proceed(request)
}
}
}
然后按照这里的代码段构建Retrofit-client,然后用它来下载文件:
val retrofit = Retrofit.Builder()
.client(SelfSigningClientBuilder.createClient(context, true)
.baseUrl("https://yourdomain.com/")
.build()
在使用这个改装客户端时,我已经通过 Wireshark, Burp-suite, and Charles Proxy 解析了我所有的 REST API 请求 - none 其中可以显示实际的请求文本而不是一些乱码数据。所以,我希望您的文件内容在执行此过程时能够免受 MITM 攻击。
作为提高应用程序安全性工作的一部分,我想保护我的客户端免受“中间人”攻击。
我有一个常见的用例,我的应用程序从 CDN 服务器下载大文件(10-50 兆)。为此 - 我正在使用系统的 DownloadMnager
有没有办法通过 API 设置任何特定的 TrustManager
或特定的服务器证书密钥?有没有其他方法可以将请求固定到特定的受信任服务器?
看起来没有这样的 API,但如果真的是这样的话我会感到惊讶,因为 GooglePlay 使用系统的下载管理器来下载 apk 然后安装它们...
作为全系统可用的 API,我怀疑是否有任何可能的方法可以将 DownloadManager
限制为特定的服务器证书。针对您提到的示例,Google-Play 很可能正在通过观察下载完成来安装下载的 APK。
但如果我的理解是正确的,你可以通过使用Retrofit library's file-download method as discussed in SelfSigningClientBuilder
class来构建改装客户端:
SelfSigningClientBuilder.kt
import android.content.Context
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.IOException
import java.security.*
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import java.util.*
import java.util.concurrent.TimeUnit
import javax.net.ssl.*
object SelfSigningClientBuilder {
private const val NET_TIMEOUT_READ = 80L
private const val NET_TIMEOUT_WRITE = 120L
private const val NET_TIMEOUT_CONNECT = 75L
@JvmStatic
fun createClient(context: Context, isCertificateNeeded: Boolean = true): OkHttpClient {
val interceptor = getInterceptor()
if (isCertificateNeeded)
try {
val cf = CertificateFactory.getInstance("X.509")
// assuming the CA certificate is put inside res/raw folder named as ca_cert.pem
val cert = context.resources.openRawResource(R.raw.ca_cert)
val ca = cf?.generateCertificate(cert)
cert.close()
val keyStoreType = KeyStore.getDefaultType()
val keyStore = KeyStore.getInstance(keyStoreType)
keyStore.load(null, null)
keyStore.setCertificateEntry("ca", ca)
val tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
val tmf = TrustManagerFactory.getInstance(tmfAlgorithm)
tmf.init(keyStore)
val trustManagers = tmf.trustManagers
if (trustManagers.size != 1 || trustManagers[0] !is X509TrustManager) {
throw IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers))
}
val trustManager = trustManagers[0] as X509TrustManager
val sslContext = SSLContext.getInstance("SSL")
sslContext!!.init(null, trustManagers, null)
return OkHttpClient.Builder()
.sslSocketFactory(sslContext.socketFactory, trustManager)
.readTimeout(NET_TIMEOUT_READ, TimeUnit.SECONDS)
.writeTimeout(NET_TIMEOUT_WRITE, TimeUnit.SECONDS)
.connectTimeout(NET_TIMEOUT_CONNECT, TimeUnit.SECONDS)
// .retryOnConnectionFailure(true)
.addInterceptor(interceptor)
.build()
} catch (e: KeyStoreException) {
e.printStackTrace()
} catch (e: CertificateException) {
e.printStackTrace()
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: KeyManagementException) {
e.printStackTrace()
}
return OkHttpClient.Builder()
.readTimeout(NET_TIMEOUT_READ, TimeUnit.SECONDS)
.writeTimeout(NET_TIMEOUT_WRITE, TimeUnit.SECONDS)
.connectTimeout(NET_TIMEOUT_CONNECT, TimeUnit.SECONDS)
.addInterceptor(interceptor)
.build()
}
private fun getInterceptor(): Interceptor {
return Interceptor { chain ->
val originalRequest = chain.request()
val request: Request = originalRequest.newBuilder()
.header("custom-header", "my-header-value")
.method(originalRequest.method(), originalRequest.body())
.build()
chain.proceed(request)
}
}
}
然后按照这里的代码段构建Retrofit-client,然后用它来下载文件:
val retrofit = Retrofit.Builder()
.client(SelfSigningClientBuilder.createClient(context, true)
.baseUrl("https://yourdomain.com/")
.build()
在使用这个改装客户端时,我已经通过 Wireshark, Burp-suite, and Charles Proxy 解析了我所有的 REST API 请求 - none 其中可以显示实际的请求文本而不是一些乱码数据。所以,我希望您的文件内容在执行此过程时能够免受 MITM 攻击。