2001 年 7 月 04 日

通常,使用的加密算法 比较简便高效,密钥简短,加解密速度快,破译极其困难。本文介绍了 MD5/SHA1,DSA,DESede/DES,Diffie-Hellman的使用。

第1章基础知识

1.1. 单钥密码体制

单钥密码体制是一种传统的加密算法,是指信息的发送方和接收方共同使用同一把密钥进行加解密。

通常,使用的加密算法比较简便高效,密钥简短,加解密速度快,破译极其困难。但是加密的安全性依靠密钥保管的安全性,在公开的计算机网络上安全地传送和保管密钥是一个严峻的问题,并且如果在多用户的情况下密钥的保管安全性也是一个问题。

单钥密码体制的代表是美国的DES

1.2. 消息摘要

一个消息摘要就是一个数据块的数字指纹。即对一个任意长度的一个数据块进行计算,产生一个唯一指印(对于SHA1是产生一个20字节的二进制数组)。

消息摘要有两个基本属性:

  • 两个不同的报文难以生成相同的摘要
  • 难以对指定的摘要生成一个报文,而由该报文反推算出该指定的摘要

代表:美国国家标准技术研究所的SHA1和麻省理工学院Ronald Rivest提出的MD5

1.3. Diffie-Hellman密钥一致协议

密钥一致协议是由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。

先决条件,允许两名用户在公开媒体上交换信息以生成"一致"的,可以共享的密钥

代表:指数密钥一致协议(Exponential Key Agreement Protocol)

1.4. 非对称算法与公钥体系

1976年,Dittie和Hellman为解决密钥管理问题,在他们的奠基性的工作"密码学的新方向"一文中,提出一种密钥交换协议,允许在不安全的媒体上通过通讯双方交换信息,安全地传送秘密密钥。在此新思想的基础上,很快出现了非对称密钥密码体制,即公钥密码体制。在公钥体制中,加密密钥不同于解密密钥,加密密钥公之于众,谁都可以使用;解密密钥只有解密人自己知道。它们分别称为公开密钥(Public key)和秘密密钥(Private key)。

迄今为止的所有公钥密码体系中,RSA系统是最著名、最多使用的一种。RSA公开密钥密码系统是由R.Rivest、A.Shamir和L.Adleman俊教授于1977年提出的。RSA的取名就是来自于这三位发明者的姓的第一个字母

1.5. 数字签名

所谓数字签名就是信息发送者用其私钥对从所传报文中提取出的特征数据(或称数字指纹)进行RSA算法操作,以保证发信人无法抵赖曾发过该信息(即不可抵赖性),同时也确保信息报文在经签名后末被篡改(即完整性)。当信息接收者收到报文后,就可以用发送者的公钥对数字签名进行验证。 

在数字签名中有重要作用的数字指纹是通过一类特殊的散列函数(HASH函数)生成的,对这些HASH函数的特殊要求是:

  1. 接受的输入报文数据没有长度限制;
  2. 对任何输入报文数据生成固定长度的摘要(数字指纹)输出
  3. 从报文能方便地算出摘要;
  4. 难以对指定的摘要生成一个报文,而由该报文反推算出该指定的摘要;
  5. 两个不同的报文难以生成相同的摘要

代表:DSA


回页首

第2章在JAVA中的实现

2.1. 相关

Diffie-Hellman密钥一致协议和DES程序需要JCE工具库的支持,可以到 http://java.sun.com/security/index.html 下载JCE,并进行安装。简易安装把 jce1.2.1/lib 下的所有内容复制到 %java_home%/lib/ext下,如果没有ext目录自行建立,再把jce1_2_1.jar和sunjce_provider.jar添加到CLASSPATH内,更详细说明请看相应用户手册

2.2. 消息摘要MD5和SHA的使用

使用方法:

首先用生成一个MessageDigest类,确定计算方法

java.security.MessageDigest alga=java.security.MessageDigest.getInstance("SHA-1");

添加要进行计算摘要的信息

alga.update(myinfo.getBytes());

计算出摘要

byte[] digesta=alga.digest();

发送给其他人你的信息和摘要

其他人用相同的方法初始化,添加信息,最后进行比较摘要是否相同

algb.isEqual(digesta,algb.digest())

相关AIP

java.security.MessageDigest 类

static getInstance(String algorithm)

返回一个MessageDigest对象,它实现指定的算法

参数:算法名,如 SHA-1 或MD5

void update (byte input)

void update (byte[] input)

void update(byte[] input, int offset, int len)

添加要进行计算摘要的信息

byte[] digest()

完成计算,返回计算得到的摘要(对于MD5是16位,SHA是20位)

void reset()

复位

static boolean isEqual(byte[] digesta, byte[] digestb)

比效两个摘要是否相同

代码:

import java.security.*;

public class myDigest {

public static void main(String[] args)  {

myDigest my=new myDigest();

my.testDigest();

}

public void testDigest()

{

try {

String myinfo="我的测试信息";

//java.security.MessageDigest alg=java.security.MessageDigest.getInstance("MD5");

java.security.MessageDigest alga=java.security.MessageDigest.getInstance("SHA-1");

alga.update(myinfo.getBytes());

byte[] digesta=alga.digest();

System.out.println("本信息摘要是:"+byte2hex(digesta));

//通过某中方式传给其他人你的信息(myinfo)和摘要(digesta) 对方可以判断是否更改或传输正常

java.security.MessageDigest algb=java.security.MessageDigest.getInstance("SHA-1");

algb.update(myinfo.getBytes());

if (algb.isEqual(digesta,algb.digest())) {

System.out.println("信息检查正常");

}

else

{

System.out.println("摘要不相同");

}

}

catch (java.security.NoSuchAlgorithmException ex) {

System.out.println("非法摘要算法");

}

}

public String byte2hex(byte[] b) //二行制转字符串

{

String hs="";

String stmp="";

for (int n=0;n<b.length;n++)

{

stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));

if (stmp.length()==1) hs=hs+"0"+stmp;

else hs=hs+stmp;

if (n<b.length-1)  hs=hs+":";

}

return hs.toUpperCase();

}

}

2.3. 数字签名DSA

  1. 对于一个用户来讲首先要生成他的密钥对,并且分别保存

    生成一个KeyPairGenerator实例

    java.security.KeyPairGenerator  keygen=java.security.KeyPairGenerator.getInstance("DSA");
    
    如果设定随机产生器就用如相代码初始化
    
    SecureRandom secrand=new SecureRandom();
    
    secrand.setSeed("tttt".getBytes()); //初始化随机产生器
    
    keygen.initialize(512,secrand);     //初始化密钥生成器
    
    否则
    
    keygen.initialize(512);
    
    生成密钥公钥pubkey和私钥prikey
    
    KeyPair keys=keygen.generateKeyPair(); //生成密钥组
    
    PublicKey pubkey=keys.getPublic();
    
    PrivateKey prikey=keys.getPrivate();
    
    分别保存在myprikey.dat和mypubkey.dat中,以便下次不在生成
    
    (生成密钥对的时间比较长
    
    java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myprikey.dat"));
    
    out.writeObject(prikey);
    
    out.close();
    
    out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("mypubkey.dat"));
    
    out.writeObject(pubkey);
    
    out.close();
    
  2. 用他私人密钥(prikey)对他所确认的信息(info)进行数字签名产生一个签名数组

    从文件中读入私人密钥(prikey)

    java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("myprikey.dat"));
    
    PrivateKey myprikey=(PrivateKey)in.readObject();
    
    in.close();
    
    初始一个Signature对象,并用私钥对信息签名
    
    java.security.Signature signet=java.security.Signature.getInstance("DSA");
    
    signet.initSign(myprikey);
    
    signet.update(myinfo.getBytes());
    
    byte[] signed=signet.sign();
    
    把信息和签名保存在一个文件中(myinfo.dat)
    
    java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myinfo.dat"));
    
    out.writeObject(myinfo);
    
    out.writeObject(signed);
    
    out.close();
    
    把他的公钥的信息及签名发给其它用户
    
  3. 其他用户用他的公共密钥(pubkey)和签名(signed)和信息(info)进行验证是否由他签名的信息

    读入公钥
    java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("mypubkey.dat"));
    PublicKey pubkey=(PublicKey)in.readObject();
    in.close();

    读入签名和信息
    in=new java.io.ObjectInputStream(new java.io.FileInputStream("myinfo.dat"));
    String info=(String)in.readObject();
    byte[] signed=(byte[])in.readObject();
    in.close();

    初始一个Signature对象,并用公钥和签名进行验证
    java.security.Signature signetcheck=java.security.Signature.getInstance("DSA");
    signetcheck.initVerify(pubkey);
    signetcheck.update(info.getBytes());
    if (signetcheck.verify(signed)) { System.out.println("签名正常");}

    对于密钥的保存本文是用对象流的方式保存和传送的,也可可以用编码的方式保存.注意要
    import java.security.spec.*
    import java.security.*

    具休说明如下

    • public key是用X.509编码的,例码如下:

      byte[] bobEncodedPubKey=mypublic.getEncoded(); //生成编码
      
      //传送二进制编码
      
      //以下代码转换编码为相应key对象
      
      X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(bobEncodedPubKey);
      
      KeyFactory keyFactory = KeyFactory.getInstance("DSA");
      
      PublicKey bobPubKey = keyFactory.generatePublic(bobPubKeySpec);
      
    • 对于Private key是用PKCS#8编码,例码如下:
      byte[] bPKCS=myprikey.getEncoded();
      
      //传送二进制编码
      
      //以下代码转换编码为相应key对象
      
      PKCS8EncodedKeySpec priPKCS8=new PKCS8EncodedKeySpec(bPKCS);
      
      KeyFactory keyf=KeyFactory.getInstance("DSA");
      
      PrivateKey otherprikey=keyf.generatePrivate(priPKCS8);
      
  4. 常用API

    java.security.KeyPairGenerator 密钥生成器类
    public static KeyPairGenerator getInstance(String algorithm) throws NoSuchAlgorithmException
    以指定的算法返回一个KeyPairGenerator 对象
    参数: algorithm 算法名.如:"DSA","RSA"

    public void initialize(int keysize)

    以指定的长度初始化KeyPairGenerator对象,如果没有初始化系统以1024长度默认设置

    参数:keysize 算法位长.其范围必须在 512 到 1024 之间,且必须为 64 的倍数

    public void initialize(int keysize, SecureRandom random)
    以指定的长度初始化和随机发生器初始化KeyPairGenerator对象
    参数:keysize 算法位长.其范围必须在 512 到 1024 之间,且必须为 64 的倍数
    random 一个随机位的来源(对于initialize(int keysize)使用了默认随机器

    public abstract KeyPair generateKeyPair()
    产生新密钥对

    java.security.KeyPair 密钥对类
    public PrivateKey getPrivate()
    返回私钥

    public PublicKey getPublic()
    返回公钥

    java.security.Signature 签名类
    public static Signature getInstance(String algorithm) throws NoSuchAlgorithmException
    返回一个指定算法的Signature对象
    参数 algorithm 如:"DSA"

    public final void initSign(PrivateKey privateKey)
    throws InvalidKeyException
    用指定的私钥初始化
    参数:privateKey 所进行签名时用的私钥

    public final void update(byte data)
    throws SignatureException
    public final void update(byte[] data)
    throws SignatureException
    public final void update(byte[] data, int off, int len)
    throws SignatureException
    添加要签名的信息

    public final byte[] sign()
    throws SignatureException
    返回签名的数组,前提是initSign和update

    public final void initVerify(PublicKey publicKey)
    throws InvalidKeyException
    用指定的公钥初始化
    参数:publicKey 验证时用的公钥

    public final boolean verify(byte[] signature)
    throws SignatureException
    验证签名是否有效,前提是已经initVerify初始化
    参数: signature 签名数组

    */
    
    import java.security.*;
    
    import java.security.spec.*;
    
    public class testdsa {
    
    public static void main(String[] args) throws java.security.NoSuchAlgorithmException,java.lang.Exception {
    
    testdsa my=new testdsa();
    
    my.run();
    
    }
    
    public void run()
    
    {
    
    //数字签名生成密钥
    
    //第一步生成密钥对,如果已经生成过,本过程就可以跳过,对用户来讲myprikey.dat要保存在本地
    
    //而mypubkey.dat给发布给其它用户
    
    if ((new java.io.File("myprikey.dat")).exists()==false) {
    
    if (generatekey()==false) {
    
    System.out.println("生成密钥对败");
    
    return;
    
    };
    
    }
    
    //第二步,此用户
    
    //从文件中读入私钥,对一个字符串进行签名后保存在一个文件(myinfo.dat)中
    
    //并且再把myinfo.dat发送出去
    
    //为了方便数字签名也放进了myifno.dat文件中,当然也可分别发送
    
    try {
    
    java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("myprikey.dat"));
    
    PrivateKey myprikey=(PrivateKey)in.readObject();
    
    in.close();
    
    // java.security.spec.X509EncodedKeySpec pubX509=new java.security.spec.X509EncodedKeySpec(bX509);
    
    //java.security.spec.X509EncodedKeySpec pubkeyEncode=java.security.spec.X509EncodedKeySpec
    
    String myinfo="这是我的信息";    //要签名的信息
    
    //用私钥对信息生成数字签名
    
    java.security.Signature signet=java.security.Signature.getInstance("DSA");
    
    signet.initSign(myprikey);
    
    signet.update(myinfo.getBytes());
    
    byte[] signed=signet.sign();  //对信息的数字签名
    
    System.out.println("signed(签名内容)="+byte2hex(signed));
    
    //把信息和数字签名保存在一个文件中
    
    java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myinfo.dat"));
    
    out.writeObject(myinfo);
    
    out.writeObject(signed);
    
    out.close();
    
    System.out.println("签名并生成文件成功");
    
    }
    
    catch (java.lang.Exception e) {
    
    e.printStackTrace();
    
    System.out.println("签名并生成文件失败");
    
    };
    
    //第三步
    
    //其他人通过公共方式得到此户的公钥和文件
    
    //其他人用此户的公钥,对文件进行检查,如果成功说明是此用户发布的信息.
    
    //
    
    try {
    
    java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("mypubkey.dat"));
    
    PublicKey pubkey=(PublicKey)in.readObject();
    
    in.close();
    
    System.out.println(pubkey.getFormat());
    
    in=new java.io.ObjectInputStream(new java.io.FileInputStream("myinfo.dat"));
    
    String info=(String)in.readObject();
    
    byte[] signed=(byte[])in.readObject();
    
    in.close();
    
    java.security.Signature signetcheck=java.security.Signature.getInstance("DSA");
    
    signetcheck.initVerify(pubkey);
    
    signetcheck.update(info.getBytes());
    
    if (signetcheck.verify(signed)) {
    
    System.out.println("info="+info);
    
    System.out.println("签名正常");
    
    }
    
    else  System.out.println("非签名正常");
    
    }
    
    catch (java.lang.Exception e) {e.printStackTrace();};
    
    }
    
    //生成一对文件myprikey.dat和mypubkey.dat---私钥和公钥,
    
    //公钥要用户发送(文件,网络等方法)给其它用户,私钥保存在本地
    
    public boolean generatekey()
    
    {
    
    try {
    
    java.security.KeyPairGenerator  keygen=java.security.KeyPairGenerator.getInstance("DSA");
    
    // SecureRandom secrand=new SecureRandom();
    
    // secrand.setSeed("tttt".getBytes()); //初始化随机产生器
    
    // keygen.initialize(576,secrand);     //初始化密钥生成器
    
    keygen.initialize(512);
    
    KeyPair keys=keygen.genKeyPair();
    
    //  KeyPair keys=keygen.generateKeyPair(); //生成密钥组
    
    PublicKey pubkey=keys.getPublic();
    
    PrivateKey prikey=keys.getPrivate();
    
    java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myprikey.dat"));
    
    out.writeObject(prikey);
    
    out.close();
    
    System.out.println("写入对象 prikeys ok");
    
    out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("mypubkey.dat"));
    
    out.writeObject(pubkey);
    
    out.close();
    
    System.out.println("写入对象 pubkeys ok");
    
    System.out.println("生成密钥对成功");
    
    return true;
    
    }
    
    catch (java.lang.Exception e) {
    
    e.printStackTrace();
    
    System.out.println("生成密钥对失败");
    
    return false;
    
    };
    
    }
    
    public String byte2hex(byte[] b)
    
    {
    
    String hs="";
    
    String stmp="";
    
    for (int n=0;n<b.length;n++)
    
    {
    
    stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));
    
    if (stmp.length()==1) hs=hs+"0"+stmp;
    
    else hs=hs+stmp;
    
    if (n<b.length-1)  hs=hs+":";
    
    }
    
    return hs.toUpperCase();
    
    }
    
    }
    

