Java安全编程笔记【2】【续3】------数据内容的保护---加密和解密。

<!-- /* Font Definitions */ @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 680460288 22 0 262145 0;} @font-face {font-family:幼圆; panose-1:0 0 0 0 0 0 0 0 0 0; mso-font-alt:宋体; mso-font-charset:134; mso-generic-font-family:auto; mso-font-format:other; mso-font-pitch:auto; mso-font-signature:1 135135232 16 0 262144 0;} @font-face {font-family:"/@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 680460288 22 0 262145 0;} @font-face {font-family:楷体_GB2312; panose-1:0 0 0 0 0 0 0 0 0 0; mso-font-alt:宋体; mso-font-charset:134; mso-generic-font-family:auto; mso-font-format:other; mso-font-pitch:auto; mso-font-signature:1 135135232 16 0 262144 0;} @font-face {font-family:"/@幼圆"; panose-1:0 0 0 0 0 0 0 0 0 0; mso-font-charset:134; mso-generic-font-family:auto; mso-font-format:other; mso-font-pitch:auto; mso-font-signature:1 135135232 16 0 262144 0;} @font-face {font-family:"/@楷体_GB2312"; panose-1:0 0 0 0 0 0 0 0 0 0; mso-font-charset:134; mso-generic-font-family:auto; mso-font-format:other; mso-font-pitch:auto; mso-font-signature:1 135135232 16 0 262144 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:12.0pt; font-family:"Times New Roman"; mso-fareast-font-family:宋体;} @page Section1 {size:612.0pt 792.0pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.Section1 {page:Section1;} -->

2.7 生成非对称加密的公钥和私钥

前面几节的程序中,加密和解密使用的是同一个密钥,这种方式称为对称加密。使用对称密钥时,若 A 想让 B 向其秘密传送信息, A 必须先将密钥提供给 B ,或者由 B 将密钥提供给 A 。如果在传递密钥过程中密钥被窃取,则 A B 之间的通信就不再安全了。非对称加密解决了这一问题。它将加密的密钥和解密的密钥分开。 A 事先生成一对密钥,一个用于加密,称为公钥(公钥),一个用于解密,称为私钥。由于产生这一对密钥的一些数学特性,公钥加密的信息只能用私钥解密。这样, A 只要将公钥对外公开,不论谁就可以使用这个公钥给 A 发送秘密信息了。 A 接收到加密信息后可以用私钥打开。由于只需要传递公钥,而公钥只能加密不能解密,因此即使攻击者知道了公钥也无济于事。本节以 RSA 算法为例介绍 Java 中如何生成公钥和私钥。

实例说明

本实例演示了如何使用 Java 中定义好的类创建 RSA 公钥和私钥。

编程思路:

Java KeyPairGenerator 类提供了一些方法来创建密钥对以便用于非对称加密,密钥对创建好后封装在 KeyPair 类型的对象中,在 KeyPair 类中提供了获取公钥和私钥的方法。具体步骤如下:

1 ) 创建密钥对生成器

KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");

分析:密钥对生成器即 KeyPairGenerator 类型的对象,和 2.2.1 小节的第 1 步中介绍的 KeyGenerator 类一样, KeyPairGenerator 类是一个工厂类,它通过其中预定义

的一个静态方法 getInstance( )获取 KeyPairGenerator 类型的对象。 getInstance()方法的参数是一个字符串,指定非对称加密所使用的算法,常用的有 RSA DSA等。

2 ) 初始化密钥生成器

kpg.initialize(1024);

分析:对于密钥长度。对于 RSA 算法,这里指定的其实是 RSA 算法中所用的模的位数。可以在 512 2048 之间。

3 ) 生成密钥对

KeyPair kp=kpg.genKeyPair( );

分析:使用 KeyPairGenerator 类的 genKeyPair( )方法生成密钥对,其中包含了一对公钥和私钥的信息。

4 ) 获取公钥和私钥

PublicKey pbkey=kp.getPublic( );

PrivateKey prkey=kp.getPrivate( );

分析:使用 KeyPair 类的 getPublic( ) getPrivate( )方法获得公钥和私钥对象。

//文件: Skey_RSA.java

import java.io.*;

