了解Java密码扩展的基础

来源:中国IT实验室发布时间:2006-11-15
   Java密码扩展(The Java Cryptography Extension),是JDK1.4的一个重要部分,基本上,他是由一些包构成的,这些包形成了一个框架并实现了一些加密,密钥生成算法和协议,消息认证码等算法,这篇文章将想你介绍JCE的安装和使用。
    值得注意的是,尽管JCE是JDK1.4的核心包的一部分,我们将首先用JDK1.2及高一点的版本向你演示一下如果安装配置JCE(静态安装)。稍后,将向你介绍如何在不安装的情况下使用JCE(动态安装)。最后,将演示怎么生成密钥和密码,及如果进行基本的加密.解密。
提供者是什么?
    提供者是特定加密算法的实现者,有的提供者(提供的加密技术)是免费的,有的不免费,IBM, Bouncy Castle, 和 RSA都是一些(加密)提供者.在本文的后面,我们将考察一下来自Bouncy Castle的RSA算法。Sun 也向大家说明了如果实现自己的提供者(需要符合jDK的一些约定)。
静态安装
    在安装和使用JCE之前,你需要从 Sun Web site(这里是以暗中sun的提供者为例).获得他的安装包,JCE有sun他自己的安全提供者-sunJCE,为了吧sunJCE静态的安装到默认的提供者列表中,你需要修改安全属性文件:
•    <java-home>jrelibsecurityjava.security (Win32) 
•    <java-home>/jre/lib/security/java.security (UNIX)
如果你把JDK安装在C:jdk1.3,你需要编辑以下文件:
C:jdk1.3jrelibsecurityjava.security 
为了安装SunJCE,你需要在以上文件中加入:
security.provider.n=com.sun.crypto.provider.SunJCE
把n用你加入的提供者的优先级代替(注意:序号要保持递增,不能跳过,但可以调整前后顺序)。
Listing A 用于查看你安装过的提供者的信息,结果在Listing B中列出,显示提供者的能力,比如说可用的加密算法。
Listing A: ProviderInformation.java
import java.security.Provider;
import java.security.Security;
import java.util.Set;
import java.util.Iterator;
public class ProviderInformation {
    public static void main(String[] args) {
        Provider[] providers = Security.getProviders();
        for (int i = 0; i < providers.length; i++) {
            Provider provider = providers[i];
            System.out.println("Provider name: " + provider.getName());
           System.out.println("Provider information: " + provider.getInfo());
            System.out.println("Provider version: " + provider.getVersion());
            Set entries = provider.entrySet();
            Iterator iterator = entries.iterator();
           while (iterator.hasNext()) {
                System.out.println("Property entry: " + iterator.next());
            }
        }
    }
}
Listing B: ProviderInformation.java output
Provider name: SUN
Provider information: SUN (DSA key/parameter generation; DSA signing; SHA-1, MD5 digests; SecureRandom; X.509 certificates; JKS keystore)
Provider version: 1.2
Property entry: Alg.Alias.KeyFactory.1.2.840.10040.4.1=DSA
Property entry: Alg.Alias.Signature.1.2.840.10040.4.3=SHA1withDSA
Property entry: Alg.Alias.KeyPairGenerator.OID.1.2.840.10040.4.1=DSA
Property entry: Signature.SHA1withDSA KeySize=1024
Property entry: Signature.SHA1withDSA ImplementedIn=Software
动态安装:Listing c 说明了如何在运行时动态加载安全提供者,要注意的是,当你用Security.addProvider(…)加载提供者时,它是对整个JVM环境都有用的;
Listing C: DynamicProvider.java
import java.security.Security;
public class DynamicProvider {
    public static void main(String[] args) {
        // This is all there is to it!
        Security.addProvider(new com.sun.crypto.provider.SunJCE());
    }
}
    如前所述,当你安装一个提供者时,你用n来指明此提供者的优先级,但一个算法的实例被调用时,JVM将按照提供的优先级来在已经安装的提供者中查找可用的实现,并使用他首先找到的可用算法。你也可用在调研时加上附加参数来指明要在那个提供者中寻找使用的算法。

实现细节:
JCE API包含了大量的为实现安全特性的类和接口,首先,我们做一个DES对称加密的例子。

