数字证书及其在安全测试中的应用

做Web层面的安全测试,免不了要做中间人代理来截包分析。常用的工具有BurpSuit,Fiddker,Charles等等。关于这些工具的用法网上已经有很多介绍,这里就不赘述了。然而在测试一些安全性高的站点时,往往会遇到SSL通信的问题。这里对这些数字证书的问题进行一个小结,欢迎拍砖交流。

0x1 数字证书

数字证书主要在互联网上的用于身份验证的用途。 安全站点在获得CA(Certificate Authority)认证后,获得一个数字证书,以此来标识其合法身份的真实性。
数字证书的格式遵循X.509标准。(X.509是由国际电信联盟(ITU-T)制定的数字证书标准。它设定了一系列严格的CA分级体系来颁发数字证书。)
数字证书主要分为服务器证书和客户端证书。服务器证书(SSL证书)用来进行身份验证和通信的加密,客户端证书主要用于身份验证和电子签名。下面对数字证书的组成结构和工作原理进行一个简单的介绍。
最简单的证书包含了:
1. 证书内容
  a) 证书所有者的公钥
  b) 颁发者信息
  c) 使用者信息
  d) 等等
2. CA的数字签名
(CA使用私钥对证书内容的报文摘要进行加密后的结果)
3. 签名算法

真实的数字证书主要结构如下图:
kingx
其中证书内容字段的详细结构如下:
kingx
CA的数字签名是CA用其私钥对证书信息的HASH值加密后的结果。由于使用私钥加密以及非对称加密算法的特性,数字签名具有不可伪造的特点,同时报文摘要也保证了证书信息的完整性。用户可以使用CA的公钥对数字证书中CA的数字签名进行解密来判断该数字证书信息的真实性。
客户端校验服务器证书的大致流程如下:


1. 查看证书是否过期
2. CA是否可靠
3. CA的公钥能否正确解开服务器证书的CA数字签名,即证书的签名值
4. 服务器证书上的域名是否和服务器的实际域名相匹配

其中CA的根证书大多是由操作系统预装好并将它们设置为受信任的证书。所谓根证书就是未被签名的公钥证书或自签名的证书,是CA给自己颁发的证书,包含了CA的公钥等信息。安装某个CA的根证书,就表示信任这个CA,它是证书信任链的开始。国际著名的CA有:verySign,Batltimore,Entrust等。
中国金融认证中心(China Financial Certification Authority,简称CFCA)是经中国人民银行和国家信息安全管理机构批准成立的国家级权威安全认证机构,是国家重要的金融信息安全基础设施之一。

Tips:
某些浏览器内置的CA证书列表:
https://www.mozilla.org/en-US/about/governance/policies/security-group/certs/included/

0x2 证书格式

常见的证书文件有以下格式:

格式 扩展名
DER .cer .crt .rsa
PKCS7 .p7b .p7r
CMS .p7c .p7m .p7s
PEM .pem (ASCII文件 一般使用base64编码)
PKCS10 .p10 .csr (ASCII文件)
SPC .pvk .spc

常见的密钥库(keystore)文件格式如下:
格式 扩展名
JKS .jks .ks
JCEKS .jce
PKCS12 .p12
BKS .bks
UBER .ubr

0x3 代理软件的数字证书

BurpSuit和Fiddler等代理软件可以用来拦截HTTPS流量。它们使用自带的一个SSL证书与客户端进行交互,充当中间人的角色去转发数据包。由于这些代理软件的证书是自签名的根证书,而未经过知名CA机构认证,所以默认是不受信任,浏览器会进行警告阻拦。
使用桌面浏览器测试时,可以通过添加例外的方式信任它们的证书。在移动端测试时,也可以导出这些代理软件的证书,再将其安装到移动设备,方便对移动应用的HTTPS流量进行代理。
直接下载Burp证书,访问BurpSuit的代理端口,如:127.0.0.1:8080可以直接点击右上角的CA Certificate按钮并下载根证书。
在浏览器证书管理器中查看BurpSuit证书:
kingx
Fiddler选项中可以直接导出证书:
kingx
kingx

0x4 移动应用测试中的数字证书

直接安装
代理软件的数字证书可以安装到手机系统中,方便对走HTTPS流量的APP进行安全测试。
安卓系统中的服务器证书安装方法如下:
将数字证书拷贝到安卓的sdcard目录下。在设置->安全->选择从SDCARD安装证书。
kingx
kingx
IOS中也是类似的操作。