import java.security.*;

import javax.crypto.*;

import javax.crypto.spec.*;

public class Skey_RSA{

   public static void main(String args[]) throws Exception{

        KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");

        kpg.initialize(1024);

        KeyPair kp=kpg.genKeyPair();

        PublicKey pbkey=kp.getPublic();

        PrivateKey prkey=kp.getPrivate();

        FileOutputStream  f1=new FileOutputStream("Skey_RSA_pub.dat");

        ObjectOutputStream b1=new  ObjectOutputStream(f1);

       b1.writeObject(pbkey);

        FileOutputStream  f2=new FileOutputStream("Skey_RSA_priv.dat");

        ObjectOutputStream b2=new  ObjectOutputStream(f2);

       b2.writeObject(prkey);

   }

}

分析:本实例和 2.2.1 小节一样使用对象流将密钥保存在文件中,所不同的是加密所用

的公钥和解密所用的私钥分开保存。将公钥对外公布,供其他人加密使用,而把私钥秘密保

存,在需要解密时使用。

运行程序

输入 java Skey_RSA 运行程序,当前目录下将生成两个文件: Skey_RSA_pub.dat

Skey_RSA_priv.dat ,前者保存着公钥,后者保存着私钥。将文件 Skey_RSA_pub.dat 对外公

布(如放在 Web 服务器上给大家下载,或者直接拷贝给所有需要的人),而 Skey_RSA_priv.dat

秘密保存。

2.8 使用 RSA 算法进行加密和解密

2.7 节的程序创建了 RSA 的公钥和密钥,本节使用公钥进行加密,然后使用私钥对加密的信息进行解密。

2.8.1 使用 RSA 公钥进行加密

实例说明

本实例以加密一串最简单的字符串“ Hello World! ”为例,演示了如何使用 2.7 节生成的 RSA 公钥文件 Skey_RSA_pub.dat 进行加密。

编程思路:

使用 RSA 公钥进行加密的代码和 2.3.1 小节使用 DESede 进行加密其实没什么大的区别,只是 Cipher 类的 getInstance( ) 方法的参数中应该指定使用 RSA 。但由于 J2SDK1.4 中只实现了 RSA 密钥的创建,没有实现 RSA 算法,因此需要安装其他加密提供者软件才能直接使用 Cipher 类执行加密解密。其实有了 RSA 公钥和私钥后,自己编写程序从底层实现 RSA 算法也并不复杂。本实例给出简单的例子实现了 RSA 加密,使读者只使用 J2SDK1.4 便能直观地了解非对称加密算法。 RSA 算法是使用整数进行加密运算的,在 RSA 公钥中包含了两个信息:公钥对应的整数 e 和用于取模的整数 n 。对于明文数字 m ,计算密文的公式是: me mod n 。因此,编程步骤如下:

1 ) 获取公钥

FileInputStream f=new FileInputStream("Skey_RSA_pub.dat");

ObjectInputStream b=new ObjectInputStream(f);

RSAPublicKey pbk=(RSAPublicKey)b.readObject( );

分析: 从 2.7 节生成的公钥文件 Skey_RSA_pub.dat 中读取公钥,由于 2.7 节使用的是 RSA 算法,因此从文件读取公钥对象后强制转换为 RSAPublicKey 类型,以便后面读取 RSA 算法所需要的参数 .

2 ) 获取公钥的参数 (e, n)

BigInteger e=pbk.getPublicExponent();

BigInteger n=pbk.getModulus();

分析:使用 RSAPublicKey 类的 getPublicExponent( ) getModulus( )方法可以分别获得公始中 e n 的值。由于密钥很长,因此对应的整数值非常大,无法使用一般的整型来存储, Java 中定义了 BigInteger 类来存储这类很大的整数并可进行各种运算。

3 ) 获取明文整数 (m)

String s="Hello World!";

byte ptext[]=s.getBytes("UTF8");

BigInteger m=new BigInteger(ptext);

分析:明文是一个字符串,为了用整数表达这个字符串,先使用字符串的 getBytes( ) 方法 将其转换为 byte 类型数组,它其实是字符串中各个字符的二进制表达方式,这一串二进制数转换为一个整数将非常大,因此仍旧使用 BigInteger 类将这个二进制串转换为整型。本实例中出于简化,将整个字符串转换为一个整数。实际使用中,应该对明文进行分组,因为 RSA 算法要求整型数 m 的值必须小于 n