2.4. DESede/DES对称算法

首先生成密钥,并保存(这里并没的保存的代码,可参考DSA中的方法)

KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);

SecretKey deskey = keygen.generateKey();

用密钥加密明文(myinfo),生成密文(cipherByte)

Cipher c1 = Cipher.getInstance(Algorithm);

c1.init(Cipher.ENCRYPT_MODE,deskey);

byte[] cipherByte=c1.doFinal(myinfo.getBytes());

传送密文和密钥,本文没有相应代码可参考DSA

.............

用密钥解密密文

c1 = Cipher.getInstance(Algorithm);

c1.init(Cipher.DECRYPT_MODE,deskey);

byte[] clearByte=c1.doFinal(cipherByte);

相对来说对称密钥的使用是很简单的,对于JCE来讲支技DES,DESede,Blowfish三种加密术

对于密钥的保存各传送可使用对象流或者用二进制编码,相关参考代码如下

SecretKey deskey = keygen.generateKey();

byte[] desEncode=deskey.getEncoded();

javax.crypto.spec.SecretKeySpec destmp=new javax.crypto.spec.SecretKeySpec(desEncode,Algorithm);

SecretKey mydeskey=destmp;

相关API

KeyGenerator 在DSA中已经说明,在添加JCE后在instance进可以如下参数

DES,DESede,Blowfish,HmacMD5,HmacSHA1

javax.crypto.Cipher 加/解密器

public static final Cipher getInstance(java.lang.String transformation)

throws java.security.NoSuchAlgorithmException,

NoSuchPaddingException

返回一个指定方法的Cipher对象

参数:transformation 方法名(可用 DES,DESede,Blowfish)

public final void init(int opmode, java.security.Key key)
throws java.security.InvalidKeyException

用指定的密钥和模式初始化Cipher对象

参数:opmode 方式(ENCRYPT_MODE, DECRYPT_MODE, WRAP_MODE,UNWRAP_MODE)

key 密钥

public final byte[] doFinal(byte[] input)

throws java.lang.IllegalStateException,

IllegalBlockSizeException,

BadPaddingException

对input内的串,进行编码处理,返回处理后二进制串,是返回解密文还是加解文由init时的opmode决定

注意:本方法的执行前如果有update,是对updat和本次input全部处理,否则是本inout的内容

/*

安全程序 DESede/DES测试

*/

import java.security.*;

import javax.crypto.*;

public class testdes {

public static void main(String[] args){

testdes my=new testdes();

my.run();

}

public  void run() {

//添加新安全算法,如果用JCE就要把它添加进去

Security.addProvider(new com.sun.crypto.provider.SunJCE());

String Algorithm="DES"; //定义 加密算法,可用 DES,DESede,Blowfish

String myinfo="要加密的信息";

try {

//生成密钥

KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);

SecretKey deskey = keygen.generateKey();

//加密

System.out.println("加密前的二进串:"+byte2hex(myinfo.getBytes()));

System.out.println("加密前的信息:"+myinfo);

Cipher c1 = Cipher.getInstance(Algorithm);

c1.init(Cipher.ENCRYPT_MODE,deskey);

byte[] cipherByte=c1.doFinal(myinfo.getBytes());

System.out.println("加密后的二进串:"+byte2hex(cipherByte));

//解密

c1 = Cipher.getInstance(Algorithm);

c1.init(Cipher.DECRYPT_MODE,deskey);

byte[] clearByte=c1.doFinal(cipherByte);

System.out.println("解密后的二进串:"+byte2hex(clearByte));

System.out.println("解密后的信息:"+(new String(clearByte)));

}

catch (java.security.NoSuchAlgorithmException e1) {e1.printStackTrace();}

catch (javax.crypto.NoSuchPaddingException e2) {e2.printStackTrace();}

catch (java.lang.Exception e3) {e3.printStackTrace();}

}

public String byte2hex(byte[] b) //二行制转字符串

{

String hs="";

String stmp="";

for (int n=0;n<b.length;n++)

{

stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));

if (stmp.length()==1) hs=hs+"0"+stmp;

else hs=hs+stmp;

