`

数字加密和数字签名

阅读更多

最近初步接触了下Java加密和数字签名的相关内容,我学习的过程大概分五步:
1)消息摘要
2)私钥加密
3)公钥加密
4)数字签名
5)数字证书

6)keystore提取私钥和证书  

 

 1)消息摘要

Java代码 复制代码
  1. package  security;   
  2.   
  3. import  java.security.MessageDigest;   
  4.   
  5. /**  
  6.  * 消息摘要是一种与消息认证码结合使用以确保消息完整性的技术  
  7.  * 目前广泛使用的算法有MD4、MD5、SHA-1  
  8.  * 在java中进行消息摘要很简单, java.security.MessageDigest提供了一个简易的操作方法,如下  
  9.  * 注意:消息摘要是单向的  
  10.  */   
  11. public   class  MessageDigestExample {   
  12.      public   static   void  main(String[] args)  throws  Exception {   
  13.            
  14.         String beforeDegist =  "asdf" ;   
  15.         System.out.println( "摘要前:" +beforeDegist);     
  16.            
  17.          //初始信息要转换成字节流的形式   
  18.          byte [] plainText = beforeDegist.getBytes( "UTF8" );   
  19.   
  20.          //使用getInstance("算法")来获得消息摘要,这里使用SHA-1的160位算法   
  21. //        MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");   
  22.         MessageDigest messageDigest = MessageDigest.getInstance( "MD5" );   
  23.            
  24. //        System.out.println("\n" + messageDigest.getProvider().getInfo());   
  25.            
  26.          //开始使用算法   
  27.         messageDigest.update(plainText);   
  28.            
  29.          //输出算法运算结果   
  30.         String afterDegist =  new  String(messageDigest.digest(), "UTF8" );   
  31.         System.out.println( "摘要后:" +afterDegist);   
  32.     }   
  33. }  
 
2)私钥加密
Java代码 复制代码
  1. package  security;   
  2.   
  3. import  javax.crypto.Cipher;   
  4. import  javax.crypto.KeyGenerator;   
  5. import  java.security.Key;   
  6.   
  7. /**  
  8.  * 此例子是对一个字符串信息,用一个私钥(key)加密,然后在用该私钥解密,验证是否一致  
  9.  * 私钥加密,是对称加密  
  10.  * 使用对称算法。比如:A用一个密钥对一个文件加密,而B读取这个文件的话,则需要和A一样的密钥,双方共享一  
  11.  * 个私钥(而在web环境下,私钥在传递时容易被侦听)  
  12.  *   
  13.  * 附:主要对称算法有:DES(实际密钥只用到56 位)  
  14.  * AES(支持三种密钥长度:128、192、256位),通常首先128位,其他的还有DESede等  
  15.  */   
  16. public   class  PrivateKey {   
  17.      public   static   void  main(String[] args)  throws  Exception {   
  18.   
  19.         String before =  "asdf" ;          
  20.          byte [] plainText = before.getBytes( "UTF8" );   
  21.            
  22.          //1步**********************************************************************   
  23.         System.out.println( "Start generate AES key." );   
  24.          //得到一个使用AES算法的KeyGenerator的实例   
  25.         KeyGenerator keyGen = KeyGenerator.getInstance( "AES" );   
  26.          //定义密钥长度128位   
  27.         keyGen.init( 128 );   
  28.          //通过KeyGenerator产生一个key(密钥算法刚才已定义,为AES)   
  29.         Key key = keyGen.generateKey();   
  30.         System.out.println( "Finish generating AES key." +key);   
  31.   
  32.            
  33.          //2步**********************************************************************   
  34.          //获得一个私钥加密类Cipher,定义Cipher的基本信息:ECB是加密方式,PKCS5Padding是填充方法   
  35.         Cipher cipher = Cipher.getInstance( "AES/ECB/PKCS5Padding" );   
  36. //        System.out.println("\n" + cipher.getProvider().getInfo());   
  37.   
  38.            
  39.          //3步**********************************************************************   
  40.          //使用私钥加密   
  41.         System.out.println( "\n用私钥加密..." );   
  42.          //把刚才生成的key当作参数,初始化使用刚才获得的私钥加密类,Cipher.ENCRYPT_MODE意思是加密   
  43.         cipher.init(Cipher.ENCRYPT_MODE, key);   
  44.            
  45.          //私钥加密类Cipher进行加密,加密后返回一个字节流byte[]   
  46.          byte [] cipherText = cipher.doFinal(plainText);   
  47.            
  48.          //以UTF8格式把字节流转化为String   
  49.         String after1 =  new  String(cipherText,  "UTF8" );   
  50.         System.out.println( "用私钥加密完成:" +after1);   
  51.   
  52.            
  53.          //4步**********************************************************************   
  54.          //使用私钥对刚才加密的信息进行解密,看看是否一致,Cipher.DECRYPT_MODE意思是解密钥   
  55.         System.out.println( "\n用私钥解密..." );   
  56.         cipher.init(Cipher.DECRYPT_MODE, key);   
  57.            
  58.          //对刚才私钥加密的字节流进行解密,解密后返回一个字节流byte[]   
  59.          byte [] newPlainText = cipher.doFinal(cipherText);   
  60.            
  61.         String after2 =  new  String(newPlainText,  "UTF8" );   
  62.         System.out.println( "用私钥解密完成:" +after2);   
  63.     }   
  64. }  

 

3)公钥加密
Java代码 复制代码

  1. package  security;   
  2.   
  3. import  java.security.KeyPair;   
  4. import  java.security.KeyPairGenerator;   
  5. import  javax.crypto.Cipher;   
  6.   
  7. /**  
  8.  * 此例子是一个公钥加密例子,Cipher类使用KeyPairGenerator(顾名思义:一对钥匙生成器)生成的公钥和私钥  
  9.  *   
  10.  * 公钥加密也叫不对称加密,不对称算法使用一对密钥对,一个公钥,一个私钥,使用公钥加密的数据,只有私钥能  
  11.  * 解开(可用于加密);同时,使用私钥加密的数据,只有公钥能解开(签名)。但是速度很慢(比私钥加密慢100到  
  12.  * 1000倍),公钥的主要算法有RSA,还包括Blowfish,Diffie-Helman 等  
  13.  */   
  14. public   class  PublicKey {   
  15.      public   static   void  main(String[] args)  throws  Exception {   
  16.            
  17.         String before =  "asdf" ;          
  18.          byte [] plainText = before.getBytes( "UTF8" );   
  19.            
  20.          //产生一个RSA密钥生成器KeyPairGenerator(顾名思义:一对钥匙生成器)   
  21.         KeyPairGenerator keyGen = KeyPairGenerator.getInstance( "RSA" );   
  22.          //定义密钥长度1024位   
  23.         keyGen.initialize( 1024 );   
  24.          //通过KeyPairGenerator产生密钥,注意:这里的key是一对钥匙!!   
  25.         KeyPair key = keyGen.generateKeyPair();   
  26.   
  27.          //获得一个RSA的Cipher类,使用公钥加密   
  28.         Cipher cipher = Cipher.getInstance( "RSA/ECB/PKCS1Padding" );   
  29. //        System.out.println("\n" + cipher.getProvider().getInfo());   
  30.   
  31.         System.out.println( "\n用公钥加密..." );   
  32.          //Cipher.ENCRYPT_MODE意思是加密,从一对钥匙中得到公钥 key.getPublic()   
  33.         cipher.init(Cipher.ENCRYPT_MODE, key.getPublic());   
  34.          //用公钥进行加密,返回一个字节流   
  35.          byte [] cipherText = cipher.doFinal(plainText);   
  36.          //以UTF8格式把字节流转化为String   
  37.         String after1 =  new  String(cipherText,  "UTF8" );   
  38.         System.out.println( "用公钥加密完成:" +after1);   
  39.   
  40.            
  41.          //使用私钥解密   
  42.         System.out.println( "\n用私钥解密..." );   
  43.          //Cipher.DECRYPT_MODE意思是解密,从一对钥匙中得到私钥 key.getPrivate()   
  44.         cipher.init(Cipher.DECRYPT_MODE, key.getPrivate());   
  45.          //用私钥进行解密,返回一个字节流   
  46.          byte [] newPlainText = cipher.doFinal(cipherText);   
  47.   
  48.         String after2 =  new  String(newPlainText,  "UTF8" );   
  49.         System.out.println( "用私钥解密完成:" +after2);   
  50.     }   
  51. }  

4)数字签名

Java代码 复制代码
  1. package  security;   
  2.   
  3. import  java.security.Signature;   
  4. import  java.security.KeyPairGenerator;   
  5. import  java.security.KeyPair;   
  6. import  java.security.SignatureException;   
  7.   
  8. /**  
  9.  * 此例子是数字签名的例子,使用RSA私钥对消息摘要(这里指的是原始数据)进行签名,然后使用公钥验证签名  
  10.  *   
  11.  * A通过使用B的公钥加密数据后发给B,B利用B的私钥解密就得到了需要的数据(进过B公钥加密的数据只有B的私钥能够  
  12.  * 解开,C没有B的私钥,所以C解不开,但C可以使用B的公钥加密一份数据发给B,这样一来,问题来了,B收到的数据到  
  13.  * 底是A发过来的还是C发过来的呢)  
  14.  * 由于私钥是唯一的,那么A就可以利用A自己的私钥进行加密,然后B再利用A的公钥来解密,就可以确定:一定是A的消  
  15.  * 息,数字签名的原理就基于此  
  16.  *   
  17.  * 总结:A想将目标数据传给B,此时A需要准备1和2两部分  
  18.  * 1:A使用B的公钥将原始信息加密,以起到保密作用(只有B的私钥能解开,其他人使用其他钥匙都解不开,当然就保密咯)  
  19.  * 2:A使用A的私钥将原始信息的摘要进行签名,以起到接收方B确定是A发过来的作用(A用A的私钥对目标数据的摘要进行签  
  20.  * 名,然后传给B,同时,C用C的私钥对任意信息进行签名也传给B,B想接受的是A的数据(比如说一个转帐请求),于是B  
  21.  * 就通过A的公钥对接受到的两个信息进行解密,解开的就是A(A的公钥能且只能解开A的私钥加密的数据))  
  22.  */   
  23. public   class  DigitalSignature {   
  24.      public   static   void  main(String[] args)  throws  Exception {   
  25.   
  26.         String before =  "asdf" ;   
  27.          byte [] plainText = before.getBytes( "UTF8" );   
  28.   
  29.          //形成RSA公钥对   
  30.         KeyPairGenerator keyGen = KeyPairGenerator.getInstance( "RSA" );   
  31.         keyGen.initialize( 1024 );   
  32.         KeyPair key = keyGen.generateKeyPair();   
  33.   
  34.          //使用私钥签名**********************************************************   
  35.         Signature sig = Signature.getInstance( "SHA1WithRSA" );   
  36.         sig.initSign(key.getPrivate()); //sig对象得到私钥   
  37.          //签名对象得到原始数据   
  38.         sig.update(plainText); //sig对象得到原始数据(现实中用的是原始数据的摘要,摘要的是单向的,即摘要算法后无法解密)   
  39.          byte [] signature = sig.sign(); //sig对象用私钥对原始数据进行签名,签名后得到签名signature   
  40. //        System.out.println(sig.getProvider().getInfo());   
  41.         String after1 =  new  String(signature,  "UTF8" );   
  42.         System.out.println( "\n用私钥签名后:" +after1);   
  43.   
  44.          //使用公钥验证**********************************************************   
  45.         sig.initVerify(key.getPublic()); //sig对象得到公钥   
  46.          //签名对象得到原始信息   
  47.         sig.update(plainText); //sig对象得到原始数据(现实中是摘要)   
  48.          try  {   
  49.              if  (sig.verify(signature)) { //sig对象用公钥解密签名signature得到原始数据(即摘要),一致则true   
  50.                 System.out.println( "签名验证正确!!" );   
  51.             }  else  {   
  52.                 System.out.println( "签名验证失败!!" );   
  53.             }   
  54.         }  catch  (SignatureException e) {   
  55.             System.out.println( "签名验证失败!!" );   
  56.         }   
  57.     }   
  58. }  


5)数字证书

下面的代码是第五部分:数字证书
比起前四部分,这部分就稍微麻烦点了,我想我有必要给刚刚接触数字证书的朋友们,把在本地跑通下面代码的前提说一下:
1此例是对“数字证书”文件的操作,所以,你先要在本地建立一个证书库

2建立证书库(密钥库)
cmd中输入:C:/>keytool -genkey -alias TestCertification -keyalg RSA -keysize 1024 -keystore BocsoftKeyLib -validity 365
意思是:在c盘目录下创建一个证书,指定证书库为BocsoftKeyLib,创建别名为TestCertification的一条证书,它指定用 RSA 算法生成,且指定密钥长度为1024,证书有效期为1年
建立库的过程中会询问问题,详见第5条

3将证书导出到证书文件TC.cer
在cmd中输入:C:/>keytool -export -alias TestCertification -file TC.cer -keystore BocsoftKeyLib
意思是:将把证书库BocsoftKeyLib中的别名为TestCertification的证书导出到TC.cer证书文件中,它包含证书主体的信息及证书的公钥,不包括私钥,可以公开

4以上3步就基本上完成了证书的操作,下面操作是可选的
导出的证书文件无法用文本编辑器正确显示,可以输入如下命令,然后在以记事本形式打开TC.cer就能看了,看看传说中的证书里到底写的什么鬼东西~~~
C:\>keytool -export -alias TestCertification -file TC.cer -keystore BocsoftKeyLib -storepass keystore -rfc

5在创建证书库时,系统会询问如下问题:
输入keystore密码:  keystore(你证书库的密码)
您的名字与姓氏是什么?
  [Unknown]:  miaozhuang
您的组织单位名称是什么?
  [Unknown]:  csii
您的组织名称是什么?
  [Unknown]:  csii
您所在的城市或区域名称是什么?
  [Unknown]:  tianjin
您所在的州或省份名称是什么?
  [Unknown]:  tianjin
该单位的两字母国家代码是什么
  [Unknown]:  ch
CN=miaozhuang, OU=csii, O=csii, L=tianjin, ST=tianjin, C=ch 正确吗?
  [否]:
上面的信息都是随便输入的,无关大局
此时输入: y 回车即可(别像我一样,输个yes……傻死了)



 

Java代码 复制代码
  1. package  security;   
  2.   
  3. import  java.security.*;   
  4. import  java.security.PublicKey;   
  5. import  java.io.*;   
  6. import  java.util.*;   
  7. import  java.security.*;   
  8. import  java.security.cert.*;   
  9. import  sun.security.x509.*;   
  10. import  java.security.cert.Certificate;   
  11. import  java.security.cert.CertificateFactory;   
  12.   
  13. /**  
  14.  * 此例是对“数字证书”文件的操作  
  15.  * java平台(在机器上安装jdk)为你提供了密钥库(证书库),cmd下提供了keytool命令就可以创建证书库  
  16.  *   
  17.  * 在运行此例前:  
  18.  * 在c盘目录下创建一个证书,指定证书库为BocsoftKeyLib,创建别名为TestCertification的一条证书,它指定用   
  19.  * RSA 算法生成,且指定密钥长度为1024,证书有效期为1年  
  20.  * 导出证书文件为TC.cer已存于本地磁盘C:/  
  21.  * 密码是keystore  
  22.  */   
  23.   
  24. public   class  DigitalCertificate {   
  25.   
  26.      public   static   void  main(String[] args) {   
  27.          try  {   
  28.              //前提:将证书库中的一条证书导出到证书文件(我写的例子里证书文件叫TC.cer)   
  29.              //从证书文件TC.cer里读取证书信息   
  30.              /*CertificateFactory cf = CertificateFactory.getInstance("X.509");  
  31.             FileInputStream in = new FileInputStream("C:/TC.cer");  
  32.             //将文件以文件流的形式读入证书类Certificate中  
  33.             Certificate c = cf.generateCertificate(in);  
  34.             System.err.println("转换成String后的证书信息:"+c.toString());*/   
  35.                
  36.                
  37.              //或者不用上面代码的方法,直接从证书库中读取证书信息,和上面的结果一摸一样   
  38.             String pass= "keystore" ;      
  39.             FileInputStream in2= new  FileInputStream( "C:/BocsoftKeyLib" );      
  40.             KeyStore ks=KeyStore.getInstance( "JKS" );      
  41.             ks.load(in2,pass.toCharArray());   
  42.             String alias =  "TestCertification" //alias为条目的别名   
  43.             Certificate c=ks.getCertificate(alias);   
  44.             System.err.println( "转换成String后的证书信息:" +c.toString());   
  45.                
  46.                
  47.              //获取获取X509Certificate类型的对象,这是证书类获取Certificate的子类,实现了更多方法   
  48.             X509Certificate t=(X509Certificate)c;   
  49.              //从信息中提取需要信息   
  50.             System.out.println( "版本号:" +t.getVersion());      
  51.             System.out.println( "序列号:" +t.getSerialNumber().toString( 16 ));      
  52.             System.out.println( "主体名:" +t.getSubjectDN());      
  53.             System.out.println( "签发者:" +t.getIssuerDN());      
  54.             System.out.println( "有效期:" +t.getNotBefore());      
  55.             System.out.println( "签名算法:" +t.getSigAlgName());      
  56.              byte  [] sig=t.getSignature(); //签名值   
  57.             PublicKey pk = t.getPublicKey();    
  58.              byte  [] pkenc=pk.getEncoded();      
  59.             System.out.println( "公钥:" );      
  60.              for ( int  i= 0 ;i<pkenc.length;i++){   
  61.                 System.out.print(pkenc[i]+ "," );      
  62.             }   
  63.             System.err.println();   
  64.                
  65.                
  66.              //证书的日期有效性检查,颁发的证书都有一个有效性的日期区间   
  67.             Date TimeNow= new  Date();         
  68.             t.checkValidity(TimeNow);      
  69.             System.out.println( "证书的日期有效性检查:有效的证书日期!" );      
  70.   
  71.                
  72.              //验证证书签名的有效性,通过数字证书认证中心(CA)机构颁布给客户的CA证书,比如:caroot.crt文件   
  73.              //我手里没有CA颁给我的证书,所以下面代码执行不了   
  74.              /*FileInputStream in3=new FileInputStream("caroot.crt");     
  75.             //获取CA证书  
  76.             Certificate cac = cf.generateCertificate(in3);     
  77.             //获取CA的公钥     
  78.             PublicKey pbk=cac.getPublicKey();     
  79.             //c为本地证书,也就是待检验的证书,用CA的公钥校验数字证书c的有效性  
  80.             c.verify(pbk);*/   
  81.                
  82.                
  83.         }  catch (CertificateExpiredException e){ //证书的日期有效性检查:过期      
  84.             System.out.println( "证书的日期有效性检查:过期" );         
  85.         }  catch (CertificateNotYetValidException e){  //证书的日期有效性检查:尚未生效      
  86.             System.out.println( "证书的日期有效性检查:尚未生效" );      
  87.         }  catch  (CertificateException ce) {   
  88.             ce.printStackTrace();   
  89.         }  catch  (FileNotFoundException fe) {   
  90.             fe.printStackTrace();   
  91.         }  /*catch (IOException ioe){  
  92.               
  93.         } catch (KeyStoreException kse){  
  94.               
  95.         }*/   catch  (Exception e){   
  96.             e.printStackTrace();   
  97.         }   
  98.   
  99.     }   
  100. }  

 

 

 6)keystore提取私钥和证书  

 

keytool -genkey -alias test -keyalg RSA -keystore c:/key.store    生成keyStore

RSA是一个既能用于数据加密也能用于数字签名的算法。

DSA(Digital Signature Algorithm,数字签名算法,用作数字签名标准的一部分),它是另一种公开密钥算法,它不能用作加密,只用作数字签名。DSA使用公开密钥,为接受者验证数据的完整性和数据发送者的身份。

 

提取证书:通过keytool命令我们可以很轻松的提取证书.证书包括主体信息,公钥.

keytool -export -alias 别名 -keystore 文件名 -file 证书名称

 

但是我们无法通过KEYTOOL工具来提取私钥的..我们只能通过java的KeyStore类getEntry() 或者getKey()来提取私钥.

 

 String keystoreFilename = "C:/keystore/server.keystore";
  char[] password = "123456".toCharArray();
  String alias = "Server";
  KeyStore ks = KeyStore.getInstance("JKS");
  java.io.FileInputStream fis = new java.io.FileInputStream(keystoreFilename);
  // 从指定的输入流中加载此 KeyStore
  ks.load(fis, password);
  // keystore 中的每一项都用“别名”字符串标识。
  // 使用指定保护参数获取指定别名的 keystore Entry。 

方法1、
  // KeyStore.PrivateKeyEntry 保存 PrivateKey 和相应证书链的 KeyStore 项。
  KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) ks.getEntry(alias, 
                                                                               new KeyStore.PasswordProtection(password));
  System.out.println(pkEntry.getPrivateKey());

 

//  返回与给定别名相关联的密钥
方法2、
PrivateKey key = (PrivateKey) ks.getKey(alias, password);

 

 

怎么来验证提取的私钥是否正确呢?(因为公钥私钥必须成对出现,我们可以通过证书提取去公钥,然后用公钥加密,使用刚刚获得的私钥解密)

 

提取证书的方法:

keytool -export -alias 别名 -keystore 文件名 -file 证书名称

 

//通过证书,获取公钥
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStream in = new FileInputStream("C:\\server\\server.cer");

//生成一个证书对象并使用从输入流 inStream 中读取的数据对它进行初始化。
Certificate c = cf.generateCertificate(in);
PublicKey publicKey = c.getPublicKey(); 

//通过下面这段代码提取的私钥是否正确
 String before = "asdf";
 byte[] plainText = before.getBytes("UTF-8");
 Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
 cipher.init(Cipher.ENCRYPT_MODE, publicKey);
 // 用公钥进行加密,返回一个字节流
 byte[] cipherText = cipher.doFinal(plainText);
 cipher.init(Cipher.DECRYPT_MODE, myPrivateKey);
 // 用私钥进行解密,返回一个字节流
 byte[] newPlainText = cipher.doFinal(cipherText);
 System.out.println(new String(newPlainText, "UTF-8"));
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics