目录
- 数字签名
- 数字证书
- Https 编程
数字签名
数字签名是非对称加密与数字摘要的组合应用
应用场景
- 校验用户身份(使用私钥签名,公钥校验,只要用公钥能校验通过,则该信息一定是私钥持有者发布的)
- 校验数据的完整性(用解密后的消息摘要跟原文的消息摘要进行对比)
签名过程
“发送报文时,发送方用一个哈希函数从报文文本中生成报文摘要,然后用自己的私人密钥对这个摘要进行加密,这个加密后的摘要将作为报文的数字签名和报文一起发送给接收方,接收方首先用与发送方一样的哈希函数从接收到的原始报文中计算出报文摘要,接着再用发送方的公用密钥来对报文附加的数字签名进行解密,如果这两个摘要相同、那么接收方就能确认该数字签名是发送方的。
数字签名有两种功效:一是能确定消息确实是由发送方签名并发出来的,因为别人假冒不了发送方的签名。二是数字签名能确定消息的完整性。因为数字签名的特点是它代表了文件的特征,文件如果发生改变,数字摘要的值也将发生变化。不同的文件将得到不同的数字摘要。一次数字签名涉及到一个哈希函数、发送者的公钥、发送者的私钥。
使用步骤
1. //获取signature 对象,初始化算法:MD2withRSA, MD5withRSA, or SHA1withRSA
2. Signature signature = Signature.getInstance("MD5withRSA");
3. //创建私钥(从磁盘上读取)
4. PrivateKey privateKey = (PrivateKey)SerializableUtil.readObject(
5. "haha.privateKey");
6. //使用私钥进行初始化
7. signature.initSign(privateKey);
8. //传入需要签名的数据
9. signature.update(content.getBytes());
10. //执行签名
11. byte[] sign = signature.sign();
12.
13. //创建公钥(从磁盘上读取)
14. PublicKey publicKey = (PublicKey) SerializableUtil.readObject(
15. "haha.publicKey");
16. //使用公钥进行初始化
17. signature.initVerify(publicKey);
18. //传入需要校验的数据(即上面的原文)
19. signature.update(content.getBytes());
20. //执行校验
21. boolean verify = signature.verify(sign);
小结
数字签名一般不单独使用,基本都是用在数字证书里实现SSL 通信协议。下面将学习的数字证书就是基于数字签名技术实现的。
数字证书
数字证书就是互联网通讯中标志通讯各方身份信息的一串数字,提供了一种在Internet 上验证通信实体身份的方式,数字证书不是数字身份证,而是身份认证机构盖在数字身份证上的一个章或印(或者说加在数字身份证上的一个签名)。它是由权威机构——CA 机构,又称为证书授权(Certificate Authority)中心发行的,人们可以在网上用它来识别对方的身份。
应用场景
1,交易者身份的确定性、不可否认性、不可修改性
2,对应用进行签名认证(例如Android 的apk)
数字证书格式
数字证书的格式普遍采用的是X.509V3 国际标准,一个标准的X.509 数字证书包含以下一些内容:
证书的版本信息;
证书的序列号,每个证书都有一个唯一的证书序列号;
证书所使用的签名算法;
证书的发行机构名称,命名规则一般采用X.500 格式;
证书的有效期,通用的证书一般采用UTC 时间格式,它的计时范围为1950-2049;
证书所有人的名称,命名规则一般采用X.500 格式;
证书所有人的公开密钥;
证书发行者对证书的签名。
数字证书原理
数字证书是安全领域里的终极武器,SSL 通信协议里最核心的东西就是数字证书。他涉及到前面提到的所有知识:对称加密、非对称加密、消息摘要、数字签名等。数字证书可以通过java 自带的KeyTool 工具生成,生成后的数字证书一般保管在KeyStore 里。KeyStore可以叫做秘钥仓库。
秘钥仓库可以保管3 种类型的数据:KeyStore.PrivateKeyEntry(非对称机密里的私钥)、KeyStore.SecretKeyEntry (对称加密里的秘钥)、KeyStore.TrustedCertificateEntry(受信任的证书)
Keytool 工具
路径:jre\bin\keytool.exe
常用命令:
生成keypair
keytool -genkeypair
keytool -genkeypair -alias lisi(后面部分是为证书指定别名,否则采用默认的名称为mykey)
看看keystore 中有哪些项目:
keytool -list 或keytool -list -v
keytool -exportcert -alias lisi -file lisi.cer
生成可打印的证书:
keytool -exportcert -alias lisi -file lisi.cer –rfc
显示数字证书文件中的证书信息:
keytool -printcert -file lisi.cer
直接双击lisi.cer,用window 系统的内置程序打开lisi.cer
Android 的keystore 相关知识
debug 签名路径:user.android\debug.keystore
debug.keystore 的别名(alias)及密码:
别名:androiddebugkey,密码:android
签名命令(jdk1.6):
jarsigner -verbose -keystore debug.keystore -signedjar 1signed.apk 1.apk androiddebugkey
签名命令(jdk1.7):
jarsigner -verbose -keystore debug.keystore -signedjar 1signed.apk 1.apk androiddebugkey -digestalg
SHA1 -sigalg MD5withRSA
优化命令:zipalign -v 4 1signed.apk 1signedaligned.apk
验证签名是否成功:jarsigner -verify 1signed.apk
补充
签名证书:
由权威颁发机构颁发给服务器或者个人用于证明自己身份的东西,默认客户端都是信任的。主要目的是用来加密和保证数据的完整性和不可抵赖性
例如根证书机构Symantec 颁发给百度的就是签名证书,是受信任的。
自签名证书:
由服务器自己颁发给自己,用于证明自己身份的东西,非权威颁发机构发布,默认客户端都是不信任的,主要目的是用来加密和保证数据的完整性和不可抵赖性,与签名证书相同.
例如中铁集团(SRCA)办法给12306 的证书就是自签名证书,自己给自己颁发的。
Https 编程
介绍
** SSL(Secure Sockets Layer 安全套接层)**,为网景公司(Netscape)所研发,用以保障在Internet 上数据传输之安全,利用数据加密(Encryption)技术,可确保数据在网络上之传输过程中不会被截取及窃听。一般通用之规格为40 bit 之安全标准,美国则已推出128 bit 之更高安全标准,但限制出境。只要3.0 版本以上之I.E.或Netscape 浏览器即可支持SSL。
TLS(Transport Layer Security 传输层安全),用于在两个通信应用程序之间提供保密性和数据完整性。TLS 是SSL 的标准化后的产物,有1.0 ,1.1 ,1.2 三个版本,默认使用1.0。TLS1.0 和SSL3.0 几乎没有区别,事实上我们现在用的都是TLS,但因为历史上习惯了SSL 这个称呼。
SSL 通信简单图示:
SSL 通信详细图示:
当请求使用自签名证书的网站数据时,例如请求12306 的客运服务页面:https://kyfw.12306.cn/otn/,则会报下面的错误,原因是客户端的根认证机构不能识别该证书
错误信息:unable to find valid certification path to requested target
解决方案1
一个证书可不可信,是由TrustManager 决定的,所以我们只需要自定义一个什么都不做的TrustManager即可,服务器出示的所有证书都不做校验,一律放行。
1. public static void main(String[] args) throws Exception {
2. //协议传输层安全TLS(transport layer secure)
3. SSLContext sslContext = SSLContext.getInstance("TLS");
4. //创建信任管理器(TrustManager 负责校验证书是否可信)
5. TrustManager[] tm = new TrustManager[]{new EmptyX509TrustManager()};
6. //使用自定义的信任管理器初始化SSL 上下文对象
7. sslContext.init(null, tm, null);
8. //设置全局的SSLSocketFactory 工厂(对所有ssl 链接都产生影响)
9. HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
10.
11. //URL url = new URL("https://www.baidu.com");
12. URL url = new URL("https://kyfw.12306.cn/otn/");
13. HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
14. InputStream in = conn.getInputStream();
15. System.out.println(Util.inputstream2String(in));
16. }
17.
18. /**
19. * 自定义一个什么都不做的信任管理器,所有证书都不做校验,一律放行
20. */
21. private static class EmptyX509TrustManager implements X509TrustManager{
22. @Override
23. public void checkClientTrusted(X509Certificate[] chain, String authType)
24. throws CertificateException {
25. }
26. @Override
27. public void checkServerTrusted(X509Certificate[] chain, String authType)
28. throws CertificateException {
29. }
30. @Override
31. public X509Certificate[] getAcceptedIssuers() {
32. return null;
33. }
解决方案2
12306 服务器出示的证书是中铁集团SRCA 给他颁发的,所以SRCA 的证书是能够识别12306 的证书的,所以只需要把SRCA 证书导入系统的KeyStore 里,之后交给TrustManagerFactory 进行初始化,则可把SRCA 添加至根证书认证机构,之后校验的时候,SRCA 对12306 证书校验时就能通过认证。
这种解决方案有两种使用方式:一是直接使用SRCA.cer 文件,二是使用改文件的RFC 格式数据,将其写在代码里。
1. //12306 证书的RFC 格式(注意要记得手动添加两个换行符)
2. private static final String CERT_12306_RFC = "-----BEGIN CERTIFICATE-----\n"
3. +
"MIICmjCCAgOgAwIBAgIIbyZr5/jKH6QwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ04xKTAn"
4. +
"BgNVBAoTIFNpbm9yYWlsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRTUkNBMB4X"
5. +
"DTA5MDUyNTA2NTYwMFoXDTI5MDUyMDA2NTYwMFowRzELMAkGA1UEBhMCQ04xKTAnBgNVBAoTIFNp"
6. +
"bm9yYWlsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRTUkNBMIGfMA0GCSqGSIb3"
7. +
"DQEBAQUAA4GNADCBiQKBgQDMpbNeb34p0GvLkZ6t72/OOba4mX2K/eZRWFfnuk8e5jKDH+9BgCb2"
8. +
"9bSotqPqTbxXWPxIOz8EjyUO3bfR5pQ8ovNTOlks2rS5BdMhoi4sUjCKi5ELiqtyww/XgY5iFqv6"
9. +
"D4Pw9QvOUcdRVSbPWo1DwMmH75It6pk/rARIFHEjWwIDAQABo4GOMIGLMB8GA1UdIwQYMBaAFHle"
10. +
"tne34lKDQ+3HUYhMY4UsAENYMAwGA1UdEwQFMAMBAf8wLgYDVR0fBCcwJTAjoCGgH4YdaHR0cDov"
11. +
"LzE5Mi4xNjguOS4xNDkvY3JsMS5jcmwwCwYDVR0PBAQDAgH+MB0GA1UdDgQWBBR5XrZ3t+JSg0Pt"
12. +
"x1GITGOFLABDWDANBgkqhkiG9w0BAQUFAAOBgQDGrAm2U/of1LbOnG2bnnQtgcVaBXiVJF8LKPaV"
13. +
"23XQ96HU8xfgSZMJS6U00WHAI7zp0q208RSUft9wDq9ee///VOhzR6Tebg9QfyPSohkBrhXQenvQ"
14. + "og555S+C3eJAAVeNCTeMS3N/M5hzBRJAoffn3qoYdAO1Q8bTguOi+2849A=="
15. + "-----END CERTIFICATE-----\n";
16. public static void main(String[] args) throws Exception {
17. // 使用传输层安全协议TLS(transport layer secure)
18. SSLContext sslContext = SSLContext.getInstance("TLS");
19. //使用SRCA.cer 文件的形式
20. //FileInputStream certInputStream = new FileInputStream(new File("srca.cer"));
21. //也可以通过RFC 字符串的形式使用证书
22. ByteArrayInputStream certInputStream = new
ByteArrayInputStream(CERT_12306_RFC.getBytes());
23. // 初始化keyStore,用来导入证书
24. KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
25. //参数null 表示使用系统默认keystore,也可使用其他keystore(需事先将srca.cer 证书导入
keystore 里)
26. keyStore.load(null);
27. //通过流创建一个证书
28. Certificate certificate = CertificateFactory.getInstance("X.509")
29. .generateCertificate(certInputStream);
30. // 把srca.cer 这个证书导入到KeyStore 里,别名叫做srca
31. keyStore.setCertificateEntry("srca", certificate);
32. // 设置使用keyStore 去进行证书校验
33. TrustManagerFactory trustManagerFactory = TrustManagerFactory
34. .getInstance(TrustManagerFactory.getDefaultAlgorithm());
35. trustManagerFactory.init(keyStore);
36. //用我们设定好的TrustManager 去做ssl 通信协议校验,即证书校验
37. sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
38. HttpsURLConnection.setDefaultSSLSocketFactory(sslContext
39. .getSocketFactory());
40. URL url = new URL("https://kyfw.12306.cn/otn/");
41. HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
42. InputStream in = conn.getInputStream();
43. System.out.println(Util.inputstream2String(in));
44. }
Android 里的https 请求:
把scra.cer 文件考到assets 或raw 目录下,或者直接使用证书的RFC 格式,接下来的做法和java工程代码一样
总结
1,对称加密与非对称加密结合使用实现了秘钥交换,之后通信双方使用该秘钥进行对称加密通信。
2,消息摘要与非对称加密实现了数字签名,根证书机构对目标证书进行签名,在校验的时候,根证书用公钥对其进行校验。若校验成功,则说明该证书是受信任的。
3,Keytool 工具可以创建证书,之后交给根证书机构认证后直接使用自签名证书,还可以输出证书的RFC格式信息等。
4,数字签名技术实现了身份认证与数据完整性保证。
5,加密技术保证了数据的保密性,消息摘要算法保证了数据的完整性,对称加密的高效保证了数据处理的可靠性,数字签名技术保证了操作的不可否认性。