if (n<b.length-1)  hs=hs+":";

}

return hs.toUpperCase();

}

}

2.5. Diffie-Hellman密钥一致协议

公开密钥密码体制的奠基人Diffie和Hellman所提出的 "指数密钥一致协议"(Exponential Key Agreement Protocol),该协议不要求别的安全性先决条件,允许两名用户在公开媒体上交换信息以生成"一致"的,可以共享的密钥。在JCE的中实现用户alice生成DH类型的密钥对,如果长度用1024生成的时间请,推荐第一次生成后保存DHParameterSpec,以便下次使用直接初始化.使其速度加快

System.out.println("ALICE: 产生 DH 对 ...");

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

aliceKpairGen.initialize(512);

KeyPair aliceKpair = aliceKpairGen.generateKeyPair();

alice生成公钥发送组bob

byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();

bob从alice发送来的公钥中读出DH密钥对的初始参数生成bob的DH密钥对

注意这一步一定要做,要保证每个用户用相同的初始参数生成的

DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams();

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

bobKpairGen.initialize(dhParamSpec);

KeyPair bobKpair = bobKpairGen.generateKeyPair();

bob根据alice的公钥生成本地的DES密钥

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

bobKeyAgree.init(bobKpair.getPrivate());

bobKeyAgree.doPhase(alicePubKey, true);

SecretKey bobDesKey = bobKeyAgree.generateSecret("DES");

bob已经生成了他的DES密钥,他现把他的公钥发给alice,

byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();

alice根据bob的公钥生成本地的DES密钥

,,,,,,解码

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

aliceKeyAgree.init(aliceKpair.getPrivate());

aliceKeyAgree.doPhase(bobPubKey, true);

SecretKey aliceDesKey = aliceKeyAgree.generateSecret("DES");

bob和alice能过这个过程就生成了相同的DES密钥,在这种基础就可进行安全能信

常用API

java.security.KeyPairGenerator 密钥生成器类
public static KeyPairGenerator getInstance(String algorithm)
throws NoSuchAlgorithmException
以指定的算法返回一个KeyPairGenerator 对象
参数: algorithm 算法名.如:原来是DSA,现在添加了 DiffieHellman(DH)

public void initialize(int keysize)
以指定的长度初始化KeyPairGenerator对象,如果没有初始化系统以1024长度默认设置
参数:keysize 算法位长.其范围必须在 512 到 1024 之间,且必须为 64 的倍数
注意:如果用1024生长的时间很长,最好生成一次后就保存,下次就不用生成了

