Saturday, September 09, 2006

How to use JCE - Part 2

Java加密扩展基础
http://www.phpwind.net/simple/index.php?t109465.html

对称密钥加密与解密
http://www.javatx.cn/clubPage.jsp?ccStyle=0&tID=798&ccID=16

Encrypt Sensitive Configuration Data with Java
http://www.devx.com/Java/10MinuteSolution/21385/1954?pf=true

Java Technology Forums - encrypt and decrypt sample code
http://forum.java.sun.com/thread.jspa?threadID=753209&tstart=135

探索 XML 加密,第 1 部分
http://www-128.ibm.com/developerworks/cn/xml/x-encrypt/index.html

Exploring XML Encryption, Part 2
http://www-128.ibm.com/developerworks/cn/xml/x-encrypt2/listing2.html

How to use JCE - Part 1

0. Introduction
JAVA的安全盾牌-JAVA安全技术 java几乎成为网络程序的标准语言,java给我们提供了先进的应用技术的同时,同时给我们提供了非常强大的安全技术。这些技术主要包括: 1 加密和解密技术; 2 JAVA源代码保护技术; 3 数据完整性保护技术(数字签名和消息摘要); 4 数字证书技术; 5 其它更高级的技术(包括Kerberos技术和JAVA GSS-API技术等等); Java加密扩展即Java Cryptography Extension,简称JCE。它是Sun的加密服务软件,包含了加密和密匙生成功能。JCE是JCA(Java Cryptography Architecture)的一种扩展。 JCE没有规定具体的加密算法,但提供了一个框架,加密算法的具体实现可以作为服务提供者加入。除了JCE框架之外,JCE软件包还包含了SunJCE服务提供者,其中包括许多有用的密码算法和数字签名算法,例如DES、RSA、DSA、SHA、MD5等。使用JCE进行加密解密非常方便,在后面我们将详细的讲述这一技术。 4 如何最大限度的保证应用的安全性系统被挂了我们可以重装系统,但是应用程序和服务的敏感数据和信息被改了或者被盗窃了那我们只有祈祷上帝,希望攻击者能够良心发现。不过千万不要沮丧,我们可以利用JAVA为我们提供的一些武器来增强我们的信心,利用这些东西至少可以挡住99%以上的攻击行为,使我们的损失最小化。(1) 使用JCE技术来加密敏感信息和保护敏感信息的数字摘要;(2) 使用JAVA Logging API建立日志和审核机制;(3) 使用JAAS进行用户身份认证和授权;(4) 利用数字签名技术保证数据的完整性;(5) 利用自定义的类加载器来加载加密后的JAVA类文件以抗击JAVA源代码分析和攻击;(6) 使用JCE来构筑安全通讯协议来保证数据传输的安全。 5使用JCE技术来加密敏感信息和保护敏感信息的数字摘要目前SUN公司的最新版本的JDK(JDK1.5)包含以下几种密码算法:(1)DES:DES(数据加密标准)是由 IBM 于上世纪 70 年代发明的,美国政府将其采纳为标准。它是一种 56 位分组密码。(2)TripleDES:该算法被用来解决使用 DES 技术的 56 位时密钥日益减弱的强度,其方法是:使用两个密钥对明文运行 DES 算法三次,从而得到 112 位有效密钥强度。TripleDES 有时称为 DESede(表示加密、解密和加密这三个阶段)。(3)AES:AES(高级加密标准)取代 DES 成为美国标准。它是由 Joan Daemen 和 Vincent Rijmen 发明的,也被称为 Rinjdael 算法。它是 128 位分组密码,密钥长度为 128 位、192 位或 256 位。(4)RC2、RC4、和 RC5:这些算法来自领先的加密安全性公司 RSA Security。(5)Blowfish:这种算法是由 Bruce Schneier 开发的,它是一种具有从 32 位到 448 位(都是 8 的整数倍)可变密钥长度的分组密码,被设计用于在软件中有效实现微处理器。(6)其它:包括RSA、MD5、SHA、DAS等。除此而外,由于JCE的开放性,JDK还支持第三方提供的密码算法。在系统中,我们首先应该搞清楚的问题是:(1)该对什么数据加密?(2)选择什么样的加密算法?(3)如何实现加密?回答这三个问题对于不同的系统有不同的说法,通常是根据应用来定的。打个比方,银行系统有两个字段:开户人帐号和开户人密码,那我们再权衡后会选择开户人密码。对于数据库系统而言,大规模加密会导致系统反映迟钝,严重影响效率。因此选择加密算法的时候必须考虑这点,对于数据加密我们用对称加密算法,对于对称加密算法密钥等信息我们可以使用公钥加密算法,对于流媒体我们可以采用流加密方法。下面我们来回答第三个问题,如何实现加密。通过JCE使用对称加密算法的过程一般是这样的: 1 引入JCE加密算法库; 2 将要加密的数据变成一个字节数组; 3 调用Cipher.getInstance建立一个Cipher实例; 4 选择加密模式,调用init方法传递密钥并初始化Cipher; 5 调用doFinal方法,传递要加密的字节数组进行加密; 6 将加密后的字节数组转变为普通的字符串类型。需要注意的是因为加密后的密文是未知的,其中可能出现对应于ASCII行尾符或者文件尾符的密文,因此一般在应用中我们采用BASE64编码进行处理。下面我们给出示范源代码(实际使用的代码比这个要复杂得多,并要使用BASE64编解码): /* writer:fleshwound
/
package myprojects.jce_smatrix; import javax.crypto.*; import java.security.*; import java.io.*; public class Jce_smatrix { public static String s="welcome to smatrix!";
public Jce_smatrix()
{
System.out.println("A JCE example begin:\n");
System.out.println("plain text is:"+s+"\n");
}
public static void main(String args[]) throws Exception
{
Jce_smatrix jceexa=new Jce_smatrix();

//初始化密钥
KeyGenerator kg=KeyGenerator.getInstance("Blowfish");
kg.init(128);
SecretKey key=kg.generateKey();
//加密
Cipher c=Cipher.getInstance("Blowfish/ECB/PKCS5padding");
c.init(Cipher.ENCRYPT_MODE,key);
byte[] content=s.getBytes("UTF8");
System.out.println("before ENCRYPT:");
for(int i=0;ihttp://www.smatrix.org/download/show.php?id=52

(reference: http://www.secwiki.com/wiki/index.php/æ•°æ®åŠ å¯†)
1. 配置jce开发环境
虽然JDK1.4将java安全包包含在核心库中,但如果不对jce进行配置,也没办法使用jce进行开发。
首先从sun网上下载jce1.2.2(我在网上看到的都是下载一个包,没用sun默认的),然后把解压得到的lib里面的所有jar文件拷到your_jdk\jre\lib\ext(your_jdk为你的jdk安装目录),编辑your_jdk\jre\lib\security\java.policy文件,在最后加上
grant codeBase "file:${java.home}/lib/ext/sunjce_provider.jar" { permission java.io.FilePermission "file:${java.home}/lib/ext/sunjce_provider.jar", "read"; permission java.lang.RuntimePermission "getProtectionDomain"; permission java.security.SecurityPermission "putProviderProperty.SunJCE";};
给sunjce_provider授予访问权限 your_jdk\jre\lib\security\java.security里面配置了可选的provider类型,这里用默认配置就行了。(这里的provider也可由用户自己用别的厂商提供的包替换,我不是太清楚怎么作)。 由于我是用Jbuilder开发,必须加上对相关库的链接。所以就在Project下面的properties的library设置里面加上对jce1_2_2.jar、sunjce_provider.jar的引用,配置完毕之后,就可以进行JCE相关的程序编写了。
我用jce写了一个des加解密的小程序,不知道为什么,运行起来很慢。感觉是装载provider花时间。偶N不了解为什么sun会用provider这种安装组件的方式,很麻烦,也很没必要。我在eclipse下配置了半天都没弄好,郁闷死了
(Reference: http://www.cnblogs.com/lzcarl/archive/2005/07/27/201477.html)

2. 使用JCE进行DES加密

1、 引言
随着科技的日益发达,人们在对方便性要求逐渐提高的同时,对安全性的要求也日益提高。而使用加密的方法保护文件已成为计算机安全应用中重要的组成部分。DES加密方法作为一种世界标准的加密形式, 已经15 年历史了,虽然有些老, 可还算是比较可靠的算法,因此在加密应用中还是有一定的市场。
2、 DES算法简介
DES是一个分组加密算法,他以64位为分组对数据加密。同时DES也是一个对称算法:加密和解密用的是同一个密钥。它的密钥长度是56位(因为每8位的最后一位都用作奇偶校验),密钥可以是任意的56位的数,而且可以任意时候改变。其中有极少量的数被认为是弱密钥,但是很容易避开他们,所以保密性依赖于密匙。虽然DES算法实现起来比较简单,加解密速度也比较快,但是因为密钥过短,比较容易被破解,地位就逐渐被其他的加密算法取代。
3、 JCE简介及配置见http://www.cnblogs.com/archive/2005/07/27/201477.html
4、 DES加密程序
这个加密程序的密钥采取自动生成的方式,并把生成的密钥保存在文件中。这样只要拥有密钥文件就可以加解密从而避免了用户忘记密钥的问题,同时,由于每次加密使用的密钥都不相同,提高了安全性。
在设计这个加密程序的时候,考虑到以后这个程序的复用性,我将程序分为三个类:
BytesCipher:提供了对byte型数组进行加解密的功能,同时还可以生成DES密钥。
FileCipher:提供了对文件加解密的功能(先将文件转换成byte型数组),同时可以保存、读取密钥。
DESCipher:主程序类。我将整个程序设计成命令行形式,仿照dos命令,用户输入命令和相应的参数就可以执行程序,同时可以查看帮助。
在异常处理方面,我把程序执行过程中抛出的异常进行了二次处理,加上了自己赋予的异常信息,再发送到主函数中统一处理。
BytesCipher类代码:
import java.security.*;import javax.crypto.spec.*;import javax.crypto.*;public class BytesCipher { //默认使用des加密 private static String algorithm = "DES"; /** *//** * 使用JDK中的JCE生成密钥 */ public static byte[] generateKey() throws Exception { try { //生成一个可信任的随机数源 SecureRandom sr = new SecureRandom(); //为我们选择的DES算法生成一个KeyGenerator对象 KeyGenerator kg = KeyGenerator.getInstance(algorithm); kg.init(sr); //生成密钥 SecretKey key = kg.generateKey(); //返回密钥的二进制形式 return key.getEncoded(); } catch (Exception e) { throw new Exception("没有这种加密算法"); } } /** *//** * 根据密钥对明文进行加密,返回密文 */ public static byte[] encrypt(byte[] plaintext, byte[] key) throws Exception { try { //产生一个可信任的随机数源 SecureRandom sr = new SecureRandom(); //从原始密钥数据创建DESKeySpec对象 DESKeySpec dks = new DESKeySpec(key); //创建一个密钥工厂,然后用它把DESKeySpec转换成Secret Key对象 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm); SecretKey keySpec = keyFactory.generateSecret(dks); //Cipher对象实际完成加密操作 Cipher cipher = Cipher.getInstance(algorithm); //用密钥初始化Cipher对象 cipher.init(Cipher.ENCRYPT_MODE, keySpec, sr); //执行加密操作 byte[] cryptotext= cipher.doFinal(plaintext); return cryptotext; } catch (InvalidKeyException e) { throw new Exception("密钥非法"); } catch (NoSuchAlgorithmException e) { throw new Exception("没有这种加密算法"); } catch (BadPaddingException e) { throw new Exception("加密失败"); } } /** *//** * 根据密钥对密文进行加密,返回明文 */ public static byte[] decrypt(byte[] cryptotext, byte[] key) throws Exception {//上面与加密代码相同 cipher.init(Cipher.DECRYPT_MODE, keySpec, sr); //执行解密操作,下面与加密代码基本相同}

FileCipher类代码:

import java.io.*;import java.nio.*;import java.nio.channels.*;public class FileCipher { //明文 private static byte[] plaintext=null; //密文 private static byte[] cryptotext=null; //密钥 private static byte[] key; /** *//** * 从文件中读数据,返回byte型数组 */ private static byte[] readFile(String filename) throws Exception { try { File f = new File(filename); FileInputStream fs = new FileInputStream(f); FileChannel fc = fs.getChannel(); //只要文件大小不超过2G(int的范围)即可加密 int size = (int) fc.size(); //采用DirectBuffer的方式读文件数据,属于JDK1.4的API,处理大容量数据更快 ByteBuffer buffer=ByteBuffer.allocateDirect(size); //通过文件通道将文件内容读入ByteBuffer fc.read(buffer); //buffer操作完一次必须flip回到最初位置,才能再进行操作 buffer.flip(); byte[] byteArray=new byte[size]; //将缓冲区内容存入数组 buffer.get(byteArray); buffer.clear(); fc.close(); fs.close(); return byteArray; } catch (FileNotFoundException e) { throw new Exception("文件未找到"); } catch (IOException e) { throw new Exception("文件操作错误"); } } /** *//** * 将byte型数组内容写入文件 */ private static void writeFile(String filename,byte[] byteArray) throws Exception { //与读文件类似、省略 } /** *//** * 根据密钥对制定文件加密 */ public static void fileEncrypt(String filename,byte[] key) throws Exception { plaintext=readFile(filename); cryptotext=BytesCipher.encrypt(plaintext,key); writeFile(filename,cryptotext); } /** *//** * 根据密钥对指定文件解密 */ public static void fileDecrypt(String filename,byte[] key) throws Exception { cryptotext=readFile(filename); plaintext=BytesCipher.decrypt(cryptotext,key); writeFile(filename,plaintext); } /** *//** * 将得到的密钥保存到文件 */ public static void saveKey(String filename,byte[] key) throws Exception { writeFile(filename,key); } /** *//** * 从文件中读出密钥,返回byte数组形式 */ public static byte[] loadKey(String filename) throws Exception { key=readFile(filename); return key; }}
DESCipher类代码:

import java.io.*;public class DESCipher{ /** *//** * 打印命令的帮助信息 */ public static void printHelp() { //都是输出函数、省略 } public static void main(String[] args) { try{ //用户没有输入参数,则打印帮助 if(args.length==0) { printHelp(); return; } //用户输入参数个数不够,则提示用户,并打印帮助 if(args.length<3) key="BytesCipher.generateKey();" key2="FileCipher.loadKey(args[2]);" href="http://www.cnblogs.com/lzcarl/archive/2005/09/24/243471.html">http://www.cnblogs.com/lzcarl/archive/2005/09/24/243471.html)