《Java加密与解密的艺术》样章连载
原创作者:
梁栋 阅读:784次 评论:0条 更新时间:2010-10-26
收藏
书名: Java加密与解密的艺术 作者: 梁栋 定价:69.00元 豆瓣网: http://book.douban.com/subject/4709510/ 互动网: http://www.china-pub.com/196506 卓越网: http://www.amazon.cn/mn/dp/B003FMV8D8 当当网: http://product.dangdang.com/product.aspx?product_id=20813335 作者简介 : 梁栋 ,资深Java开发者,有丰富的Spring、Hibernate、iBatis等Java技术的使用和开发经验,擅长Java企业级应用开发;安全技术专家,对Java加密与解密技术有系统深入的研究,实践经验亦非常丰富。他还是一位出色的项目经理,是V8Booker(手机电子书)项目的核心开发团队人员之一,负责核心模块的开发;同时他还在V8NetBank(网银系统)项目中担任项目经理,负责系统的架构和核心模块的开发。 内容简介 : 《 Java加密与解密的艺术 》是Java安全领域的百科全书,密码学领域的权威经典,4大社区一致鼎力推荐。 全书包含3个部分,基础篇对Java企业级应用的安全知识、密码学核心知识、与Java加密相关的API和通过权限文件加强系统安全方面的知识进行了全面的介绍;实践篇不仅对电子邮件传输算法、消息摘要算法、对称加密算法、非对称加密算法、数字签名算法等现今流行的加密算法的原理进行了全面而深入的剖析,而且还结合翔实的范例说明了各种算法的具体应用场景;综合应用篇既细致地讲解了加密技术对数字证书和SSL/TLS协议的应用,又以示例的方式讲解了加密与解密技术在网络中的实际应用,极具实践指导性。 Java开发者将通过本书掌握密码学和Java加密与解密技术的所有细节;系统架构师将通过本书领悟构建安全企业级应用的要义;其他领域的安全工作者也能通过本书一窥加密与解密技术的精髓。
《Java加密与解密的艺术》前言
为什么要写这本书
2009年IT界的3大主题是:安全、移动应用开发和云计算。
当你在用IM与好友聊天时,当你通过B2C网站购物时,当你用邮件与客户交流时,当你公司的应用服务器与合作伙伴交换商业数据时⋯⋯你是否考虑过你的数据是否安全?你的隐私是否会被泄露?你的银行卡是否会被盗用?你的竞争对手是否能破解你的敏感数据?
任何一项通过网络交互的数据都有可能是不安全的,而我们却越来越依赖于网络。用户密码、聊天消息、银行卡号、邮件信息、商业敏感数据,如果通过明文传输,后果不堪设想:自己的账号被盗用、隐私成为公共话题、信用卡被人滥用、竞争对手盗用自己的数据⋯⋯于是,为了确保数据不被侵犯,数据加密与解密技术应运而生,成为众多应用中的一项核心技术。
众所周知,Java EE是目前企业应用中使用最广泛的语言之一,几乎在任何一个领域都能看到Java EE的身影。随着加密与解密算法的发展,Java加密与解密技术不断演进,不断提高着数据的安全性,已成为各大企业应用中一项关键性的技术。
很多企业应用领域的架构师都很关注加密与解密算法在应用中的使用,譬如用户密码加密、网络协议加密等。如何在名目繁多的Java加密与解密技术中选择合适的算法进行企业级应用开发,如何解决Java加密与解密技术开发过程中遇到的各种问题,这成为许多开发者,尤其是架构师关注的焦点问题。然而,国内目前还没有一本书能解决这些问题。本书的作者因工作需要,采用Java加密与解密技术成功构建了企业级网银系统。在开发过程中,作者感受到了Java加密与解密技术的精妙。作者希望把Java加密与解密技术在企业应用开发领域的经验和心得分享给广大读者,提升企业应用的安全性。
本书面向的读者
本书主要适合以下读者:
❑ 所有利用Java进行企业级应用开发的软件工程师
对于企业级应用软件工程师来讲,这将是一次系统的密码学之旅。本书将介绍密码学理论、Java相关算法实现、开源组件包介绍、数字证书与安全协议等相关内容,并配有相关实例为读者提供详尽实现指导,为构建企业级安全应用提供完整的技术支持。
❑ 系统架构师
对于系统架构师来讲,如何使用成熟技术快速构建安全企业应用是安全工作的第一要务。在算法方面,本书详述了Java 6对于密码学算法的相关实现,针对AES算法密钥长度受限问题给出解决办法。同时,针对当前Java 6不支持的算法,如SHA224、ElGamal和ECDSA等,本书详细介绍如何使用第三方开源加密组件包Bouncy Castle进行相关算法实现补充,并且还详细介绍了Apache Commons Codec,这些成熟的组件包都是构建安全企业应用必不可少的工具包。
在架构方面,本书浓墨重彩地介绍了数字证书的构建、SSL/TLS协议服务搭建,并通过相关实例介绍如何构建单向/双向认证服务。
❑ 其他安全领域的软件工程师
如今企业级应用已经逐步转变以服务为主的异构体应用,如Web Service应用等。Java加密算法实现遵循密码学相关国际标准,完全可以与其他计算机语言(如C++、C#等)构建的异构体应用进行数据加密交互。本书为读者选择合适的算法并提供详尽的技术实现。
如何阅读本书
全书共分为3个部分:基础篇、实践篇、综合应用篇。
❑ 基础篇
本篇共包含4个章节,主要对Java企业级应用安全、密码学理论和Java中与加密相关的API进行了详细介绍,并详细阐述了第三方组件包Bouncy Castle和Apache Commons Codec相关的API。
第1章主要阐述了当前的安全问题,并给出了安全的相关标准。本书将在后续章节内通过各个算法介绍逐一实现这些标准,这些标准也是评判系统安全级别的准则。
第2章主要详述了密码学相关理论知识,并回顾密码学的发展历程。未曾接触过密码学的读者,可通过本章了解密码学理论的基础,本书将在后续章节中多处应用该章相关技术名词。
第3章详细阐述了Java 6安全领域相关API内容,为读者详尽介绍每一个与密码学相关的类以及方法。该章将是每位安全领域软件工程师必读的内容,在阅读本书的后续章节时需经常翻阅该章内容。
第4章主要介绍如何通过权限文件加强系统安全级别,并详述开源组件Bouncy Castle和Apache Commons Codec相关的API内容。如果您正苦于AES算法密钥长度受限,SHA224、ElGamal、ECDSA等算法缺少支持等问题,那么请您阅读该章;如果您非常希望找到Base64及
十六进制编码算法的成熟开源组件,也请您阅读该章。本书将在后续章节中介绍如何使用这些
开源组件并实现相关算法。
❑ 实践篇
这篇主要对现今流行的所有加密算法进行了全面阐述和深入剖析,并配合相关测试用例演示算法实现。在阅读这篇前,请阅读本书第2章相关理论知识,并了解第3~4章相关的API内容。这篇将是所有企业级应用Java软件工程师的必读内容。
第5章介绍了极为简单的Base64算法,该算法可以作为加密算法的入门算法。如果仅仅需要确保应用交互之间的数据达到隐藏的目的,那么您在第5章中一定可以找到满意的答案。
第6章主要详述了MD系列、SHA系列以及MAC系列三大消息摘要算法相关实现。并详细介绍如何使用Bouncy Castle构建Java 6所不支持的算法实现。对于一般网络应用,经常需要为下载软件提供对应的摘要信息用于校验文件完整性。相信在阅读这章内容后,您可以熟练地使用Apcahe Commons Codec为应用实现校验文件完整性的需求。
第7章将沿着对称加密算法的发展历程,详述DES、DESede、AES和PBE四大算法的实现细节。并详细介绍如何使用Bouncy Castle构建目前较为常用的IDEA算法。这些算法适用于中小型企业级应用网络数据加密交互需求,同时也适用于其他安全领域的相关需求,是应用最为广泛的加密算法,更是密码学领域的的核心算法。如果仅仅想要通过对称加密算法以及消息摘要算法构建简单的加密网络应用,那么该章提供的实例将非常合适。
第8章主要详述了构建于对称加密算法之上的非对称加密算法,包括DH、RSA和ElGamal三大常用算法。该章是本书后续章节内容的基础,数字签名算法、数字证书、安全协议等内容都与该章内容息息相关,请在阅读后续章节前能够对该章内容有较深入的阅读。如果对单向/双向认证服务底层实现非常有兴趣,并想要知道它的来龙去脉,那么该章就是探究该技术旅途上的第一个驿站。
第9章详述了基于消息摘要算法和非对称加密算法之上的数字签名算法,包括RSA、DSA和ECDSA三大常用算法。数字签名算法是消息摘要算法的延续,是单向/双向认证服务核心认证技术。如果想通过非对称加密算法构建简单的网络加密应用,并期望使用数字签名算法对数据进行校验,那么该章的实例将非常合适。
❑ 综合应用篇
这篇不仅细致地介绍了加密技术对数字证书和SSL/TLS协议的应用,而且还以示例的方式讲解了加密解密技术在实际网络中的各种应用,极具实践指导性。请在阅读这篇前仔细阅读实践篇的相关内容。这篇内容将是系统架构师的最爱。
第10章详细介绍了如何使用KeyTool和OpenSSL两大工具进行数字证书管理,并详细介绍__如何在Java中使用数字证书。数字证书是非对称加密算法公钥的载体,是SSL/TLS协议和单向/双向认证服务基础。如果想要构建安全的HTTPS网络服务应用,请先阅读该章内容。
第11章主要介绍了SSL/TLS协议及单向/双向认证服务。这将是探究单向/双向认证服务技术旅途上的最后一站。该章将详述如何通过简单配置Tomcat服务器快速构建单向/双向认证服务,内容详实、极具实践性。
第12章是本书的实例集合,通过三套网络应用实例揭示常规网络应用安全、即时通信网络应用安全和以数据交互为主的Web Service应用安全,并通过网络监测工具WireShark对其效果进行检测。通过不同算法的组合,三套实例逐步升级自身系统的安全级别,极具指导意义。该章为解决网络安全问题提供了可行性参考。
通过阅读本书,读者不仅能全面掌握Java加密与解密的各种基础知识,而且还能进一步了解Java加密与解密的高级技术和技巧,从而将这些知识都运用到实际开发中去。
《Java加密与解密的艺术》目录
第一部分 基础篇
第1章 企业应用安全⋯⋯⋯⋯⋯⋯⋯2
1.1 我们身边的安全问题⋯⋯⋯⋯⋯⋯2
1.2 拿什么来拯救你,我的应用⋯⋯⋯3
1.3 捍卫企业应用安全的银弹⋯⋯⋯⋯8
1.4 为你的企业应用上把锁⋯⋯⋯⋯⋯9
1.5 小结⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯10
第2章 企业应用安全的银弹—密码学⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯11
2.1 密码学的发家史⋯⋯⋯⋯⋯⋯⋯⋯11
2.2 密码学定义、术语及其分类⋯⋯⋯15
2.3 保密通信模型⋯⋯⋯⋯⋯⋯⋯⋯⋯17
2.4 古典密码⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯18
2.5 对称密码体制⋯⋯⋯⋯⋯⋯⋯⋯⋯19
2.6 非对称密码体制⋯⋯⋯⋯⋯⋯⋯⋯26
2.7 散列函数⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯28
2.8 数字签名⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯29
2.9 密码学的未来⋯⋯⋯⋯⋯⋯⋯⋯⋯30
2.10 小结⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯32
第3章 Java加密利器⋯⋯⋯⋯⋯⋯⋯34
3.1 Java与密码学⋯⋯⋯⋯⋯⋯⋯⋯⋯34
3.2 java.security包详解⋯⋯⋯⋯⋯⋯37
3.3 javax.crypto包详解⋯⋯⋯⋯⋯⋯⋯70
3.4 java.security.spec包和javax.crypto.spec包详解⋯⋯⋯⋯⋯85
3.5 java.security.cert包详解⋯⋯⋯⋯⋯91
3.6 javax.net.ssl包详解⋯⋯⋯⋯⋯⋯100
3.7 小结⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯107
第4章 他山之石,可以攻玉⋯⋯⋯109
4.1 加固你的系统⋯⋯⋯⋯⋯⋯⋯⋯109
4.2 加密组件Bouncy Castle ⋯⋯⋯⋯111
4.3 辅助工具Commons Codec ⋯⋯⋯120
4.4 小结⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯121
第二部分 实践篇
第5章 电子邮件传输算法—Base64 ⋯⋯⋯⋯⋯⋯⋯⋯⋯134
5.1 Base64算法的由来⋯⋯⋯⋯⋯⋯134
5.2 Base64算法的定义⋯⋯⋯⋯⋯⋯134
5.3 Base64算法与加密算法的关系⋯135
5.4 实现原理⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯136
5.5 模型分析⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯137
5.6 Base64算法实现⋯⋯⋯⋯⋯⋯⋯138
5.7 Url Base64算法实现⋯⋯⋯⋯⋯⋯147
5.8 应用举例⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯151
5.9 小结⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯153
第6章 验证数据完整性—消息摘要算法⋯⋯⋯⋯⋯⋯⋯⋯⋯155
6.1 消息摘要算法简述⋯⋯⋯⋯⋯⋯155
6.2 MD算法家族⋯⋯⋯⋯⋯⋯⋯⋯⋯157
6.3 SHA算法家族⋯⋯⋯⋯⋯⋯⋯⋯167
6.4 MAC算法家族⋯⋯⋯⋯⋯⋯⋯⋯181
6.5 其他消息摘要算法⋯⋯⋯⋯⋯⋯195
6.6 循环冗余校验算法—CRC算法⋯206
6.7 实例:文件校验⋯⋯⋯⋯⋯⋯⋯209
6.8 小结⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯211
第7章 初等数据加密—对称加密算法⋯⋯⋯⋯⋯⋯⋯⋯⋯213
7.1 对称加密算法简述⋯⋯⋯⋯⋯⋯213
7.2 数据加密标准—DES ⋯⋯⋯⋯214
7.3 三重DES—DESede ⋯⋯⋯⋯⋯222
7.4 高级数据加密标准—AES ⋯⋯227
7.5 国际数据加密标准—IDEA ⋯⋯232
7.6 基于口令加密—PBE ⋯⋯⋯⋯236
7.7 实例:对称加密网络应用⋯⋯⋯242
7.8 小结⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯254
第8章 高等数据加密—非对称加密算法⋯⋯⋯⋯⋯⋯⋯⋯⋯256
8.1 非对称加密算法简述⋯⋯⋯⋯⋯256
8.2 密钥交换算法—DH ⋯⋯⋯⋯⋯258
8.3 典型非对称加密算法—RSA ⋯269
8.4 常用非对称加密算法—ElGamal⋯277
8.5 实例:非对称加密网络应用⋯⋯284
8.6 小结⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯296
第9章 带密钥的消息摘要算法—数字签名算法⋯⋯⋯⋯⋯⋯297
9.1 数字签名算法简述⋯⋯⋯⋯⋯⋯297
9.2 模型分析⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯298
9.3 经典数字签名算法—RSA ⋯⋯299
9.4 数字签名标准算法—DSA ⋯⋯306
9.5 椭圆曲线数字签名算法—ECDSA ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯311
9.6 实例:带有数字签名的加密网络应用⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯318
9.7 小结⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯319
第三部分 综合应用篇
第10章 终极武器—数字证书⋯332
10.1 数字证书详解⋯⋯⋯⋯⋯⋯⋯⋯332
10.2 模型分析⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯335
10.3 证书管理⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯337
10.4 证书使用⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯351
10.5 应用举例⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯360
10.6 小结⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯360
第11章 终极装备—安全协议⋯362
11.1 安全协议简述⋯⋯⋯⋯⋯⋯⋯⋯362
11.2 模型分析⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯364
11.3 单向认证服务⋯⋯⋯⋯⋯⋯⋯⋯369
11.4 双向认证服务⋯⋯⋯⋯⋯⋯⋯⋯381
11.5 应用举例⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯387
11.6 小结⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯387
第12章 量体裁衣—为应用选择合适的装备⋯⋯⋯⋯389
12.1 实例:常规Web应用开发安全⋯389
12.2 实例:IM应用开发安全⋯⋯⋯399
12.3 实例:Web Service应用开发安全⋯420
12.4 小结⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯443
附录A Java 6支持的算法⋯⋯⋯445
附录B Bouncy Castle支持的算法⋯⋯⋯⋯⋯⋯⋯⋯⋯447
第4章 他山之石,可以攻玉
在第3章中,我们了解了Java 6中与加密/解密相关的API,几乎各种常用加密算法都能找到对应的实现,但还是难免会有遗憾:受出口限制,密钥长度上不能满足需求;部分算法未能支持,如MD4、SHA-224等算法;API使用起来还不是很方便;一些常用的进制转换辅助工具未能提供,如Base64编码转换、十六进制编码转换等工具。 对于上述这些问题,该如何解决呢?他山之石,可以攻玉! 对于出口限制,Sun通过权限文件(local_policy.jar、US_export_policy.jar)做了相应限制。但幸运的是,Sun在官方主页上提供了替换文件,可减少相关的限制。当然,你需要结合本地进出口政策,合法使用该文件。 对于Java 6未能支持的加密算法,各大密码学机构以及以加密算法为核心的软件组织和软件公司都不遗余力地研究,并提供了相应的实现。但因为版权问题,我们很少能够使用到这些机构提供的加密软件包。在这样一个开源的时代,难道就没有其他的替代方案吗?当然不是,Bouncy Castle(http://www.bouncycastle.org/)提供了一系列算法支持实现,并可以跻身于JCE框架之下,以提供者的方式纳入其中。而且,Bouncy Castle是一款开源组件,你不必为版权问题而伤脑筋。此外,Bouncy Castle提供了Base64和十六进制编码转换相关的实现。 Commons Codec(http://commons.apache.org/codec/)是国际开源组织Apache(http://www.apache.org/)旗下的一款开源软件。它与Bouncy Castle不同,并未对Java 6提供扩展加密算法。仅仅是对Java 6提供的API做了改进,提供了更加易用的API。
4.1 加固你的系统
鉴于出口限制问题,我们得到的JDK安全强度不够高,主要是密钥长度不够。对于这一点,Sun在其官方网站上提供了无政策限制权限文件(Unlimited Strength Jurisdiction Policy Files),我们只需要将其部署在JRE环境中。
如果你所在的国家或地区不受该进出口限制,就完全可以使用该文件解除权限限制问题。
4.1.1 获得权限文件
Sun在其下载页面(http://java.sun.com/javase/downloads/index.jsp)上提供了该权限文件的下载地址,如图4-1所示,注意Other Downloads项。
图4-1 权限文件下载页面
Other Downloads项提供了Java Cryptography Extension(JCE)Unlimited Strength Jurisdiction Policy Files 6的下载地址。很显然,它的版本是6,对应Java 6。下载后,会得到一个名为jce_policy-6.zip的文件。用WinRAR打开后,如图4-2所示。
图4-2 jce_policy-6.zip文件中jce目录列表
在这个压缩包中仅有一个目录,也就是jce目录。该目录中包含了4个文件:README.txt、COPYRIGHT.html、local_policy.jar和US_export_policy.jar。其中包含的两个jar文件正是此次配置中用到的文件。
4.1.2 配置权限文件
我们可以查看上述README.txt文件,你需要在JDK的JRE环境中,或者是JRE环境中配置上述两个jar文件。
切换到%JDK_Home%\jre\lib\security目录下,对应覆盖local_policy.jar和US_export_policy.jar两个文件。同时,你可能有必要在%JRE_Home%\lib\security目录下,也需要对应覆盖这两个文件。
配置权限文件的最终目的是为了使应用在运行环境中获得相应的权限,可以加强应用的安全性。
通常,我们在应用服务器上安装的是JRE,而不是JDK。因此,这就很有必要在应用服务器的%JRE_Home%\lib\security目录下,对应覆盖这两个权限文件。很多开发人员往往忽略了这一点,导致事故发生。
4.1.3 验证配置
经过一番调整之后,如何验证我们的系统获得相应的权限呢?
修改权限配置的目的是为了获得更长的密钥。如果对于同一个加密算法,在修改权限配置前后系统所能提供的密钥的最长长度发生了变化,那么就说明配置有效!
AES算法是较为常用的对称加密算法之一,几乎是对称加密算法中安全级别最高的算法。Java 6支持AES算法的密钥长度为128位、192位或256位。但是,如果不加权限配置直接使用256位长度的密钥,就会得到“java.security.InvalidKeyException”异常,如下面代码所示:
KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
// AES 要求密钥长度为128位、192位或256位。
kg.init(256);
// 生成秘密密钥
SecretKey secretKey = kg.generateKey();
byte[] key = secretKey.getEncoded();
如果我们未能对相应的权限配置文件作相应调整,则会得到“java.security.InvalidKeyException”异常。反之,将正常获得256位长度的密钥!
有关AES算法相关实现细节,请读者阅读第7章。
4.2 加密组件Bouncy Castle
Java 6提供了多种算法支持,但并不完善。许多加密强度较高的算法,Java 6未能提供。在本书第3章中,我们提到了java.security文件,它位于%JDK_HOME%\jre\lib\security\ext目录下,用于提供者配置。
如果你需要使用Java 6不支持的算法,如MD4和IDEA(International Data Encryption Algorithm,国际数据加密算法)等。你可以在继续沿用Java 6的API前提下,通过在JRE环境中配置开源组件包Bouncy Castle,加入对应的提供者,获得相应的算法支持。关于Bouncy Castle支持的算法,请参见附录。
4.2.1 获得加密组件
Bouncy Castle目前提供的加密组件包的版本是1.43。自1.40版本开始,Bouncy Castle提供了对IDEA算法的支持。
我们可以通过Bouncy Castle提供的下载地址(http://www.bouncycastle.org/latest_releases.html),下载最新的加密组件包,主要是bcprov-jdk16-143.jar和bcprov-ext-jdk16-143.jar两个文件,如图4-3所示。
图4-3 Bouncy Castle可执行二进制文件下载页面
当然,你有可能需要相应的源代码和API文档,它位于bcprov-jdk16-143.zip文件中,如图4-4所示。
图4-4 Bouncy Castle文档及源代码文件下载页面
获得bcprov-jdk16-143.jar和bcprov-ext-jdk16-143.jar两个文件后,我们就可以在Java 6的基础上扩充算法支持了。后面详细讲解这两个文件的使用方式。
Bouncy Castle加密组件包支持的算法不仅广泛,而且开源,常常用做J2ME的加密实现,其主页上也提供了J2ME版本的下载链接(http://www.bouncycastle.org/download/lcrypto-j2me-143.zip)。
4.2.2 扩充算法支持
对于Bouncy Castle提供的扩充算法支持,我们有两种方案可选:
1)配置方式。通过配置JRE环境,使其作为提供者(Provider)提供相应的算法支持,在代码实现层面只需指定要扩展的算法名称。
2)调用方式。在调用Java API初始化相应的密钥工厂、密钥生成器等引擎类之前,通过代码将Bouncy Castle提供者引入,获得扩展算法支持。
1. 配置方式
配置Bouncy Castle并不复杂,有点类似于4.1节中权限文件的配置,需要在%JDK_Home%和%JRE_Home%目录中做相应调整。
我们以%JDK_Home%目录配置为例。
❑使用步骤
首先,我们需要修改配置文件(java.security)。
在第3章中提到配置%JDK_Home%\ jre\lib\security\java.security文件,通过加入支持的方式获得更多的算法支持。
在这个文件中,我们可以很清晰地看到Java 6中有如下9种安全提供者:
security.provider.1=sun.security.provider.Sun
security.provider.2=sun.security.rsa.SunRsaSign
security.provider.3=com.sun.net.ssl.internal.ssl.Provider
security.provider.4=com.sun.crypto.provider.SunJCE
security.provider.5=sun.security.jgss.SunProvider
security.provider.6=com.sun.security.sasl.Provider
security.provider.7=org.jcp.xml.dsig.internal.dom.XMLDSigRI
security.provider.8=sun.security.smartcardio.SunPCSC
security.provider.9=sun.security.mscapi.SunMSCAPI
Java 7很快就要问世了,作者查看了该文件的相应配置,并无变化。
上述这些配置是按照以下方式来配置的:
#security.provider.
=
很显然,为了加入Bouncy Castle加密组件的安全提供者只需要这样做:
#增加BouncyCastleProvider
security.provider.10=org.bouncycastle.jce.provider.BouncyCastleProvider
最后,我们需要将bcprov-ext-jdk16-143.jar文件导入。
切换至%JDK_Home%\jre\lib\ext目录下,我们能够看到 sunjce_provider.jar这个文件。SunJCE就是由这个文件提供的。同理,要将Bouncy Castle加密组件扩展包导入其中,只需要将4.2.1节中获得的bcprov-ext-jdk16-143.jar文件放到这里即可。
%JRE_Home%目录的相应配置与上述%JDK_Home%目录配置相类似。
对应修改%JRE_Home%\lib\security\java.security文件,并将bcprov-ext-jdk16-143.jar文件放置到%JRE_Home%\lib\ext目录中即可。
❑应用举例
Java 6不支持MD4算法,做了上述配置后,如果要使用MD4算法可参考如下代码:
/**
* MD4加密
* @param data
* @return
* @throws Exception
*/
public static byte[] encodeMD4(byte[] data) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD4");
md.update(data);
return md.digest();
}
这是一种对使用者透明的使用方式,你无须关心MD4算法的提供者是谁,代码很清晰。
2. 调用方式
有时候,我们需要通过明显的代码调用方式引入支持者,这完全依赖于Security类的addProvider()方法,详见3.2节。
❑使用步骤
首先,我们需要将bcprov-jdk16-143.jar文件导入工程。相信读者对于这一步操作一定都不陌生,这里就不详细介绍了。
接着,我们需要将以下两个类导入(import)你的代码中:
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
当然,如果你使用Eclipse,可以在下述代码写完后,使用快捷键Ctrl+Shift+O直接导入所需类。
最后,我们只需要在初始化密钥工厂、密钥生成器等引擎类之前,调用如下代码:
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
或者,在初始化密钥工厂、密钥生成器等引擎类时,采用如下方式:
MessageDigest md = MessageDigest.getInstance("MD4", "BC");
每个提供者都有简称,Bouncy Castle提供者简称“BC”,因此我们可以通过上述方式使用BouncyCastleProvider。
执行以下代码,我们可以获得Bouncy Castle提供者的算法详细描述。
Provider provider = Security.getProvider("BC");
System.err.println(provider);
for (Map.Entry entry : provider.entrySet()) {
System.err.println(entry.getKey() + " - " + entry.getValue());
}
在控制台中我们可以看到以下内容:
BC version 1.43
Alg.Alias.Signature.SHA224withCVC-ECDSA - SHA224WITHCVC-ECDSA
AlgorithmParameters.DES - org.bouncycastle.jce.provider.JDKAlgorithm Parameters$IVAlgorithmParameters
KeyGenerator.2.16.840.1.101.3.4.22 - org.bouncycastle.jce.provider. symmetric.AES$KeyGen192
Alg.Alias.Cipher.RSA//ISO9796-1PADDING - RSA/ISO9796-1
AlgorithmParameterGenerator.NOEKEON - org.bouncycastle.jce.provider. symmetric.Noekeon$AlgParamGen
Alg.Alias.Cipher.RSA//NOPADDING - RSA
Alg.Alias.Mac.IDEA - IDEAMAC
Alg.Alias.AlgorithmParameters.PBEWITHSHAAND3-KEYTRIPLEDES - PKCS12PBE
Alg.Alias.Mac.HMAC/SHA1 - HMACSHA1
Alg.Alias.AlgorithmParameterGenerator.1.2.410.200004.1.4 - SEED
AlgorithmParameterGenerator.ELGAMAL - org.bouncycastle.jce.provider. JDKAlgorithmParameterGenerator$ElGamal
......
上述内容并未完全展现Bouncy Castle所支持的算法,本文做了简要摘录。
通常我们也可以通过上述方式检查系统支持的加密算法。
❑应用举例
Java 6未能支持MD4算法,也未能支持SHA-224算法。依照本文显式调用代码的方式,需要将bcprov-jdk16-143.jar文件导入工程,同时导入相关类(Security和BouncyCastleProvider),并通过Security类的addProvider()方法将BouncyCastleProvider类导入,见如下代码:
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
// ... 省略
/**
* SHA-224加密
*
* @param data
* @return
* @throws Exception
*/
public static byte[] encodeSHA224(byte[] data) throws Exception {
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
MessageDigest md = MessageDigest.getInstance("SHA-224");
md.update(data);
return md.digest();
}
多了几行代码,多少有点别扭。但这种方式让人心知肚明,很清楚自己使用了哪些类。
3. 两种方式对比
配置和调用两种方法都有可取之处:前者对代码无需改动,只需提供Bouncy Castle支持的算法名称,但开发者需要知道Bouncy Castle已作为提供者部署在JRE中,也就是说这种方式需要依赖环境;后者需要对代码做改动,将Bouncy Castle作为提供者在代码中调用,但对环境依赖程度较小。
作者对于这两种方式并没有一个绝对的评价,它们各有特色。读者可根据需要,选择合适的方式获得扩展算法支持。
为了引起读者的注意,避免不必要的误导,本文在后续内容中将采用第2种方式介绍Bouncy Castle相关扩展算法。
4.2.3 相关API
Bouncy Castle的API主要包含了以下几个方面:
❑JCE工具及其扩展包
仅包括org.bouncycastle.jce包。这是对JCE框架的支持。其中定义了一些扩展算法的接口与实现,如ECC和ElGamal算法。
❑JCE支持者和测试包
包括org.bouncycastle.jce.provider包及其子包。本章提到的Bouncy Castle 的安全提供者BouncyCastleProvider就位于该包中。
❑轻量级加密包
包括org.bouncycastle.crypto包及其子包。我们可以认为这个包完成了扩展算法的实现。
❑OCSP和OpenSSL PEM支持包
包括org.bouncycastle.ocsp包及其子包和org.bouncycastle.openssl包及其子包。这两个包都是与数字证书相关的支持包。OCSP(Online Certificate Status Protocol,在线证书状态协议)用于鉴定所需证书的(撤销)状态。具体协议内容请查看RFC 2560(http://www.ietf.org/rfc/rfc2560.txt)。OpenSSL用于管理数字证书,包括证书的申请和撤销等。
❑ASN.1编码支持包
包括org.bouncycastle.asn1包及其子包,该包体积最为庞大。标准的ASN.1编码规则有基本编码规则(Basic Encoding Rules, BER)、规范编码规则(Canonical Encoding Rules, CER)、唯一编码规则(Distinguished Encoding Rules, DER)、压缩编码规则(Packed Encoding Rules, PER)和XML编码规则(XML Encoding Rules, XER)。我们在导出数字证书时,常常会使用到DER编码。
❑工具包
包括org.bouncycastle.util包及其子包。提供了很多与编码转换有关的工具类,如Base64编码和十六进制编码。
❑其他包
包括org.bouncycastle.mozilla包及其子包和org.bouncycastle.x509包及其子包。org.bouncycastle.mozilla包用于支持基于Mozilla(网景)浏览器的公钥签名和身份认证。org.bouncycastle.x509包用于基于支持X.509格式的数字证书。
相信不论是谁,在看了上述介绍后都会汗颜。当然,作者也不例外。作者有意避开Bouncy Castle支系庞大的API,建议读者将Bouncy Castle作为支持的方式来使用其扩展算法。这样可以降低学习难度,这也是Sun构建JCE架构最初的目的。
本书将简要介绍org.bouncycastle.util.encoders包中的内容。
1. Base64
Base64类是用于Base64编码的工具类。当然,Sun内部实现了Base64算法,但相关实现并未在API中体现,请读者参考第5章的内容。
// 用于Base64编码/解码转换。
public class Base64
extends Object
❑方法详述
我们先来看编码方法:
// 返回Base64编码结果。
public static byte[] encode(byte[] data)
// 向输出流中写入Base64编码结果。
public static int encode(byte[] data, int off, int length, OutputStream out)
// 向输出流中写入Base64编码结果。
public static int encode(byte[] data, OutputStream out)
以下是对应的解码方法:
// 返回Base64解码结果。
public static byte[] decode(byte[] data)
// 返回Base64解码结果,空格将被忽略。
public static byte[] decode(String data)
// 向输出流中写入Base64解码结果,空格将被忽略。
public static int decode(String data, OutputStream out)
其实,Base64类在内部实现时调用了Base64Encoder类来完成相应的编码/解码操作。我们只需要知道如何使用Base64类就可以了。
❑实现示例
我们通过代码清单4-1来演示如何进行Base64编码和解码操作。
代码清单4-1 Base64编码/解码1
String str = "base64编码";
System.err.println("原文:\t" + str);
byte[] input = str.getBytes();
// Base64编码
byte[] data = Base64.encode(input);
System.err.println("编码后:\t" + new String(data));
// Base64解码
byte[] output = Base64.decode(data);
System.err.println("解码后:\t" + new String(output));
查看控制台输出如下:
原文: Base64 编码
编码后: QmFzZTY0IOe8lueggQ==
解码后: Base64 编码
编码后的内容中,出现“=”符号,这是Base64编码的标志性符号。
2. UrlBase64
Base64算法最初用于电子邮件系统,后经演变成为显式传递Url参数的一种编码算法,通常称为“Url Base64”。它是Base64算法的变体,将字符映射表中用做补位的“=”换成“.”,并用“-”和“_”分别替代“+”和“/”,使得Base64编码符合Url参数规则,可以将二进制的数据以Get方式进行传输。有关Base64算法请参见第5章。
// 用于Url Base64编码/解码转换。
public class UrlBase64
extends Object
❑方法详述
以下为编码方法:
// 返回Url Base64编码。
public static byte[] encode(byte[] data)
以下方法将编码结果输出至输出流中:
// 向输出流中写入Url Base64编码。
public static int encode(byte[] data, OutputStream out)
以下为解码方法:
// 返回Url Base64解码,空格忽略。
public static byte[] decode(byte[] data)
// 返回Url Base64解码,空格忽略。
public static byte[] decode(String data)
以下方法可以将解码结果输出至输出流中:
// 向输出流中写入Url Base64解码,空格忽略。
public static int decode(byte[] data, OutputStream out)
// 向输出流中写入Url Base64解码,空格忽略。
public static int decode(String data, OutputStream out)
UrlBase64类在内部实现时,调用了UrlBase64Encoder类来完成相应的编码/解码操作。这是Bouncy Castle一贯的代码风格。
❑实现示例
我们对上述Base64算法实现稍作调整,改为Url Base64类完成编码和解码操作,如代码清单4-2所示。
代码清单4-2 Url Base64编码/解码
String str = "Base64 编码";
System.err.println("原文:\t" + str);
byte[] input = str.getBytes();
// Url Base64编码
byte[] data = UrlBase64.encode(input);
System.err.println("编码后:\t" + new String(data));
// Url Base64解码
byte[] output = UrlBase64.decode(data);
System.err.println("解码后:\t" + new String(output));
观察控制台输出的内容:
原文: Base64编码
编码后: QmFzZTY0IOe8lueggQ..
解码后: Url Base64编码
编码后的内容中,出现了“.”符号,替换掉了“=”符号,符合了Url参数规则。当然,有些系统会对这个“.”符号较为敏感,如在文件系统中这可能导致一些错误。关于这个问题,请读者参考第5章的相关内容。
3. Hex
不用介绍,读者就能从字面上判断出Hex类和十六进制相关。Hex类用于十六进制转换。常配合消息摘要算法处理摘要值,以十六进制形式公示。
// 用于十六进制编码/解码操作。
public class Hex
extends Object
❑方法详述
以下是十六进制编码方法:
// 返回十六进制编码结果。
public static byte[] encode(byte[] data)
// 返回十六进制编码结果。
public static byte[] encode(byte[] data, int off, int length)
以下方法将编码结果输出至输出流中:
// 向输出流中写入十六进制编码结果。
public static int encode(byte[] data, int off, int length, OutputStream out)
// 向输出流中写入十六进制编码结果。
public static int encode(byte[] data, OutputStream out)
以下是十六进制解码方法:
// 返回十六进制解码结果。
public static byte[] decode(byte[] data)
// 返回十六进制解码结果,空格忽略。
public static byte[] decode(String data)
以下方法将解码结果输出至输出流中:
// 向输出流中写入十六进制解码结果,空格忽略。
public static int decode(String data, OutputStream out)
❑实现示例
我们继续对上述代码进行改造,如代码清单4-3所示:
代码清单4-3 Hex编码/解码1
String str = "Hex 编码";
System.err.println("原文:\t" + str);
byte[] input = str.getBytes();
// Hex编码。
byte[] data = Hex.encode(input);
System.err.println("编码后:\t" + new String(data));
// Hex解码。
byte[] output = Hex.decode(data);
System.err.println("解码后:\t" + new String(output));
观察控制台输出的内容:
原文: Hex编码
编码后: 48657820e7bc96e7a081
解码后: Hex编码
这时候看到编码后的字符串样式就很眼熟了,在各大软件厂商的下载页面上都很常见,通常用做MD5的十六进制表示。
Sun虽然在Java API中提供了简单的进制转换方法,但并不方便。相应的进制转换方法均包含在封装类中,如Long类的toHexString()方法可将长整型转换为十六进制字符串,并通过parseLong()方法,指定进制参数将十六进制字符串转换为长整型,参考代码如下:
// 长整型转换十六进制字符串。
String s = Long.toHexString(new Long(100));
// 十六进制字符串转换长整型。
long l = Long.parseLong(s, 16);
如果输入参数为字节数组,就不能使用上述方式来操作了。
对于一个长整型、整型、浮点型等数据类型来说,二进制转换虽然不复杂,我们完全有能力实现,但是转换过程中要考虑的细节很多,往往容易在补码、转码等问题上出现纰漏,这时就不如使用现成的开源实现。也许“不要重复制造轮子”的经典论断就是这么来的。