将要安装的证书放在网站目录下,从Safari中打开证书URL进行安装。
注:从10.3系统后,需要在手机设置-通用-关于本机-受信任证书存储区里面,找到安装的证书,选择信任

导入到密钥库
除了直接安装代理软件证书之外,有的app会自带受其信任的密钥库,如下:
在/asserts资源文件夹下,存在几个bks密钥库文件。
kingx
可以使用工具打开bks密钥库,并将代理软件的证书添加导入进去并保存。
kingx
当然,也可以使用keytool命令行进行证书导入。

Tips:
Android系统中CA证书文件的位置在:/system/etc/security/cacerts.bks

修改检测代码
有的APP还会直接在代码中进行对服务器证书进行校验。
可以逆向app得到smali代码,修改验证逻辑,再重编译打包,从而绕过对服务器证书的验证。
kingx
具体案例可以看这篇博客:http://cih.so/?p=476

0x5 关于客户端证书

除了服务端证书外,有的网银等高安全性的系统还会要求用户提供客户端证书进行身份验证。

客户端证书主要用作身份验证和电子签名的作用。一般网银包含私钥的客户端证书都被存储在USBKEY中,存储在USBKEY中包含私钥的证书不能被导出和复制。可以使用USBKEY的证书管理工具安装USBKEY中的证书,然后在浏览器中导出其公钥证书,并在代理软件中设置该客户端证书。浏览器中的导出方法如下:
kingx
测试过程中,如果发现服务器会检测用户的客户端证书,并且没有设置客户端证书,Fiddler会提示如下信息:
kingx
可以将导出的客户端公钥证书放到如上所示的目录中。

BurpSuit也可以设置客户端证书:
kingx
除了保存在USBKEY中的用户证书(客户端证书),CFCA也提供软证书的下载,也就是包含私钥的证书文件。在CFCA下载证书时,选择Microsoft Enhanced Cryptographic Provider v1.0
kingx
将软证书安装到浏览器后,也可以通过同样的方法导出公钥证书。另外还可以导出包含私钥的证书,导出时需要设置密码。在Fiddler代理中设置客户端的公钥证书后,就可以直接进行代理了。

0x6 关于安卓APP签名

安卓app签名的数字证书不需要权威机构来认证,是开发者自己产生的自签名数字证书。数字证书都是有有效期的,Android只是在应用程序安装的时候才会检查证书的有效期。如果程序已经安装在系统中,即使证书过期也不会影响程序的正常功能。
Android将数字证书用来标识应用程序的作者、在应用程序之间建立信任关系,而不是用来决定最终用户可以安装哪些应用程序。
由于恶意开发者可能通过使用相同的包名来混淆替换已经安装的程序,签名可以保证相当名字但是签名不同的app不被替换。
另外,在申请一些特别权限的时候,需要验证时会使用签名。
Eclipse调试编译app时,是使用debug证书对apk签名的。安装adt开发插件后的eclipse可以直接使用android tools图形化界面对apk进行签名。
kingx
签名之后会多出一个META-INF文件夹
/CERT.RSA (包含证书信息)
/CERT.SF
/MANIFEST.MF

查看安卓签名信息
keytool -printcert -file CERT.RSA
kingx

0x7 验证SSL证书

可以通过设置SSLSocketFactory(keyStore)的方式对证书进行验证,keyStore中可存放多个受信任的证书。
也可以继承一个SSLSocketFactory,http://drops.wooyun.org/tips/3296
读取证书
[java]
InputStream cert //读取了cer证书文件的文件流
CertificateFactory cf = CertificateFactory.getInstance("X.509");
caInput = new BuffererInputStream(cert);
Certificate ca = cf.generateCertificate(caInput);
[/java]
生成KeyStore
[java]
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null,null);
keyStore.setCertificateEntry("ca",ca); //keystore中增加一个证书
[/java]
实例化SSLSocketFactory
[java]
new SSLSocketFactory(keystore)
[/java]

设置SSLSocketFactory
[java]
httpclient.getConnectionManager().getSchemeRegistry().register(new Scheme("https",sslSocketFactory,443));
[/java]

本文已发表于乌云知识库:http://drops.wooyun.org/tips/2775

Leave a comment

Your email address will not be published. Required fields are marked *