技术标签: java  android  http  https  ssl  

Security and privacy are some of the most difficult tasks for any Android developer and it’s obvious because Android is an open-source platform and everyone knows how it works.


In this article, we’re going to deal with secure communication in Android, mainly between client and server.

在本文中,我们将处理Android中的安全通信,主要是客户端之间的通信 和服务器。

介绍 (Introduction)

Currently, the most common architecture of web services is REST-based on HTTP. The best protection method for this model of communication is the TLS/SSL standard.

当前,最常见的Web服务体系结构是基于HTTP的REST。 这种通信模型的最佳保护方法是TLS / SSL标准。

It can be combined with the HTTP protocol to create an encrypted variant called HTTPS. HTTPS ensures safe, encrypted communication between apps and server.

可以将其与HTTP协议结合以创建称为HTTPS的加密变体。 HTTPS确保应用程序和服务器之间的安全加密通信。

问题 (Problem)

It’s common for developers to implement networking calls over HTTPS, but not properly.


This can be solved by replacing the protocol name from HTTP to HTTPS in the URL. This will provide security to a certain extent by enabling TLS/SSL encryption by default (only if the server supports it).

可以通过将协议名称从HTTP替换为URL中的HTTPS来解决。 通过默认启用TLS / SSL加密(仅在服务器支持的情况下),这将在一定程度上提供安全性。

However, this is not good enough to keep your data secure. Simply replacing the protocol enables the encryption, but the app will trust every certificate issued by the server.

但是,这不足以确保数据安全。 只需替换协议即可启用加密,但是应用程序将信任服务器颁发的每个证书。

This means that the hacker can create fake certificates. The certificates will then allow the hacker to intercept encrypted communication which is well-known as a man-in-the-middle attack.

这意味着黑客可以创建伪造的证书。 然后,证书将允许黑客拦截加密的通信,这被称为中间人攻击。

It is the main reason why you should spend more time and effort to implement an HTTPS configuration correctly.



To avoid this threat, we should implement certificate pinning.


To do this, we need a server certificate with a fingerprint. We will compare the remote server certificate with the fingerprint while making the connection.

为此,我们需要带有指纹的服务器证书。 建立连接时,我们将比较远程服务器证书和指纹。

If they are identical, then it is a secure connection, otherwise, you should not do any data transfer as the connection is compromised.


There are three ways to implement certificate pinning in Android:


  1. Network security configuration.

  2. TrustManager.

  3. OkHttp and certificate pinning.


网络安全配置 (Network Security Configuration)

This is one of the easiest ways and the native way to do certificate pinning in Android.


Unlike the other two methods, this configuration requires no coding but network security configuration has one flaw: it only supports Android N and above.

与其他两种方法不同,此配置不需要编码,但是网络安全配置有一个缺陷:它仅支持Android N及更高版本。

The network security configuration feature lets apps customize their network security settings in a safe, declarative configuration file without modifying app code. These settings can be configured for specific domains and a specific app.

网络安全配置功能使应用程序可以在安全的声明性配置文件中自定义其网络安全设置,而无需修改应用程序代码。 可以为特定域和特定应用程序配置这些设置。

Network security configuration uses an XML file which has to be created under the res\xml directory and we need to declare this XML file in the manifest as shown below:


<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application android:networkSecurityConfig="@xml/network_security_config"
... >

Now that we know how to create a network security file, it’s time to configure it.


Here, we have two main tags, <base-config> and <domain-config>.

在这里,我们有两个主要标签, <base-config><domain-config>

  • <base-config> is used to declare things or configurations that should be applied to the entire app, regardless of which domain holds the connection.


  • On the other hand, <domain-config> is used to configure specific rules to only certain domains of your choice.

    另一方面, <domain-config>用于将特定规则配置为仅对您选择的某些域进行配置。

Have a look:


<?xml version="1.0" encoding="utf-8"?>
    <base-config  cleartextTrafficPermitted="false">
            <certificates src="system"/>
        <domain includeSubdomains="true">secure.example.com</domain>
            <certificates src="@raw/crtf"/>

Here, we used the <base-config> tag to disable clear-text traffic which means only HTTPS service calls will happen throughout the app and also mentions to use system certificates for networking.


But, coming to <domain-config>, we’ve mentioned a specific domain and configured certain rules for it, like only use the certificate file in the res/raw directory to make a network connection with the “secure.example.com” domain.

但是,在<domain-config> ,我们提到了一个特定的域并为其配置了某些规则,例如仅使用res/raw目录中的证书文件与“ secure.example.com ”建立网络连接。域。

Now that we know how to make use of the certificate, it’s time we use certificate pinning. Here, we use the <pin-set> tag to configure a certificate with a particular pin as shown below.

现在我们知道了如何使用证书,是时候使用证书固定了。 在这里,我们使用<pin-set>标记配置具有特定引脚的证书,如下所示。