生成密钥:
Listing D 展示了如果初时化密钥生成器来生成密钥;
Listing D: DESKeyGenerator.java
 

 

import javax.crypto.KeyGenerator;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
public class DESKeyGenerator {
    public static void main(String[] args) {
        Security.addProvider(new com.sun.crypto.provider.SunJCE());
        try {
            KeyGenerator kg = KeyGenerator.getInstance("DES");
            Key key = kg.generateKey();
            System.out.println("Key format: " + key.getFormat());
            System.out.println("Key algorithm: " + key.getAlgorithm());
        }
        catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }
}
  为了生成密钥,我们首先要初始化密钥生成器,这一步可以通过调用KeyGenerator类的静态方法getInstance来实现。我们所用的vanilla DES算法没有模式和填充模型。你同样可以(在getInstance(""))传入DES/ECB/PKCS5Padding来指明模式(ECB)和填充模式(PKCS5Padding);也可以传入另外一个参数指明所用的提供者,不过这是可选的;
KeyGenerator kg = KeyGenerator.getInstance("DES");

一旦我们有了特定的密钥生成对象,我们就可以用他得到密钥:
Key key = kg.generateKey();
生成密码:
生成密码的过程跟生成密钥类似,需要调用Cipher类的getInstance方法,参数要跟生成密钥时用的参数保持一致;
Cipher cipher = Cipher.getInstance(“DES”);

Listing E 说明了如果操作:
Listing E: DESCipherGenerator.java
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import java.security.Security;
import java.security.NoSuchAlgorithmException;
public class DESCipherGenerator {
    public static void main(String[] args) {
        Security.addProvider(new com.sun.crypto.provider.SunJCE());
        try {
            Cipher cipher = Cipher.getInstance("DES");
            System.out.println("Cipher provider: " + cipher.getProvider());
            System.out.println("Cipher algorithm: " + cipher.getAlgorithm());
        }
        catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        catch (NoSuchPaddingException e) {
            e.printStackTrace();
        }
    }
}


加解密数据
    加密是对字节的,所以保密行比较高,当你准备好了密钥和密码时,你已经做好了加密的准备,要注意的时,同一个算法要用相同的密钥和密码,比如说,你不能用DESsede的密钥,用DES的密码,密码对象用同一个方法对数据进行加密和解密,所有你要首先初时化,让他知道你要干什么:
cipher.init(Cipher.ENCRYPT_MODE, key);
这就将初始化Cipher类,以准备好加密数据,.最简单的加密方法及时对传入的字节数组调用doFinal方法:
byte[] data = “Hello World!”.getBytes();
byte[] result = cipher.doFinal(data);
Listing F 是详细的代码
Listing F: DESCryptoTest.java
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.BadPaddingException;
import java.security.Key;
import java.security.Security;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
public class DESCryptoTest {
    public static void main(String[] args) {
        Security.addProvider(new com.sun.crypto.provider.SunJCE());
        try {
            KeyGenerator kg = KeyGenerator.getInstance("DES");
            Key key = kg.generateKey();
            Cipher cipher = Cipher.getInstance("DES");
 
            byte[] data = "Hello World!".getBytes();
            System.out.println("Original data : " + new String(data));
 
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] result = cipher.doFinal(data);
            System.out.println("Encrypted data: " + new String(result));
 
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] original = cipher.doFinal(result);
            System.out.println("Decrypted data: " + new String(original));
        }
        catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        catch (NoSuchPaddingException e) {
            e.printStackTrace();
        }
        catch (InvalidKeyException e) {
            e.printStackTrace();
        }
        catch (IllegalStateException e) {
            e.printStackTrace();
        }
        catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        catch (BadPaddingException e) {
            e.printStackTrace();
        }
    }
}
总结:JCE是个功能强大的API,提供了众多的加密方法和其他安全相关的属性,我们已经找到怎样动态和静态安装JCE,并用DES对一段简单的信息进行了加密和解密,在本系列的第二部分,我们将把本文的知识用到现实的应用中去,将告诉大家如果写一个跟Socket配合使用的包装类(Wrapper),用以加密你网上交易的重要信息。