public void initialize(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException
以指定参数初始化

javax.crypto.interfaces.DHPublicKey
public DHParameterSpec getParams()
返回
java.security.KeyFactory

public static KeyFactory getInstance(String algorithm)
throws NoSuchAlgorithmException
以指定的算法返回一个KeyFactory
参数: algorithm 算法名:DSH,DH

public final PublicKey generatePublic(KeySpec keySpec)
throws InvalidKeySpecException
根据指定的key说明,返回一个PublicKey对象

java.security.spec.X509EncodedKeySpec
public X509EncodedKeySpec(byte[] encodedKey)
根据指定的二进制编码的字串生成一个key的说明
参数:encodedKey 二进制编码的字串(一般能过PublicKey.getEncoded()生成)
javax.crypto.KeyAgreement 密码一至类

public static final KeyAgreement getInstance(java.lang.String algorithm)
throws java.security.NoSuchAlgorithmException
返回一个指定算法的KeyAgreement对象
参数:algorithm 算法名,现在只能是DiffieHellman(DH)

public final void init(java.security.Key key)
throws java.security.InvalidKeyException
用指定的私钥初始化
参数:key 一个私钥

public final java.security.Key doPhase(java.security.Key key,
boolean lastPhase)
throws java.security.InvalidKeyException,
java.lang.IllegalStateException
用指定的公钥进行定位,lastPhase确定这是否是最后一个公钥,对于两个用户的
情况下就可以多次定次,最后确定
参数:key 公钥
lastPhase 是否最后公钥

public final SecretKey generateSecret(java.lang.String algorithm)
throws java.lang.IllegalStateException,
java.security.NoSuchAlgorithmException,
java.security.InvalidKeyException
根据指定的算法生成密钥
参数:algorithm 加密算法(可用 DES,DESede,Blowfish)

*/

import java.io.*;

import java.math.BigInteger;

import java.security.*;

import java.security.spec.*;

import java.security.interfaces.*;

import javax.crypto.*;

import javax.crypto.spec.*;

import javax.crypto.interfaces.*;

import com.sun.crypto.provider.SunJCE;

public class testDHKey {

public static void main(String argv[]) {

try {

testDHKey my= new testDHKey();

my.run();

} catch (Exception e) {

System.err.println(e);

}

}

private void run() throws Exception {

Security.addProvider(new com.sun.crypto.provider.SunJCE());

System.out.println("ALICE: 产生 DH 对 ...");

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

aliceKpairGen.initialize(512);

KeyPair aliceKpair = aliceKpairGen.generateKeyPair(); //生成时间长

// 张三(Alice)生成公共密钥 alicePubKeyEnc 并发送给李四(Bob) ,

//比如用文件方式,socket.....

byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();

//bob接收到alice的编码后的公钥,将其解码

KeyFactory bobKeyFac = KeyFactory.getInstance("DH");

X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec  (alicePubKeyEnc);

PublicKey alicePubKey = bobKeyFac.generatePublic(x509KeySpec);

System.out.println("alice公钥bob解码成功");

// bob必须用相同的参数初始化的他的DH KEY对,所以要从Alice发给他的公开密钥,

//中读出参数,再用这个参数初始化他的 DH key对

//从alicePubKye中取alice初始化时用的参数

DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams();

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

bobKpairGen.initialize(dhParamSpec);

KeyPair bobKpair = bobKpairGen.generateKeyPair();

System.out.println("BOB: 生成 DH key 对成功");

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

bobKeyAgree.init(bobKpair.getPrivate());

System.out.println("BOB: 初始化本地key成功");

//李四(bob) 生成本地的密钥 bobDesKey

bobKeyAgree.doPhase(alicePubKey, true);

SecretKey bobDesKey = bobKeyAgree.generateSecret("DES");

System.out.println("BOB: 用alice的公钥定位本地key,生成本地DES密钥成功");

// Bob生成公共密钥 bobPubKeyEnc 并发送给Alice,

//比如用文件方式,socket.....,使其生成本地密钥

byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();

System.out.println("BOB向ALICE发送公钥");

// alice接收到 bobPubKeyEnc后生成bobPubKey

// 再进行定位,使aliceKeyAgree定位在bobPubKey

KeyFactory aliceKeyFac = KeyFactory.getInstance("DH");

x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc);

PublicKey bobPubKey = aliceKeyFac.generatePublic(x509KeySpec);

System.out.println("ALICE接收BOB公钥并解码成功");

;

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

aliceKeyAgree.init(aliceKpair.getPrivate());

System.out.println("ALICE: 初始化本地key成功");

aliceKeyAgree.doPhase(bobPubKey, true);

// 张三(alice) 生成本地的密钥 aliceDesKey

SecretKey aliceDesKey = aliceKeyAgree.generateSecret("DES");

System.out.println("ALICE: 用bob的公钥定位本地key,并生成本地DES密钥");

if (aliceDesKey.equals(bobDesKey)) System.out.println("张三和李四的密钥相同");

//现在张三和李四的本地的deskey是相同的所以,完全可以进行发送加密,接收后解密,达到

//安全通道的的目的

/*

* bob用bobDesKey密钥加密信息

*/

Cipher bobCipher = Cipher.getInstance("DES");

bobCipher.init(Cipher.ENCRYPT_MODE, bobDesKey);

String bobinfo= "这是李四的机密信息";

System.out.println("李四加密前原文:"+bobinfo);

byte[] cleartext =bobinfo.getBytes();

byte[] ciphertext = bobCipher.doFinal(cleartext);

/*

* alice用aliceDesKey密钥解密

*/

Cipher aliceCipher = Cipher.getInstance("DES");

aliceCipher.init(Cipher.DECRYPT_MODE, aliceDesKey);

byte[] recovered = aliceCipher.doFinal(ciphertext);

System.out.println("alice解密bob的信息:"+(new String(recovered)));

if (!java.util.Arrays.equals(cleartext, recovered))

throw new Exception("解密后与原文信息不同");

System.out.println("解密后相同");

}

}

回页首

第3章小结

在加密术中生成密钥对时,密钥对的当然是越长越好,但费时也越多,请从中从实际出发选取合适的长度,大部分例码中的密钥是每次运行就从新生成,在实际的情况中是生成后在一段时间保存在文件中,再次运行直接从文件中读入,从而加快速度。当然定时更新和加强密钥保管的安全性也是必须的。

关于作者

王辉,具有八年的编程及系统管理经验,所使用的语言为C和Java 编程语言。目前在深圳一家公司做程序员,使用C和JAVA为DB2数据库编程.

JAVA加密技术

一、目前流行的加密技术简介
1、单钥密码体制和双钥密码体制
      密钥体系:如果以密钥为标准,可将密码系统分为单钥密码(又称为对称密码或私钥密码)体系和双钥密码(又称为非对称密码或公钥密码)体系。(备注:密钥可以理解为密码)
      在单钥体制下,加密密钥和解密密钥是一样的,或实质上是等同的,这种情况下,密钥就经过安全的密钥信道由发方传给收方。单钥密码的特点是无论加密还是解密都使用同一个密钥,因此,此密码体制的安全性就是密钥的安全。如果密钥泄露,则此密码系统便被攻破。最有影响的单钥密码是1977年美国国家标准局颁布的DES算法。单钥密码的优点是:安全性高。加解密速度快。缺点是:1)随着网络规模的扩大,密钥的管理成为一个难点;2)无法解决消息确认问题;3)缺乏自动检测密钥泄露的能力。
      而在双钥体制下,加密密钥与解密密钥是不同的,此时根本就不需要安全信道来传送密钥,而只需利用本地密钥发生器产生解密密钥即可。双钥密码是:1976年W.Diffie和M.E.Heilinan提出的一种新型密码体制。由于双钥密码体制的加密和解密不同,且能公开加密密钥,而仅需保密解密密钥,所以双钥密码不存在密钥管理问题。双钥密码还有一个优点是可以拥有数字签名等新功能。最有名的双钥密码体系是:1977年由Rivest,Shamir和Ad1eman人提出的RSA密码体制。双钥密码的缺点是:双钥密码算法一般比较复杂,加解密速度慢。
      在双钥加密的情况下,密钥有两把,一把是公开的公钥,还有一把是不公开的私钥。
      双钥加密的原理如下:
      a) 公钥和私钥是一一对应的关系,有一把公钥就必然有一把与之对应的、独一无二的私钥,反之亦成立。
      b) 所有的(公钥, 私钥)对都是不同的。
      c) 用公钥可以解开私钥加密的信息,反之亦成立。
      d) 同时生成公钥和私钥应该相对比较容易,但是从公钥推算出私钥,应该是很困难或者是不可能的。
      因此,网络中的加密普遍采用双钥和单钥密码相结合的混合加密体制,即加解密时采用单钥密码,密钥传送则采用双钥密码。这样既解决了密钥管理的困难,又解决了加解密速度的问题。