4 ) 执行计算

BigInteger c=m.modPow(e,n);

分析:计算前面的公式: me mod n BigInteger 类中已经提供了方法 modPow( )来执行这个计算。底数 m 执行这个方法,方法 modPow( )的第一个参数即指数 e,第二个参数即模 n。方法返回的结果即公式 me mod n 的计算结果,即密文。

//文件: Enc_RSA.java

import java.security.*;

import java.security.spec.*;

import javax.crypto.*;

import javax.crypto.spec.*;

import javax.crypto.interfaces.*;

import java.security.interfaces.*;

import java.math.*;

import java.io.*;

public class Enc_RSA{

   public static void main(String args[]) throws Exception{

        String s="Hello World!";

        FileInputStream f=new FileInputStream("Skey_RSA_pub.dat");

        ObjectInputStream b=new ObjectInputStream(f);

        RSAPublicKey  pbk=(RSAPublicKey)b.readObject( );

        BigInteger e=pbk.getPublicExponent();

        BigInteger n=pbk.getModulus();

        System.out.println("e= "+e);

        System.out.println("n= "+n);

        byte ptext[]=s.getBytes("UTF8");

        BigInteger m=new BigInteger(ptext);

        BigInteger c=m.modPow(e,n);

        System.out.println("c= "+c);

        String cs=c.toString( );

        BufferedWriter out=

          new BufferedWriter(new OutputStreamWriter(

            new FileOutputStream("Enc_RSA.dat")));

        out.write(cs,0,cs.length( ));

        out.close( );

   }

}程序最后将密文 c 打印出来,并以字符串形式保存在文件中。

运行程序

输入 java Enc_RSA 运行程序

2.8.2 使用 RSA 私钥进行解密

实例说明

本实例使用 2.7 节生成的私钥文件 Skey_RSA_priv.dat ,对 2.8.1 小节生成的密文文件 Enc_RSA.dat 进行解密。

编程思路:

2.8.1 小节类似,使用 RSA 私钥进行解密的代码也可以在 Cipher 类的 getInstance( ) 方法的参数中指定使用 RSA ,使用解密模式进行解密。但需要安装其他加密提供者软件才能直接使用 Cipher 类执行加密解密。本实例给出简单的例子从底层实现 RSA 解密,以便只使用 J2SDK1.4 便能直观地了解非对称加密算法。 RSA 算法的解密和加密类似,在 RSA 私钥中包含了两个信息:私钥对应的整数 d 和用于取模的整数 n 。其中的 n 和加密时的 n 完全相同。对于密文数字 c ,计算明文的公式是: cd mod

n ,之所以加密时由公式 me mod n 得到的密文 c 通过这个公式计算一下就可以反过来得到原来的明文 m ,有其本身的数学规律决定。从编程角度只需要知道这个结果就行了。编程步骤如下:

1 ) 读取密文

BufferedReader in=

new BufferedReader(new InputStreamReader(

new FileInputStream("Enc_RSA.dat")));

String ctext=in.readLine();

BigInteger c=new BigInteger(ctext);

分析: 从 2.8.1 小节生成的密文文件 Enc_RSA.dat 中读取密文,由于 2.8.1 小节保存的只是一行字符串,因此只要一条 readLine( )语句即可。由于这一行字符串表示的是一个很大的整型数,因此使用 BigInteger 类来表示这个整型数。

2 ) 获取私钥

FileInputStream f=new FileInputStream("Skey_RSA_priv.dat");

ObjectInputStream b=new ObjectInputStream(f);

RSAPrivateKey prk=(RSAPrivateKey)b.readObject( );

分析: 从 2.7 节生成的私钥文件 Skey_RSA_priv.dat 中读取公钥,由于 2.7 节使用的是 RSA 算法,因此从文件读取公钥对象后强制转换为 RSAPrivateKey 类型,以便后面读取 RSA 算法所需要的参数。

3 ) 获取私钥的参数 (d, n)

