| « | April 2026 | » | | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | | | |
| 公告 |
| 暂无公告... |
| Blog信息 |
|
blog名称:天地无用 日志总数:55 评论数量:43 留言数量:1 访问次数:197304 建立时间:2008年4月17日 |

| |
|
Java加密扩展基础(转) 软件技术
kkk888929 发表于 2008/4/22 16:27:23 |
| Java加密扩展(JCE)现在已经成为Java SDK 1.4的核心组成部分。JCE基本上是一组提供加密框架和实现、密钥生成和协商以及消息认证代码(MAC)算法的Java包。 不过你可得注意了,虽然JCE目前是Java SDK 1.4核心组成包,不过我们将演示如何用Java SDK 1.2 或者更高版本对其进行配置。此外,本文还要讨论如何在动态安装的情况下使用安全供应者。最后我会演示创建密钥和密码的过程,以及如何实施基本的数据加密和解密。 什么是安全供应者? 安全供应者就是承担特定安全机制的实现。有些供应者是完全免费的,而另一些供应者则需要付费。提供安全供应者的公司有IBM、Bouncy Castle和RSA等。今后我们将对Bouncy Castle的RSA实现做阐述。Sun 提供了如何实现你自己供应者的细节。 静态安装 在使用或安装JCE之前,你必须首先从Sun的网站获取相关的Java库。JCE包含了Sun自产的安全供应者SunJCE。为了在你的默认供应者列表中静态添加SunJCE,你需要编辑安全属性文件; <java-home>\jre\lib\security\java.security (Win32) <java-home>/jre/lib/security/java.security (UNIX) 比方说,假如你在Windows计算机的C:\jdk1.3文件夹下面安装了JDK,那么你需要编辑如下文件: C:\jdk1.3\jre\lib\security\java.security 为了安装SunJCE你还得在以上文件中添加以下代码行: security.provider.n=com.sun.crypto.provider.SunJCE 请用正确的供应者替代以上代码中的n。 程序清单A 演示了如何查看安装供应者的信息。程序清单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 =================== 动态安装 程序清单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()); } } ========================== 如上所述,当你在安装供应者的时候,你必须提供一个数字表示其优先级。在调用实现时,JVM会根据优先级搜索整个安装的安全供应者并用找到的第1个供应者实现请求的算法。你还可以通过在方法调用中包含其他参数的方法显式地调用给定的供应者,以后我们可以了解这一点。 实现细节 JCE API由很多类和接口组成。它们具有若干种算法和安全特性。本文首先讨论常用的对称算法数据加密标准DES(Data Encryption Standard)。 产生密钥 程序清单D 说明了如何初始化KeyGenerator和产生密钥。 =============================== 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的一个实例。这可以由调用KeyGenerator类的静态方法getInstance完成。我们用普通的、不带模式或者方案的DES算法。可选地,你可以如下所示传递参数: DES/ECB/PKCS5Padding 这样就提供了带ECB(电子密码本)模式和PKCS#5补充风格的DES算法。你还可以传递第2个字符串参数指定要使用的供应者实现,不过这并不是必需的: KeyGenerator kg = KeyGenerator.getInstance("DES"); 获得自己的KeyGenerator,之后就可以调用generateKey方法获得我们的密钥: Key key = kg.generateKey(); 产生密码 我们采用生成密钥相同的方式生成一个密码。我们必须调用Cipher类的静态方法getInstance。该方法的参数同KeyGenerator完全一样: Cipher cipher = Cipher.getInstance(“DES”); 程序清单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(); } } } ======================= 数据加密和解密 加密是在字节级别上进行的,所以数据的各个方面一个不落全都会被加密。一旦获得了密钥和密码,你就可以对数据进行安全操作了。值得注意的是密钥和密码必须采用同类算法。你不能用DESede初始化密钥却用DES初始化密码。Cipher对象采用同样的方法对数据加密和解密,所以你必须首先对其初始化以便它知道对数据进行怎样的操作: cipher.init(Cipher.ENCRYPT_MODE, key); 以上方法调用对Cipher对象初始化以准备加密数据。加密数据的最简单办法就是调用Cipher对象的doFinal方法,同时传递字节数组: byte[] data = “Hello World!”.getBytes(); byte[] result = cipher.doFinal(data); result现在包含传递数据的加密形式。对同一数据解密也很容易。但在解密之前,我们必须重新初始化Cipher对象以便其准备解密: cipher.init(Cipher.DECRYPT_MODE, key); 之后即可解密: byte[] original = cipher.doFinal(result); 现在original应该和data.完全相同。程序清单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,以及采用对称加密算法对简单消息加密和解密。在本系列的第2部分,我还会利用以上的这些知识运用到真实环境下来,教你如何编写简单的封装类以结合套接字加密应用程序的所有网络流量。 -------------------------------------------------------- 用Java的加密机制来保护你的数据 作者:BUILDER.COM Tuesday, June 18 2002 10:30 AM Java开发工具包(JDK)对加密和安全性有很好的支持。其中一个优势就是其内置的对Socket通信的支持。因此,很容易做到在服务器和客户之间建立安全的数据流。 -------------------------------------------------------------------------------- 背景材料 想了解JDK的安全性内容,请查看《Java加密扩展基础》 -------------------------------------------------------------------------------- 流 Java streams 是一个强大的编程工具。java.io包提供了很多标准的流类型,并能很容易的建立自己的流类型。流的一个有用的特点是和链表一样的简单处理过程。表A是一个用链表读取文本的例子。这段代码将FileReader和BufferedReader链接起来。我们在用客户机/服务器应用程序的时候也会用到类似的概念。 ========================= Listing A—Reading text files BufferedReader br = new BufferedReader( new FileReader(“c:\foo.txt”)); String line = null; while((line = br.readLine()) != null) { System.out.println(line); } ========================= 关键字 对于验证来说,关键字很重要,表B (KeyGen.java)提供了一个称为getSecretKey的标准方法。通过运行KeyGen来产生一个关键字。因为我们采用同步方法,所以客户机和服务器必须用相同的关键字。 ========================== Listing B—KeyGen.java /* * Created by IntelliJ IDEA. * User: jbirchfield * Date: Mar 19, 2002 * Time: 9:33:22 AM */ import com.sun.crypto.provider.SunJCE; import javax.crypto.KeyGenerator; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.Security; public class KeyGen { public static final String KEY_FILE = "secret.key"; public static final String ALGORITHM = "DES"; public static void main(String[] args) { Security.addProvider(new SunJCE()); new KeyGen(); } public KeyGen() { KeyGenerator kg = null; try { kg = KeyGenerator.getInstance(ALGORITHM); Key key = kg.generateKey(); writeKey(KEY_FILE, key); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } private void writeKey(String filename, Object o) { try { FileOutputStream fos = new FileOutputStream(filename); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(o); oos.flush(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } public static Key getSecretKey() { Security.addProvider(new SunJCE()); FileInputStream fis = null; try { fis = new FileInputStream(KEY_FILE); } catch (FileNotFoundException e) { e.printStackTrace(); } Key key = null; try { ObjectInputStream ois = null; ois = new ObjectInputStream(fis); key = null; key = (Key) ois.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println("key = " + key); return key; } } ========================== 安全socket 我们从一个简单的类开始,它提供我们在普通socket对象之上的加密。表C (SecretSocket.java) 包含了两段代码-Socket和Key对象。我们的构造器创建了变量并初始化了密码: outCipher = Cipher.getInstance(algorithm); outCipher.init(Cipher.ENCRYPT_MODE, key); inCipher = Cipher.getInstance(algorithm); inCipher.init(Cipher.DECRYPT_MODE, key); =========================================== Listing C—SecretSocket.java /* * Created by IntelliJ IDEA. * User: jbirchfield * Date: Mar 20, 2002 * Time: 9:07:51 AM */ import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Security; public class SecretSocket { private Key key = null; private Cipher outCipher = null; private Cipher inCipher = null; private CipherInputStream cis = null; private CipherOutputStream cos = null; private Socket socket = null; private String algorithm = "DES"; public SecretSocket(Socket socket, Key key) { this.socket = socket; this.key = key; algorithm = key.getAlgorithm(); initializeCipher(); } private void initializeCipher() { try { outCipher = Cipher.getInstance(algorithm); outCipher.init(Cipher.ENCRYPT_MODE, key); inCipher = Cipher.getInstance(algorithm); inCipher.init(Cipher.DECRYPT_MODE, key); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } } public InputStream getInputStream() throws IOException { InputStream is = socket.getInputStream(); cis = new CipherInputStream(is, inCipher); return cis; } public OutputStream getOutputStream() throws IOException { OutputStream os = socket.getOutputStream(); cos = new CipherOutputStream(os, outCipher); return cos; } } =========================================== 因为socket是双向的通信,所以我们采用两个密码。加密输出的数据并解密输入的数据。我们使用getInputStream()和getOutputStream(),这两种方法来加密合解密通用的输入和输出的经过包装的数据流。见表D 。 ====================================== Listing D public InputStream getInputStream() throws IOException { InputStream is = socket.getInputStream(); cis = new CipherInputStream(is, inCipher); return cis; } public OutputStream getOutputStream() throws IOException { OutputStream os = socket.getOutputStream(); cos = new CipherOutputStream(os, outCipher); return cos; } ===================================== 在JCE的javax.crypto包中包含CipherInputStream和CipherOutputStream这两种流类型。他们接收输入输出的流对象和密码对象。 Socket 服务器 开始写我们的socket服务器类吧。表E (SecretSocketServer.java)是一个完整的列表。SecretSocketServer在一个端口打开ServerSocket,当接收到连接时,使用SocketHandler产生一个线程来操作连接。 ===================================== Listing E—SecretSocketServer.java /* * Created by IntelliJ IDEA. * User: jbirchfield * Date: Mar 20, 2002 * Time: 9:32:17 AM */ import java.net.ServerSocket; import java.net.Socket; import java.io.IOException; public class SecretSocketServer { public static void main(String[] args) { new SecretSocketServer(); } public SecretSocketServer() { ServerSocket ss = null; try { ss = new ServerSocket(4444); } catch (IOException e) { e.printStackTrace(); } while(true) { try { System.out.println("Waiting..."); Socket s = ss.accept(); SocketHandler h = new SocketHandler(s); Thread t = new Thread(h); t.start(); } catch (IOException e) { e.printStackTrace(); } } } } ===================================== Socket 句柄 表F (SocketHandler.java) 确定一个socket对象,通过KeyGen来定位关键字,并建立一个SecretSocket 对象。. Key key = KeyGen.getSecretKey(); this.ss = new SecretSocket(s, key); ==================================== Listing F—SocketHandler.java /* * Created by IntelliJ IDEA. * User: jbirchfield * Date: Mar 20, 2002 * Time: 9:34:22 AM */ import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.security.Key; public class SocketHandler implements Runnable { private Socket s = null; private SecretSocket ss = null; private InputStream in = null; public SocketHandler(Socket s) { this.s = s; Key key = KeyGen.getSecretKey(); this.ss = new SecretSocket(s, key); try { in = ss.getInputStream(); } catch (IOException e) { e.printStackTrace(); } } public void run() { boolean bool = true; while (bool) { bool = listen(); } try { s.close(); } catch (IOException e) { e.printStackTrace(); } } public boolean listen() { int aByte; try { while ((aByte = in.read()) >= 0) { System.out.println((char)aByte); } } catch (IOException e) { System.out.println("returning false..."); } return false; } } ==================================== 注意表F中的ss对SocketHandler来说是一个实变量。所有的socket 处理都是通过SecretSocket而不是Socket对象。然后我们使用下面的代码: in = ss.getInputStream(); 记住,在SecretSocket中,getInputStream是和CipherInputStream以及InputStream相结合的。因为SocketHandler 是一个可执行的界面,我们为它生成一个run()方法。这个方法只是在等待socket的数据: boolean bool = true; while (bool) { bool = listen(); } listen方法用来监听socket 。 int aByte; while ((aByte = in.read()) >= 0) { system.out.println((char)aByte); } Socket 客户 现在我们来看看客户端。见表G 。客户端的工作和服务器端很相似,只是反过来了。首先,我们创立一个套接字连接到服务器。使用KeyGen 找到关键字,创立一个安全套接字(SecretSocket)。然后我们利用它的OutputStream给服务器发送数据: Key key = KeyGen.getSecretKey(); Socket s = new Socket("localhost", 4444); SecretSocket ss = new SecretSocket(s, key); OutputStream os = ss.getOutputStream(); os.write("Hello World!".getBytes()); os.flush(); os.close(); s.close(); =================================== Listing G—SecretSocketClient.java /* * Created by IntelliJ IDEA. * User: jbirchfield * Date: Mar 20, 2002 * Time: 9:39:32 AM */ import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.security.Key; public class SecretSocketClient { public SecretSocketClient() { try { Key key = KeyGen.getSecretKey(); Socket s = new Socket("localhost", 4444); SecretSocket ss = new SecretSocket(s, key); OutputStream os = ss.getOutputStream(); os.write("Hello World!".getBytes()); os.flush(); os.close(); s.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { new SecretSocketClient(); } } =================================== 总结 通过JCE中的java流和链表,我们可以轻松的加密基于socket的网络通信。 下载文章中的代码 KeyGen.java SecretSocket.java SecretSocketServer.java SocketHandler.java SecretSocketClient.java ================================== /* * Created by IntelliJ IDEA. * User: jbirchfield * Date: Mar 19, 2002 * Time: 9:33:22 AM */ import com.sun.crypto.provider.SunJCE; import javax.crypto.KeyGenerator; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.Security; public class KeyGen { public static final String KEY_FILE = "secret.key"; public static final String ALGORITHM = "DES"; public static void main(String[] args) { Security.addProvider(new SunJCE()); new KeyGen(); } public KeyGen() { KeyGenerator kg = null; try { kg = KeyGenerator.getInstance(ALGORITHM); Key key = kg.generateKey(); writeKey(KEY_FILE, key); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } private void writeKey(String filename, Object o) { try { FileOutputStream fos = new FileOutputStream(filename); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(o); oos.flush(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } public static Key getSecretKey() { Security.addProvider(new SunJCE()); FileInputStream fis = null; try { fis = new FileInputStream(KEY_FILE); } catch (FileNotFoundException e) { e.printStackTrace(); } Key key = null; try { ObjectInputStream ois = null; ois = new ObjectInputStream(fis); key = null; key = (Key) ois.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println("key = " + key); return key; } } ================================== /* * Created by IntelliJ IDEA. * User: jbirchfield * Date: Mar 20, 2002 * Time: 9:07:51 AM */ import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Security; public class SecretSocket { private Key key = null; private Cipher outCipher = null; private Cipher inCipher = null; private CipherInputStream cis = null; private CipherOutputStream cos = null; private Socket socket = null; private String algorithm = "DES"; public SecretSocket(Socket socket, Key key) { this.socket = socket; this.key = key; algorithm = key.getAlgorithm(); initializeCipher(); } private void initializeCipher() { try { outCipher = Cipher.getInstance(algorithm); outCipher.init(Cipher.ENCRYPT_MODE, key); inCipher = Cipher.getInstance(algorithm); inCipher.init(Cipher.DECRYPT_MODE, key); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } } public InputStream getInputStream() throws IOException { InputStream is = socket.getInputStream(); cis = new CipherInputStream(is, inCipher); return cis; } public OutputStream getOutputStream() throws IOException { OutputStream os = socket.getOutputStream(); cos = new CipherOutputStream(os, outCipher); return cos; } } ================================== /* * Created by IntelliJ IDEA. * User: jbirchfield * Date: Mar 20, 2002 * Time: 9:32:17 AM */ import java.net.ServerSocket; import java.net.Socket; import java.io.IOException; public class SecretSocketServer { public static void main(String[] args) { new SecretSocketServer(); } public SecretSocketServer() { ServerSocket ss = null; try { ss = new ServerSocket(4444); } catch (IOException e) { e.printStackTrace(); } while(true) { try { System.out.println("Waiting..."); Socket s = ss.accept(); SocketHandler h = new SocketHandler(s); Thread t = new Thread(h); t.start(); } catch (IOException e) { e.printStackTrace(); } } } } ================================== /* * Created by IntelliJ IDEA. * User: jbirchfield * Date: Mar 20, 2002 * Time: 9:34:22 AM */ import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.security.Key; public class SocketHandler implements Runnable { private Socket s = null; private SecretSocket ss = null; private InputStream in = null; public SocketHandler(Socket s) { this.s = s; Key key = KeyGen.getSecretKey(); this.ss = new SecretSocket(s, key); try { in = ss.getInputStream(); } catch (IOException e) { e.printStackTrace(); } } public void run() { boolean bool = true; while (bool) { bool = listen(); } try { s.close(); } catch (IOException e) { e.printStackTrace(); } } public boolean listen() { int aByte; try { while ((aByte = in.read()) >= 0) { System.out.println((char)aByte); } } catch (IOException e) { System.out.println("returning false..."); } return false; } } ================================== /* * Created by IntelliJ IDEA. * User: jbirchfield * Date: Mar 20, 2002 * Time: 9:39:32 AM */ import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.security.Key; public class SecretSocketClient { public SecretSocketClient() { try { Key key = KeyGen.getSecretKey(); Socket s = new Socket("localhost", 4444); SecretSocket ss = new SecretSocket(s, key); OutputStream os = ss.getOutputStream(); os.write("Hello World!".getBytes()); os.flush(); os.close(); s.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { new SecretSocketClient(); } } ================================== JCE提供了一个使用非对称加密法的API 作者:BUILDER.COM Tuesday, January 14 2003 11:47 AM 使用对称加密系统有一些缺点。由于只有一个秘钥能够解密密文,所以你必须把它随密文一起送出去,这就会引起一些风险:有人可能会截取到密钥。此外,每个接收者都必须有一个不同的密钥,所以你还要解决密钥管理的问题。 通过使用公钥和密钥,非对称加密法圆满地解决了这些问题。使用你的公钥,任何人都可以加密只能由你解开的信息。尽管你还是必须保护好自己的密钥,但是你的公钥可以任意发放或者放在某个中央库供人取用。在本文中,我将讲解如何使用Java扩展加密法(JCE)来利用非对称加密法的优势。在开始之前,先要下载完整的示例代码。 -------------------------------------------------------------------------------- 背景材料 本系列的头两篇文章讲解了对称加密: 《掌握Java扩展加密法(JCE)的基础》 《使用Java的加密特性保护你的网络信息流》 -------------------------------------------------------------------------------- 使用JCE JCE提供了一个API来使用非对称加密法。第一步是使用下面的代码片段生成一对公钥和私钥: KeyPairGenerator kpg = null; kpg = KeyPairGenerator.getInstance("RSA", "BC"); kpg.initialize(1024, new SecureRandom()); KeyPair kp = kpg.generateKeyPair(); PrivateKey priKey = kp.getPrivate(); PublicKey pubKey = kp.getPublic(); 在这里,KeyPairGenerator的getInstance方法接受两个参数——实现和提供者。本文所有的例子都把Bouncy Castle(BC)作为提供者。在获得了KeyPairGenerator的实例之后,你必须将它初始化,指出你所期望的安全级别。在本文的例子里,你正在生成一对1,024位的密钥。下一步,你要创建KeyPair。通过KeyPair对象,你可以访问公钥和私钥。在我们的示例代码里,这对密钥被保存在文件系统里,供以后使用。 要注意的一点内容 有了这些密钥,加密数据就简单了。但是你必须小心在加密和解密数据的时候不要超过区块大小。让我们看看示例代码里crypt方法的一个代码段: cipher.init(type, key); out = new ByteArrayOutputStream(); int s = cipher.getBlockSize(); int r = 0; for (int t = 0; t < text.length; t += s) { if (text.length - t <= s) { r = text.length - t; } else { r = s; } out.write(cipher.doFinal(text, t, r)); } out.flush(); out.close(); 在初始化密码之后,你就要创建一个新的ByteArrayOutputStream来容纳加密/解密数据。然后使用区块大小,你要在目标文本内往复并加密/解密文本块,这些加密/解密块要小于或者等于我们密码的区块大小。如果你尝试加密/解密的数据比区块大小要大,那么这样的操作会失败,而且会出现异常。 更高的安全性 如果使用对称系统加密,那么你就在冒风险:有人会截取密码并读取密文。使用非对称的方法,明文被公钥和私钥保护起来。正如示例代码所示,只需使用一些辅助方法读取和编写密钥,实现信息交换的安全性是很容易的。 |
|
|