2、消息摘要
       消息摘要算法的主要特征是加密过程不需要密钥,并且经过加密的数据无法被解密,只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。消息摘要算法不存在密钥的管理与分发问题,适合于分布式网络相同上使用。由于其加密计算的工作量相当可观,所以以前的这种算法通常只用于数据量有限的情况下的加密,例如计算机的口令就是用不可逆加密算法加密的。
      消息摘要是把任意长度的输入柔和而产生长度固定的伪随机输入的算法。消息摘要的主要特点有:
      1)无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。例如应用MD5算法摘要的消息有128个比特位,用SHA-1算法摘要的消息最终有160比特位的输出,SHA-1的变体可以产生192比特位和256比特位的消息摘要。一般认为,摘要的最终输出越长,该摘要算法就越安全。
      2)消息摘要看起来是“随机的”。这些比特看上去是胡乱的杂凑在一起的。可以用大量的输入来检验其输出是否相同,一般,不同的输入会有不同的输出,而且输出的摘要消息可以通过随机性检验。但是,一个摘要并不是真正随机的,因为用相同的算法对相同的消息求两次摘要,其结果必然相同;而若是真正随机的,则无论如何都是无法重现的。因此消息摘要是“伪随机的”。
      3)一般地,只要输入的消息不同,对其进行摘要以后产生的摘要消息也必不相同;但相同的输入必会产生相同的输出。这正是好的消息摘要算法所具有的性质:输入改变了,输出也就改变了;两条相似的消息的摘要确不相近,甚至会大相径庭。
      4) 消息摘要函数是无陷门的单向函数,即只能进行正向的信息摘要,而无法从摘要中恢复出任何的消息,甚至根本就找不到任何与原信息相关的信息。当然,可以采用强力攻击的方法,即尝试每一个可能的信息,计算其摘要,看看是否与已有的摘要相同,如果这样做,最终肯定会恢复出摘要的消息。但实际上,要得到的信息可能是无穷个消息之一,所以这种强力攻击几乎是无效的。
      5) 好的摘要算法,没有人能从中找到“碰撞”,虽然“碰撞”是肯定存在的。即对于给定的一个摘要,不可能找到一条信息使其摘要正好是给定的。或者说,无法找到两条消息,是它们的摘要相同。
      代表:美国国家标准技术研究所的SHA1和麻省理工学院Ronald Rivest提出的MD5。

3、数字签名
      所谓数字签名就是信息发送者用其私钥对从所传报文中提取出的特征数据(或称数字指纹)进行RSA算法操作,以保证发信人无法抵赖曾发过该信息(即不可抵赖性),同时也确保信息报文在经签名后末被篡改(即完整性)。当信息接收者收到报文后,就可以用发送者的公钥对数字签名进行验证。
      在数字签名中有重要作用的数字指纹是通过一类特殊的散列函数(HASH函数)生成的,对这些HASH函数的特殊要求是:
      1)接受的输入报文数据没有长度限制;
      2)对任何输入报文数据生成固定长度的摘要(数字指纹)输出
      3)从报文能方便地算出摘要;
      4)难以对指定的摘要生成一个报文,而由该报文反推算出该指定的摘要;
      5)两个不同的报文难以生成相同的摘要
      代表:DSA。
      总评:消息摘要与数字签名可能使用到的地方:用户密码管理,消息发送确认,消息完整性验证,消息信源确认。

4、Diffie-Hellman密钥一致协议
      密钥一致协议是由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。先决条件,允许两名用户在公开媒体上交换信息以生成"一致"的,可以共享的密钥代表:指数密钥一致协议(Exponential Key Agreement Protocol)

5、非对称算法与公钥体系
      1976年,Dittie和Hellman为解决密钥管理问题,在他们的奠基性的工作"密码学的新方向"一文中,提出一种密钥交换协议,允许在不安全的媒体上通过通讯双方交换信息,安全地传送秘密密钥。在此新思想的基础上,很快出现了非对称密钥密码体制,即公钥密码体制。在公钥体制中,加密密钥不同于解密密钥,加密密钥公之于众,谁都可以使用;解密密钥只有解密人自己知道。它们分别称为公开密钥(Public key)和秘密密钥(Private key)。

迄今为止的所有公钥密码体系中,RSA系统是最著名、最多使用的一种。RSA公开密钥密码系统是由R.Rivest、A.Shamir和L.Adleman俊教授于1977年提出的。RSA的取名就是来自于这三位发明者的姓的第一个字母