BigInteger d=prk.getPrivateExponent( );

BigInteger n=prk.getModulus( );

分析:使用 RSAPrivateKey 类的 getPrivateExponent( ) getModulus( )方法可以分别获得公始中 d n 的值。

4 ) 执行计算

BigInteger m=c.modPow(d,n);

分析:使用 BigInteger modPow( )方法计算前面的公式: cd mod n。方法返回的结果即公式 cd mod n 的计算结果,即明文对应的整型数 m

5 ) 计算明文整型数对应的字符串

byte[] mt=m.toByteArray();

for(int i=0;i<mt.length;i++){

      System.out.print((char) mt[i]);

}

分析: RSA 算法解密的结果 m 是一个很大的整数,为了计算出其对应的字符串的值,先使用 BigInteger 类的 toByteArray( )方法得到代表该整型数的字节数组,然后将数组中每个元素转换为字符,组成字符串。

//文件: Dec_RSA.java

import java.security.*;

import java.security.spec.*;

import javax.crypto.*;

import javax.crypto.spec.*;

import javax.crypto.interfaces.*;

import java.security.interfaces.*;

import java.math.*;

import java.io.*;

public class Dec_RSA{

   public static void main(String args[]) throws Exception{

        BufferedReader in=

          new BufferedReader(new InputStreamReader(new FileInputStream("Enc_RSA.dat")));

        String ctext=in.readLine();

        BigInteger c=new BigInteger(ctext);

        FileInputStream f=new FileInputStream("Skey_RSA_priv.dat");

        ObjectInputStream b=new ObjectInputStream(f);

        RSAPrivateKey prk=(RSAPrivateKey)b.readObject( );

        BigInteger d=prk.getPrivateExponent();

        BigInteger n=prk.getModulus();

        System.out.println("d= "+d);

        System.out.println("n= "+n);

        BigInteger m=c.modPow(d,n);

        System.out.println("m= "+m);

        byte[] mt=m.toByteArray();

        System.out.println("PlainText is ");

        for(int i=0;i<mt.length;i++){

             System.out.print((char) mt[i]);

       }

   }

}

运行程序

输入 java Dec_RSA 运行程序

2.9 使用密钥协定创建共享密钥

非对称加密解决了密钥分发的难题,但其计算量比对称密钥大,因此一般并不使用非对称加密加密大量数据。常见的做法是:主要数据通过对称密钥加密,而使用非对称加密来分发对称密钥。将两者的优势结合了起来。例如若 A B 之间想秘密传送大量数据,一方(如 A )先创建公钥和私钥对,公钥对外公布,另一方(如 B )创建对称密钥,然后使用公钥加密对称密钥,传递给 A A 收到后用私钥解密,得到对称密钥,以后 A B 之间就可以使用对称密钥加密通信了。除了这种方式以外,还可以使用密钥协定来交换对称密钥。执行密钥协定的标准算法是 DH 算法( Diffie-Hellman 算法),本节介绍在 Java 中如何使用 DH 算法来交换共享密钥。

2.9.1 创建 DH 公钥和私钥

实例说明

DH 算法是建立在 DH 公钥和私钥的基础上的, A 需要和 B 共享密钥时, A B 各自生成 DH 公钥和私钥,公钥对外公布而私钥各自秘密保存。本实例将介绍 Java 中如何创建并部署 DH 公钥和私钥,以便后面一小节利用它创建共享密钥。

编程思路:

2.7 节中使用了 KeyPairGenerator 类创建 RSA 公钥和私钥,本节也一样,只是其参数中指定“ DH ”,此外在初始化时需要为 DH 指定特定的参数。具体步骤如下:

1 ) 生成 DH 参数

DHParameterSpec DHP=

new DHParameterSpec(skip1024Modulus,skip1024Base);

