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

6)keystore提取私钥和证书  

 

 1)消息摘要


Java代码



    1. package 
    2.     
    3. import 
    4.     
    5. /** 
    6.  * 消息摘要是一种与消息认证码结合使用以确保消息完整性的技术 
    7.  * 目前广泛使用的算法有MD4、MD5、SHA-1 
    8.  * 在java中进行消息摘要很简单, java.security.MessageDigest提供了一个简易的操作方法,如下 
    9.  * 注意:消息摘要是单向的 
    10.  */
    11. public    class 
    12. public    static    void   main(String[] args)  throws 
    13.              
    14. "asdf"
    15. "摘要前:"
    16.              
    17. //初始信息要转换成字节流的形式 
    18. byte  [] plainText = beforeDegist.getBytes( "UTF8"
    19.     
    20. //使用getInstance("算法")来获得消息摘要,这里使用SHA-1的160位算法 
    21. //        MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); 
    22. "MD5"
    23.              
    24. //        System.out.println("\n" + messageDigest.getProvider().getInfo()); 
    25.              
    26. //开始使用算法 
    27.          messageDigest.update(plainText);    
    28.              
    29. //输出算法运算结果 
    30. new   String(messageDigest.digest(), "UTF8"
    31. "摘要后:"
    32.      }    
    33.  }


    2)私钥加密


    Java代码

    1. package 
    2.     
    3. import 
    4. import 
    5. import 
    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 
    17. public    static    void   main(String[] args)  throws 
    18.     
    19. "asdf"
    20. byte  [] plainText = before.getBytes( "UTF8"
    21.              
    22. //1步********************************************************************** 
    23. "Start generate AES key."
    24. //得到一个使用AES算法的KeyGenerator的实例 
    25. "AES"
    26. //定义密钥长度128位 
    27. 128
    28. //通过KeyGenerator产生一个key(密钥算法刚才已定义,为AES) 
    29.          Key key = keyGen.generateKey();    
    30. "Finish generating AES key."
    31.     
    32.              
    33. //2步********************************************************************** 
    34. //获得一个私钥加密类Cipher,定义Cipher的基本信息:ECB是加密方式,PKCS5Padding是填充方法 
    35. "AES/ECB/PKCS5Padding"
    36. //        System.out.println("\n" + cipher.getProvider().getInfo()); 
    37.     
    38.              
    39. //3步********************************************************************** 
    40. //使用私钥加密 
    41. "\n用私钥加密..."
    42. //把刚才生成的key当作参数,初始化使用刚才获得的私钥加密类,Cipher.ENCRYPT_MODE意思是加密 
    43.          cipher.init(Cipher.ENCRYPT_MODE, key);    
    44.              
    45. //私钥加密类Cipher进行加密,加密后返回一个字节流byte[] 
    46. byte 
    47.              
    48. //以UTF8格式把字节流转化为String 
    49. new   String(cipherText,  "UTF8"
    50. "用私钥加密完成:"
    51.     
    52.              
    53. //4步********************************************************************** 
    54. //使用私钥对刚才加密的信息进行解密,看看是否一致,Cipher.DECRYPT_MODE意思是解密钥 
    55. "\n用私钥解密..."
    56.          cipher.init(Cipher.DECRYPT_MODE, key);    
    57.              
    58. //对刚才私钥加密的字节流进行解密,解密后返回一个字节流byte[] 
    59. byte 
    60.              
    61. new   String(newPlainText,  "UTF8"
    62. "用私钥解密完成:"
    63.      }    
    64.  }


     

    3)公钥加密
    Java代码


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


    4)数字签名

    Java代码


    1. package 
    2.     
    3. import 
    4. import 
    5. import 
    6. import 
    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 
    24. public    static    void   main(String[] args)  throws 
    25.     
    26. "asdf"
    27. byte  [] plainText = before.getBytes( "UTF8"
    28.     
    29. //形成RSA公钥对 
    30. "RSA"
    31. 1024
    32.          KeyPair key = keyGen.generateKeyPair();    
    33.     
    34. //使用私钥签名********************************************************** 
    35. "SHA1WithRSA"
    36. //sig对象得到私钥 
    37. //签名对象得到原始数据 
    38. //sig对象得到原始数据(现实中用的是原始数据的摘要,摘要的是单向的,即摘要算法后无法解密) 
    39. byte  [] signature = sig.sign(); //sig对象用私钥对原始数据进行签名,签名后得到签名signature 
    40. //        System.out.println(sig.getProvider().getInfo()); 
    41. new   String(signature,  "UTF8"
    42. "\n用私钥签名后:"
    43.     
    44. //使用公钥验证********************************************************** 
    45. //sig对象得到公钥 
    46. //签名对象得到原始信息 
    47. //sig对象得到原始数据(现实中是摘要) 
    48. try 
    49. if   (sig.verify(signature)) { //sig对象用公钥解密签名signature得到原始数据(即摘要),一致则true 
    50. "签名验证正确!!"
    51. else 
    52. "签名验证失败!!"
    53.              }    
    54. catch 
    55. "签名验证失败!!"
    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 
    2.     
    3. import 
    4. import 
    5. import 
    6. import 
    7. import 
    8. import 
    9. import 
    10. import 
    11. import 
    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 
    25.     
    26. public    static    void 
    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. "keystore"
    39. new   FileInputStream( "C:/BocsoftKeyLib"
    40. "JKS"
    41.              ks.load(in2,pass.toCharArray());    
    42. "TestCertification" ;  //alias为条目的别名 
    43.              Certificate c=ks.getCertificate(alias);    
    44. "转换成String后的证书信息:"
    45.                  
    46.                  
    47. //获取获取X509Certificate类型的对象,这是证书类获取Certificate的子类,实现了更多方法 
    48.              X509Certificate t=(X509Certificate)c;    
    49. //从信息中提取需要信息 
    50. "版本号:"
    51. "序列号:" +t.getSerialNumber().toString( 16
    52. "主体名:"
    53. "签发者:"
    54. "有效期:"
    55. "签名算法:"
    56. byte   [] sig=t.getSignature(); //签名值 
    57.              PublicKey pk = t.getPublicKey();     
    58. byte 
    59. "公钥:"
    60. for  ( int   i= 0
    61. ","
    62.              }    
    63.              System.err.println();    
    64.                  
    65.                  
    66. //证书的日期有效性检查,颁发的证书都有一个有效性的日期区间 
    67. new 
    68.              t.checkValidity(TimeNow);       
    69. "证书的日期有效性检查:有效的证书日期!"
    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. "证书的日期有效性检查:过期"
    85. catch  (CertificateNotYetValidException e){  //证书的日期有效性检查:尚未生效    
    86. "证书的日期有效性检查:尚未生效"
    87. catch 
    88.              ce.printStackTrace();    
    89. catch 
    90.              fe.printStackTrace();    
    91. /*catch (IOException ioe){ 
    92.              
    93.         } catch (KeyStoreException kse){ 
    94.              
    95.         }*/   catch 
    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"));