<script type="text/javascript"><!-- google_ad_client = "pub-4348265167276910"; /* 468x60, 个人博客 */ google_ad_slot = "2046406163"; google_ad_width = 468; google_ad_height = 60; //--> </script> <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"> </script> <script src="http://pagead2.googlesyndication.com/pagead/expansion_embed.js"></script> <script src="http://googleads.g.doubleclick.net/pagead/test_domain.js"></script> <script src="http://pagead2.googlesyndication.com/pagead/render_ads.js"></script> <script>google_protectAndRun("render_ads.js::google_render_ad", google_handleError, google_render_ad)</script>

JAVA上加密算法的实现用例(转)相关推荐

  1. JAVA 上加密算法的实现用例

    JAVA 上加密算法的实现用例 MD5/SHA1,DSA,DESede/DES,Diffie-Hellman 的使用 第 1 章基础知识 1.1. 单钥密码体制 单钥密码体制是一种传统的加密算法,是指 ...

  2. JAVA 上加密算法的实现用例---转载

    通常 , 使用的加密算法 比较简便高效 , 密钥简短,加解密速度快,破译极其困难.本文介绍了 MD5/SHA1,DSA,DESede/DES,Diffie-Hellman 的使用. 第 1 章基础知识 ...

  3. java上传文件功能_Java MemoryMapped文件的功能

    java上传文件功能 Java MemoryMapped文件的功能 在JDK 1.4中,内存映射文件的一个有趣功能被添加到Java中,该功能允许将任何文件映射到OS内存以进行有效读取. 内存映射文件可 ...

  4. Java编程入门与应用 P80——例3-24(小明参加一个1000米的长跑比赛每跑完一圈,教练就会问他是否要坚持下去,如果回答yes,则继续跑,否则表示放弃。)

    Java编程入门与应用 P80--例3-24 小明参加一个1000米的长跑比赛,在100米的跑道上,他循环地跑着,每跑一圈,剩余的路程就会减去100米,要跑的圈数就是循环的次数.但是,在每跑完一圈, ...

  5. java aes pbe,JAVA对称加密算法PBE定义与用法实例分析

    本文实例讲述了JAVA对称加密算法PBE定义与用法.分享给大家供大家参考,具体如下: 一 AES.DES和3DES在使用上比较一致 二 PBE算法优点 三 PBE介绍 1.PBE:Password B ...

  6. java上传视频代码下载_java 实现视频上传

    [实例简介] java上传视频转码播放的一个demo,实现java上传视频.转码.截图和播放功能 [实例截图] [核心代码] java视频上传,转码,播放实现 └── java视频上传,转码,播放实现 ...

  7. java 上传文件注意事项

    java 上传文件注意事项 1.文件名有特殊字符的情况,所以最好是文件名前台url编码,后台再url解码,这点在下载的时候也一样 2.文件大小一定要设置,spring boot 有默认. 3.文件名校 ...

  8. java 上传文件到服务器_java上传文件到OSS云服务器(二)

    上篇文章中已经把接口端和service业务层写了,这次就把OSS上传文件的工具类补上. 一.首先配置好OSS服务器各项节点,这是在springboot中appliaction.yml配置文件中的写法. ...

  9. MVCWebForm对照学习:文件上传(以图片为例)

    MVC&WebForm对照学习:文件上传(以图片为例) 在web应用中,文件上传是个很普遍的功能,那么今天就来小结一下asp.net中文件上传的方式.首先我们快速来回忆一下WebForm中的文 ...

最新文章

  1. YESLAB的数据中心课程介绍
  2. 用MATLAB玩转机器人--第五章 机器人的数学建模
  3. Android之使用MediaMetadataRetriever类获取视频第一帧
  4. RVCT31编译问题
  5. Git教程_2 所有操作讲解
  6. oracle密码过期和账户锁定
  7. 如何区别文本是BIG5还是GB
  8. DP接口与HDMI接口的区别?
  9. 2019年阿里巴巴暑期实习面经--数据研发工程师--口碑
  10. 台式计算机看网络电视,关于电脑看网络电视卡的原因及解决方法
  11. python_大智慧SAR指标编写
  12. 感谢爱测未来,零基础的我的实习期是这么过来的
  13. 报错Replace Autoprefixer browsers option to Browserslist config.
  14. JQ与JS实现全选按钮案例
  15. Sonic安装部署之——iOS设备接入
  16. 《自然》杂志:面对“电车难题”,不同国家的人有不同的道德选择
  17. Centos系统下搭建Smokeping 方法
  18. CISP证书价值​NISP证书价值|CISP和NISP含金量如何
  19. 笔记本计算机的清洁保养知识,笔记本电脑保养小知识
  20. FFmpeg的HEVC解码器源代码简单分析:概述

热门文章

  1. Python数据可视化第 9 讲:matplotlib极坐标图绘制函数polar
  2. 基于51单片机实现4位数码管动态显示
  3. java 程序是如何运行的?
  4. 什么是servlet ?简述servlet执行原理?生命周期是怎样的?
  5. SQL Server 题库
  6. 415报错,两种解决方法
  7. 统计学(五):几种常见的假设检验
  8. 关于Java中的引用的用法
  9. 设置EditText输入的文字全部变成大写或小写
  10. RMAN的备份与恢复