<?xml version="1.0" encoding="utf-8"?>
        <domain includeSubdomains="true">sampleexample.com</domain>
        <pin-set expiration="2018-01-01">
            <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
            <!-- backup pin -->
            <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>

信托管理人 (TrustManager)

This is one of the oldest methods to implement certificate pinning in Android.


TrustManager is responsible for deciding if the app should allow credentials given by the peer or not. This technique is from the javax.net.ssl package and we used it here to implement certificate pinning.

TrustManager负责确定应用程序是否应允许对等方提供的凭据。 该技术来自javax.net.ssl包,我们在这里使用它来实现证书固定。

第1步 (Step 1)

Add your certificate file in the res/raw directory. It would be preferable if the certificate is in PEM or DER format without any comment lines in it.

将证书文件添加到res/raw目录中。 如果证书为PEM或DER格式,且其中没有任何注释行,则将是更可取的。

第2步 (Step 2)

Initialize the KeyStore with a certificate as shown below:


val resource_stream = resources.openRawResource(R.raw.cert)
val key_store_type = KeyStore.getDefaultType()
val key_store = KeyStore.getInstance(key_store_type)

key_store.load(resource_stream, null)

第三步 (Step 3)

Now that we have the certificate instance it’s time to initialize TrustManager.


val trust_manager_algorithm = TrustManagerFactory.getDefaultAlgorithm()
val trust_manager_factory = TrustManagerFactory.getInstance(trust_manager_algorithm)


第4步 (Step 4)

Now that we have the certificate and trust manager instances, let’s complete the final step by creating the SSL context with TLS protocol and then create a secure SSL connection with the TrustManager.


val ssl_context = SSLContext.getInstance("TLS")
ssl_context.init(null, trust_manager_factory.trustManagers, null)
val url = URL("http://www.secureexample.com/")
val url_connection = url.openConnection() as HttpsURLConnection
url_connection.sslSocketFactory = ssl_context.socketFactory

OkHttp和证书固定 (OkHttp and Certificate Pinning)

OkHttp is a very famous networking library from Square. Retrofit uses OkHttp for networking. The Okhttp team has made it very simple to implement certificate pinning.

OkHttp是来自Square的非常著名的网络库。 改造使用OkHttp进行联网。 Okhttp团队使实现证书固定变得非常简单。

First, we need to create a certificate pinner instance from the dedicated OkHttp CertificatePinner builder and then we add a domain and corresponding fingerprint to it.

首先,我们需要从专用的OkHttp CertificatePinner构建器创建证书固定器实例,然后向其添加域和相应的指纹。

Finally, add the builder to the OkHttp client. Have a look:

最后,将构建器添加到OkHttp客户端。 看一看:

val certificatePinner = CertificatePinner.Builder()

val okHttpClient = OkHttpClient.Builder()

We can also add multiple fingerprints to the builder. This will be helpful to add additional fingerprints if the present one is going to expire. We can also import the certificate files to the resources folder, as shown in the TrustManager case.

我们还可以向构建器添加多个指纹。 如果当前的指纹即将过期,这将有助于添加其他指纹。 我们也可以将证书文件导入到资源文件夹,如TrustManager案例所示。

Now, you need to manually write a class that will extract the fingerprint from the file. You can also use the Peer certificate extractor to extract fingerprints.

现在,您需要手动编写一个类,该类将从文件中提取指纹。 您还可以使用对等证书提取器提取指纹。

It’s definitely not recommended to mention the fingerprints statically in the code. Mention them in the Gradle file as a build-config field.

绝对不建议在代码中静态提及指纹。 在Gradle文件中将它们作为build-config字段提及。

证书简介 (Briefly About Certificates)

There are almost 138 certificate authorities that are accepted by the Android ecosystem and the count increases every day.


You can add your self-signed, leaf, intermediate, or root certificate. Let me explain these certificates a bit more so that you’ll have a good idea of what they are.

您可以添加自签名证书,叶证书,中间证书或根证书。 让我进一步解释这些证书,以便您对它们的含义有所了解。

树叶证书 (Leaf Certificate)

By using a leaf certificate you are making it 100% sure that this is your certificate exactly, and you are establishing a secure connection.


Leaf certificates have a very short expiry time so you need to push the update to your app to make sure of the connectivity. It’s highly recommended to use back-up keys.

叶子证书的有效时间非常短,因此您需要将更新推送到您的应用程序以确保连接性。 强烈建议使用备份密钥。

中级证书 (Intermediate Certificate)

By using an intermediate certificate you’re depending on the intermediate certificate authority.


This method has an advantage. As long as you stick to the same certificate provider, then any changes to your leaf certificates will work without having to update your app. Using an intermediate certificate is secure only when your provider is trustworthy.

该方法具有优势。 只要您坚持使用同一证书提供者,那么对叶子证书的任何更改都将起作用,而无需更新您的应用程序。 仅在您的提供商可信赖的情况下,使用中间证书才是安全的。

根证书 (Root Certificate)