分析:和 RSA 算法类似, DH 算法涉及到一些指数和取模运算, DH 参数指定 A B双方在创建 DH 密钥时所公用的基数和模, Java DHParameterSpec 类可以定义 DH 参数,其构造器的第一个参数指定模,第二个参数指定基数。模和基数的取值在 Internet协议简单密钥管理( SKIP)标准中已经标准化,在安装 J2SDK1.4 后,计算机 C 盘中 C:/j2sdk-1_4_0-doc/docs/guide/security/jce/JCERefGuide.html 文件也包含了密钥长度为 1024 DH 密钥中模和基数的定义, 可以直接拷贝下来使用, 在 JCERefGuide.html 文件中查找“ 1024 bit Diffie-Hellman modulus”注释语句,将其下的 skip1024ModulusBytes[ ]数组以及 BigInteger 类型的 skip1024Modulus skip1024Base 变量拷贝下来即可。在本小节的“代码与分析”中也给出了完整的代码。此外 DH 密钥长度也可以是 512 2048 位。

2 ) 创建密钥对生成器

KeyPairGenerator kpg= KeyPairGenerator.getInstance("DH");

分析:密钥对生成器即 KeyPairGenerator 类型的对象,和 2.7 节一样,通过其中预定义的一个静态方法 getInstance( )获取 KeyPairGenerator 类型的对象。 getInstance( )方法的参数指定为“ DH”。

3 ) 初始化密钥生成器

kpg.initialize(DHP);

分析:初始化时使用的参数即第 1 步中生成的参数。

4 ) 生成密钥对,获取公钥和私钥

KeyPair kp=kpg.genKeyPair();

PublicKey pbk=kp.getPublic( );

PrivateKey prk=kp.getPrivate( );

分析:和 2.7 节一样,使用使用 KeyPairGenerator 类的 genKeyPair( )方法生成密钥对,进而使用密钥对的 getPublic( ) getPrivate( ) 获取公钥和私钥。

//文件: Key_DH.java

import java.io.*;

import java.math.*;

import java.security.*;

import java.security.spec.*;

import javax.crypto.*;

import javax.crypto.spec.*;

import javax.crypto.interfaces.*;

public class Key_DH{

// The 1024 bit Diffie-Hellman modulus values used by SKIP

    private static final byte skip1024ModulusBytes[] = {

        (byte)0xF4, (byte)0x88, (byte)0xFD, (byte)0x58,

        (byte)0x4E, (byte)0x49, (byte)0xDB, (byte)0xCD,

         (byte)0x20, (byte)0xB4, (byte)0x9D, (byte)0xE4,

        (byte)0x91, (byte)0x07, (byte)0x36, (byte)0x6B,

        (byte)0x33, (byte)0x6C, (byte)0x38, (byte)0x0D,

        (byte)0x45, (byte)0x1D, (byte)0x0F, (byte)0x7C,

        (byte)0x88, (byte)0xB3, (byte)0x1C, (byte)0x7C,

        (byte)0x5B, (byte)0x2D, (byte)0x8E, (byte)0xF6,

        (byte)0xF3, (byte)0xC9, (byte)0x23, (byte)0xC0,

        (byte)0x43, (byte)0xF0, (byte)0xA5, (byte)0x5B,

        (byte)0x18, (byte)0x8D, (byte)0x8E, (byte)0xBB,

        (byte)0x55, (byte)0x8C, (byte)0xB8, (byte)0x5D,

        (byte)0x38, (byte)0xD3, (byte)0x34, (byte)0xFD,

        (byte)0x7C, (byte)0x17, (byte)0x57, (byte)0x43,

        (byte)0xA3, (byte)0x1D, (byte)0x18, (byte)0x6C,

        (byte)0xDE, (byte)0x33, (byte)0x21, (byte)0x2C,

        (byte)0xB5, (byte)0x2A, (byte)0xFF, (byte)0x3C,

        (byte)0xE1, (byte)0xB1, (byte)0x29, (byte)0x40,

        (byte)0x18, (byte)0x11, (byte)0x8D, (byte)0x7C,

        (byte)0x84, (byte)0xA7, (byte)0x0A, (byte)0x72,

        (byte)0xD6, (byte)0x86, (byte)0xC4, (byte)0x03,

        (byte)0x19, (byte)0xC8, (byte)0x07, (byte)0x29,

        (byte)0x7A, (byte)0xCA, (byte)0x95, (byte)0x0C,

        (byte)0xD9, (byte)0x96, (byte)0x9F, (byte)0xAB,

        (byte)0xD0, (byte)0x0A, (byte)0x50, (byte)0x9B,

        (byte)0x02, (byte)0x46, (byte)0xD3, (byte)0x08,

        (byte)0x3D, (byte)0x66, (byte)0xA4, (byte)0x5D,

        (byte)0x41, (byte)0x9F, (byte)0x9C, (byte)0x7C,

        (byte)0xBD, (byte)0x89, (byte)0x4B, (byte)0x22,

        (byte)0x19, (byte)0x26, (byte)0xBA, (byte)0xAB,

        (byte)0xA2, (byte)0x5E, (byte)0xC3, (byte)0x55,

        (byte)0xE9, (byte)0x2F, (byte)0x78, (byte)0xC7

    };

    // The SKIP 1024 bit modulus

    private static final BigInteger skip1024Modulus

              = new BigInteger(1, skip1024ModulusBytes);

    // The base used with the SKIP 1024 bit modulus

    private static final BigInteger skip1024Base = BigInteger.valueOf(2);

public static void main(String args[ ]) throws Exception{

    DHParameterSpec DHP=new DHParameterSpec(skip1024Modulus,skip1024Base);

     KeyPairGenerator kpg= KeyPairGenerator.getInstance("DH");

     kpg.initialize(DHP);

     KeyPair kp=kpg.genKeyPair();

     PublicKey pbk=kp.getPublic();

     PrivateKey prk=kp.getPrivate();

     FileOutputStream  f1=new FileOutputStream(args[0]);

     ObjectOutputStream b1=new  ObjectOutputStream(f1);

     b1.writeObject(pbk);

     FileOutputStream  f2=new FileOutputStream(args[1]);

     ObjectOutputStream b2=new  ObjectOutputStream(f2);

     b2.writeObject(prk);

   }     

程序最后将公钥和私钥以对象流的形式保存在文件中,文件名通过命令行参数指定,第一个命令行参数对应的文件保存公钥,第二个命令行参数对应的文件保存私钥。

运行程序

建立两个目录 A B ,模拟需要秘密通信的 A B 双方,由于 DH 算法需要 A B 各自生成 DH 公钥和私钥,因此在这两个目录下都拷贝编译后文件 Key_DH 。首先由 A 创建自己的公钥和私钥,即在 A 目录下输入“ java Key_DH Apub.dat Apri.dat ”运行程序,这时在目录 A 下将产生文件 Apub.dat Apri.dat ,前者保存着 A 的公钥,后者保存着 A 的私钥。然后由 B 创建自己的公钥和私钥,即在 B 目录下输入“ java Key_DH Bpub.dat Bpri.dat ”运行程序,这时在目录 B 下将产生文件 Bpub.dat Bpri.dat ,前者保存着 B 的公钥,后者保存着 B 的私钥。最后发布公钥, A Apub.dat 拷贝到 B 目录, B Bpub.dat 拷贝到 A 的目录。这样, A B 双方的 DH 公钥和私钥已经创建并部署完毕。

2.9.2 创建共享密钥

实例说明

DH 算法中, A 可以用自己的密钥和 B 的公钥按照一定方法生成一个密钥, B 也可以用自己的密钥和 A 的公钥按照一定方法生成一个密钥,由于一些数学规律,这两个密钥完全相同。这样, A B 间就有了一个共同的密钥可以用于各种加密。本实例介绍 Java 中在上一小节的基础上如何利用 DH 公钥和私钥各自创建共享密钥。

编程思路:

Java KeyAgreement 类实现了密钥协定,它使用 init( ) 方法传入自己的私钥,使用 doPhase

( )方法传入对方的公钥,进而可以使用 generateSecret( ) 方法生成共享的信息具体步骤如下:

1 ) 读取自己的 DH 私钥和对方的 DH 公钥

FileInputStream f1=new FileInputStream(args[0]);

ObjectInputStream b1=new ObjectInputStream(f1);

PublicKey pbk=(PublicKey)b1.readObject( );

FileInputStream f2=new FileInputStream(args[1]);

ObjectInputStream b2=new ObjectInputStream(f2);

PrivateKey prk=(PrivateKey)b2.readObject( );