By using the root certificate, you’re depending on all of the intermediate certificates approved by the root certificate authority. If any of the intermediate certificates are compromised then there are chances for your app to be cracked by hackers.

通过使用根证书,您将依赖于由根证书颁发机构批准的所有中间证书。 如果任何中间证书被盗用,那么您的应用就有可能被黑客破解。

结论 (Conclusion)

My suggestion of using OkHttp with certificate pinning is the best way to go.


Although many of us prefer native network security configurations, as I said, it only supports Android N and above devices. There will be no complete protection with the native methods, yet.

尽管我们许多人都喜欢本机网络安全配置,但正如我所说,它仅支持Android N及更高版本的设备。 本地方法还没有完全的保护。

Hopefully, in a year or two, the minimum android version will reach Android N and then we can use the native security configuration.

希望在一两年内,最低的android版本将达到Android N,然后我们可以使用本机安全配置。

Thank you for reading.


翻译自: https://medium.com/better-programming/secure-communication-with-the-server-from-your-android-client-with-certificate-pinning-5f53cea55972

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。



面试是公司选择你,Offer 是你选择公司。程序员这个职业在初期,门槛比较低,快速掌握一门编程语言,在工作中能参与有挑战且持续的项目,加入有活力和学习氛围的团队,初级程序员很快就能成长为高级工程师。这也是很多老程序员的焦虑所在,一旦自己稍微松懈一点,没能及时迭代自己的能力模型,那行业内不断涌入的新人就可能对自己形成威胁,无论是技术水平,还是充沛的体力和精力,新人都充满竞争力,并且,企业需要付给...


var listData = [ {id:"1234",name:"bob"}, {id:"2451",name:"mary"}, {id:"6666",name:"tom"}, {id:"5675",name:"jerry"}, {id:"8421",name:"ken"},]let index = listData.findIndex( item...


一、概述在APP的开发过程中,可能需要利用手势操作去实现一些功能或者效果。UIKit框架提供了检测常见手势的预定义手势识别器。 在手势开发上,最好尽可能使用预定义的手势识别器,因为它们的简单性减少了我们的的代码量。当然,我们也可以自定义一些特殊的手势,具体的可以查看官方文档学习:Creating a Custom Gesture Recognizer。iOS中的事件可以分为3大类型:触

Untiy-Resources 加载图片_unity resources加载图片_zebintang的博客-程序员宅基地

一开始以为 将图片导入Unity时, 将其 图片转为sprite 以为就可以直接 load为sprite了,可是 一直报null异常原来是 加载后Debug出来是这个类型因为 加载的时候 是Texture2D类型,而我硬生生将其 转为 sprite,难怪会报异常了,其实 用 Load方法的话,一般会Loade第一个,而不会load子物体(sprite),然后sprit...



CSS Sprites:鱼翅还是三鹿?_叶ch楼喽的博客-程序员宅基地

原文链接无处不在的 CSS sptites - 为数不多的几个可以直接跳过”流行”这个过程,而可以马上并且牢牢地跻身于最佳 CSS 实践之中的几个技术之一。虽然它真正流行是在 A List Apart 解释并认可这个技术之后,但是早在 2003 年 7 月份,Peter Stanicek 就已经开始谈论它了。目前大多数的开发人员对这个技术都有相当地掌握,也有很多关于它的教程和文


用jquery获取form表单值的方法总结_jq from.get_gogiqp_jyh的博客-程序员宅基地

用jquery获取form表单值的方法总结 用jquery获取form表单值的方法总结: jquery获取radio单选按钮的值 $("input[name='items']:checked").val(); jquery radio取值,checkbox取值,select取值,radio选中,checkbox选中,select选中,及其相关 获取一组radio被选中项的值 var item

AttributeError: module ‘tensorflow.contrib‘ has no attribute ‘l2_regularizer‘_attributeerror: module 'tensorflow.keras.layers' h_Wanderer001的博客-程序员宅基地



获取脑图方式请看最下面!JVM类文件(Class文件)结构ClassFile {u4 magic;u2 minor_version;u2 major_version;u2 constant_pool_count;cp_info constant_pool[constant_pool_count-1];u2 access_flags;u2

org.dom4j.DocumentException: 2 字节的 UTF-8 序列的字节 2 无效。 Nested exception: 2 字节的 UTF-8 序列的字节 2 无效。异常解决办法_程序员小刘的博客-程序员宅基地

本人最近因为自身需求,开始接触了XML技术,利用dom4j做一个对XML读写操作的时候,给原XML添加一条数据时遇到了这个错误。下面分享一下自己解决的心路历程吧。这是xml文件的内容:            张三        20        男        杭州        001                李四



使用fastjson时出现'Content-Type' cannot contain wildcard type '*'报错_FenG·的博客-程序员宅基地

在springmvc配置文件里添加:&amp;lt;mvc:annotation-driven&amp;gt; &amp;lt;mvc:message-converters&amp;gt; &amp;lt;bean class=&quot;com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter&quot;&amp;gt; ...