分析:和 2.3.1 小节类似,从文件中获取密钥。只是分为公钥和私钥两个文件,通过命令行参数传入公钥和私钥文件名,第一个命令行参数为对方的公钥文件名,第二个命令行参数为自己的私钥文件名。

2 ) 创建密钥协定对象

KeyAgreement ka=KeyAgreement.getInstance("DH");

分析:密钥协定对象即 KeyAgreement 类型的对象,和 KeyPairGenerator 类类似, KeyAgreement 类是一个工厂类,通过其中预定义的一个静态方法 getInstance( )获取 KeyAgreement 类型的对象。 getInstance( )方法的参数指定为“ DH”。

3 ) 初始化密钥协定对象

ka.init(prk);

分析:执行密钥协定对象的 init( )方法,传入第 1 步获得的自己的私钥,它在第 1 步中通过第 2 个命令行参数提供。

4 ) 执行密钥协定

ka.doPhase(pbk,true);

分析:执行密钥协定对象的 doPhase( )方法,其第一个参数中传入对方的公钥。在本实例中,只有 A B 两方需要共享密钥,因此对方只有一个,因此第二个参数设置为 true。如果有 A B C 三方需要共享密钥,则对方有两个, doPhase()方法要写两次,每次在第 1 个参数中传入一个公钥,第 2 个参数最初设置为 false,最后一次设置为 true。例如 C 方应该执行 ka.doPhase(pbk_of_A,false); ka.doPhase(pbk_of_B,true); 。一次类推,可以用密钥协定实现多方共享一个密钥。

5 ) 生成共享信息

byte[ ] sb=ka.generateSecret();

分析:执行密钥协定对象的 generateSecret( ) 方法,返回字节类型的数组。 A B双方得到的该数组的内容完全相同,用它创建密钥也各方完全相同。如可使用 SecretKeySpec k=new SecretKeySpec(sb,"DESede"); 创建密钥。

//文件: KeyAgree.java

import java.io.*;

import java.math.*;

import java.security.*;

import java.security.spec.*;

import javax.crypto.*;

import javax.crypto.spec.*;

import javax.crypto.interfaces.*;

public class KeyAgree{

   public static void main(String args[ ]) throws Exception{

      FileInputStream f1=new FileInputStream(args[0]);

      ObjectInputStream b1=new ObjectInputStream(f1);

      PublicKey  pbk=(PublicKey)b1.readObject( );

      FileInputStream f2=new FileInputStream(args[1]);

      ObjectInputStream b2=new ObjectInputStream(f2);

      PrivateKey  prk=(PrivateKey)b2.readObject( );

     KeyAgreement ka=KeyAgreement.getInstance("DH");

     ka.init(prk);

     ka.doPhase(pbk,true);

     byte[ ] sb=ka.generateSecret();

     for(int i=0;i<sb.length;i++){

        System.out.print(sb[i]+",");

     }

    SecretKeySpec k=new  SecretKeySpec(sb,"DESede")

  }

程序最后将共享信息打印了出来,以便直观地对比 A B 得到的信息是否相同。

运行程序

将程序 KeyAgree 编译后分别拷贝在 A B 两个目录,首先在 A 目录输入“ java KeyAgree

Bpub.dat Apri.dat ”运行程序,它使用文件 Bpub.dat 中对方的公钥和文件 Apri.dat 中自己的

私钥创建了一段共享的字节数组。然后在 B 目录输入“ java KeyAgree Apub.dat Bpri.dat ”运行程序,它使用文件 Apub.dat 中对方的公钥和文件 Bpri.dat 中自己的私钥创建了一段共享的字节数组。

可以看到 A B 运行后得到的字节数组内容完全相同,因此使用它创建的密钥也将相同。由于 DH 算法内在的数学规律, A B 在运行时只使用了对方可以公开的信息(公钥),而各自独立地得到了相同的密钥,完成了密钥分发工作。该部分介绍了对称加密和非对称加密的基本用法,并演示了使用密钥协定进行密钥分发。读者在掌握了其原理后,可以举一反三,互相组合,满足不同应用。出于实例的简洁,该部分的例子都使用文件来交换信息,读者也可以使用其它方式,如在密钥协定中可以使用 Socket 等多种方式在程序间传递信息。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值