1. 程式人生 > >Java加解密密和數字簽名

Java加解密密和數字簽名

最近初步接觸了下Java加密和數字簽名的相關內容,目前學習的主要內容如下:

1)訊息摘要

2)私鑰加密

3)公鑰加密

4)數字簽名

5)數字證書 


以下是對這幾步的程式碼例項,程式碼就是我們的語言  哈

1. 訊息摘要
        String beforeDegist = "asdf";  
        System.out.println("摘要前:"+beforeDegist);    
          
        //初始資訊要轉換成位元組流的形式  
        byte[] plainText = beforeDegist.getBytes("UTF8");  
 
        //使用getInstance("演算法")來獲得訊息摘要,這裡使用SHA-1的160位演算法或者MD5演算法
	//MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");  
        MessageDigest messageDigest = MessageDigest.getInstance("MD5");  
          
	//System.out.println("/n" + messageDigest.getProvider().getInfo());  
          
        //開始使用演算法  
        messageDigest.update(plainText);  
          
        //輸出演算法運算結果  
        String afterDegist = new String(messageDigest.digest(),"UTF8");  
        System.out.println("摘要後:"+afterDegist); 
              
2. 私鑰加密
  /** 
   * 此例子是對一個字串資訊,用一個私鑰(key)加密,然後在用該私鑰解密,驗證是否一致 
   * 私鑰加密,是對稱加密 
   * 使用對稱演算法。比如:A用一個金鑰對一個檔案加密,而B讀取這個檔案的話,則需要和A一樣的金鑰,雙方共享一 
   * 個私鑰(而在web環境下,私鑰在傳遞時容易被偵聽) 
   *  
   * 附:主要對稱演算法有:DES(實際金鑰只用到56 位) 
   * AES(支援三種金鑰長度:128、192、256位),通常首先128位,其他的還有DESede等 
   */ 
String before = "asdf";         
        byte[] plainText = before.getBytes("UTF8");  
          
        // STEP 1.               
        System.out.println("Start generate AES key.");  

        //得到一個使用AES演算法的KeyGenerator的例項  
        KeyGenerator keyGen = KeyGenerator.getInstance("AES");  

        //定義金鑰長度128位  
        keyGen.init(128);  

        //通過KeyGenerator產生一個key(金鑰演算法剛才已定義,為AES)  
        Key key = keyGen.generateKey();  

        System.out.println("Finish generating AES key="+key);  

        //STEP 2.       
        //獲得一個私鑰加密類Cipher,定義Cipher的基本資訊:ECB是加密方式,PKCS5Padding是填充方法  
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");  
	//System.out.println("/n" + cipher.getProvider().getInfo());  
         
        //STEP 3.      
        // 使用私鑰加密  
        System.out.println("/n用私鑰加密...");  
        // 把剛才生成的key當作引數,初始化使用剛才獲得的私鑰加密類,Cipher.ENCRYPT_MODE意思是加密  
        cipher.init(Cipher.ENCRYPT_MODE, key);  
          
        //私鑰加密類Cipher進行加密,加密後返回一個位元組流byte[]  
        byte[] cipherText = cipher.doFinal(plainText);  
          
        //以UTF8格式把位元組流轉化為String  
        String after1 = new String(cipherText, "UTF8");  
        System.out.println("用私鑰加密完成:"+after1);  
          
        // STEP 4.     
        //使用私鑰對剛才加密的資訊進行解密,看看是否一致,Cipher.DECRYPT_MODE意思是解金鑰  
        System.out.println("/n用私鑰解密...");  
        cipher.init(Cipher.DECRYPT_MODE, key);  
          
        //對剛才私鑰加密的位元組流進行解密,解密後返回一個位元組流byte[]  
        byte[] newPlainText = cipher.doFinal(cipherText);  
          
        String after2 = new String(newPlainText, "UTF8");  
        System.out.println("用私鑰解密完成:"+after2);  

3. 公鑰加密
        String before = "asdf";         
        byte[] plainText = before.getBytes("UTF8");  
          
        //產生一個RSA金鑰生成器KeyPairGenerator(顧名思義:一對鑰匙生成器)  
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");  


        //定義金鑰長度1024位  
        keyGen.initialize(1024);  


        //通過KeyPairGenerator產生金鑰,注意:這裡的key是一對鑰匙!!  
        KeyPair key = keyGen.generateKeyPair();  
 
        //獲得一個RSA的Cipher類,使用公鑰加密  
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");  
	//System.out.println("/n" + cipher.getProvider().getInfo());  
 
        System.out.println("/n用公鑰加密...");  


        //Cipher.ENCRYPT_MODE意思是加密,從一對鑰匙中得到公鑰 key.getPublic()  
        cipher.init(Cipher.ENCRYPT_MODE, key.getPublic());  


        //用公鑰進行加密,返回一個位元組流  
        byte[] cipherText = cipher.doFinal(plainText);  


        //以UTF8格式把位元組流轉化為String  
        String after1 = new String(cipherText, "UTF8");  


        System.out.println("用公鑰加密完成:"+after1);  
 
          
        //使用私鑰解密  
        System.out.println("/n用私鑰解密...");  


        //Cipher.DECRYPT_MODE意思是解密模式,從一對鑰匙中得到私鑰 key.getPrivate()  
        cipher.init(Cipher.DECRYPT_MODE, key.getPrivate());  


        //用私鑰進行解密,返回一個位元組流  
        byte[] newPlainText = cipher.doFinal(cipherText);  
 
        String after2 = new String(newPlainText, "UTF8");  
        System.out.println("用私鑰解密完成:"+after2); 

4. 數字簽名
       /** 
         * 此例子是數字簽名的例子,使用RSA私鑰對訊息摘要(這裡指的是原始資料)進行簽名,然後使用公鑰驗證簽名 
         *  
         * A通過使用B的公鑰加密資料後發給B,B利用B的私鑰解密就得到了需要的資料(進過B公鑰加密的資料只有B的私鑰能夠 
         * 解開,C沒有B的私鑰,所以C解不開,但C可以使用B的公鑰加密一份資料發給B,這樣一來,問題來了,B收到的資料到 
         * 底是A發過來的還是C發過來的呢) 
         * 由於私鑰是唯一的,那麼A就可以利用A自己的私鑰進行加密,然後B再利用A的公鑰來解密,就可以確定:一定是A的消 
         * 息,數字簽名的原理就基於此 
         *  
         * 總結:A想將目標資料傳給B,此時A需要準備1和2兩部分 
         * 1:A使用B的公鑰將原始資訊加密,以起到保密作用(只有B的私鑰能解開,其他人使用其他鑰匙都解不開,當然就保密咯) 
         * 2:A使用A的私鑰將原始資訊的摘要進行簽名,以起到接收方B確定是A發過來的作用(A用A的私鑰對目標資料的摘要進行籤 
         * 名,然後傳給B,同時,C用C的私鑰對任意資訊進行簽名也傳給B,B想接受的是A的資料(比如說一個轉帳請求),於是B 
         * 就通過A的公鑰對接受到的兩個資訊進行解密,解開的就是A(A的公鑰能且只能解開A的私鑰加密的資料)) 
         */ 
        String before = "asdf";  
        byte[] plainText = before.getBytes("UTF8");  
 
        //形成RSA公鑰對  
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");  
        keyGen.initialize(1024);  
        KeyPair key = keyGen.generateKeyPair();  
 
        //使用私鑰簽名**********************************************************  
        Signature sig = Signature.getInstance("SHA1WithRSA");  
        sig.initSign(key.getPrivate());//sig物件得到私鑰  


        //簽名物件得到原始資料  
        sig.update(plainText);//sig物件得到原始資料(現實中用的是原始資料的摘要,摘要的是單向的,即摘要演算法後無法解密)  
        byte[] signature = sig.sign();//sig物件用私鑰對原始資料進行簽名,簽名後得到簽名signature  
	//System.out.println(sig.getProvider().getInfo());  


        String after1 = new String(signature, "UTF8");  
        System.out.println("/n用私鑰簽名後:"+after1);  
 
        //使用公鑰驗證
        key = keyGen.generateKeyPair(); 
        sig.initVerify(key.getPublic());//sig物件得到公鑰 


        //簽名物件得到原始資訊 
        sig.update(plainText);//sig物件得到原始資料(現實中是摘要)  
        try {  
            if (sig.verify(signature)) {//sig物件用公鑰解密簽名signature得到原始資料(即摘要),一致則true  
                System.out.println("簽名驗證正確!!"+new String(plainText, "UTF8"));  
            } else {  
                System.out.println("簽名驗證失敗!!");  
            }  
        } catch (SignatureException e) {  
            System.out.println("簽名驗證失敗!!");  
        }  

  
5. 數字證書  
    /** 
      * 此例是對“數字證書”檔案的操作 
       * java平臺(在機器上安裝jdk)為你提供了金鑰庫(證書庫),cmd下提供了keytool命令就可以建立證書庫 
       *  
       * 在執行此例前: 
       * 在c盤目錄下建立一個證書,指定證書庫為BocsoftKeyLib,建立別名為TestCertification的一條證書,它指定用  
       * RSA 演算法生成,且指定金鑰長度為1024,證書有效期為1年 
       * 匯出證書檔案為TC.cer已存於本地磁碟C:/ 
       * 密碼是qazzaq
       */ 
      try {  
            //前提:將證書庫中的一條證書匯出到證書檔案(我寫的例子裡證書檔案叫TC.cer)  
            //從證書檔案TC.cer裡讀取證書資訊  
            CertificateFactory cf = CertificateFactory.getInstance("X.509"); 


            FileInputStream in = new FileInputStream("C:/TC.cer"); 


            //將檔案以檔案流的形式讀入證書類Certificate中 
            Certificate c = cf.generateCertificate(in); 
            System.err.println("轉換成String後的證書資訊:"+c.toString());*/ 
            
            //或者不用上面程式碼的方法,直接從證書庫中讀取證書資訊,和上面的結果一摸一樣  
            String pass="qazzaq";     
            FileInputStream in2=new FileInputStream("C:/BocsoftKeyLib");     


            KeyStore ks=KeyStore.getInstance("JKS");     
            ks.load(in2,pass.toCharArray());  
            String alias = "TestCertification"; //alias為條目的別名  
            Certificate c=ks.getCertificate(alias);  
            System.err.println("轉換成String後的證書資訊:"+c.toString());  
            
            //獲取獲取X509Certificate型別的物件,這是證書類獲取Certificate的子類,實現了更多方法  
            X509Certificate t=(X509Certificate)c;  
            //從資訊中提取需要資訊  
            System.out.println("版本號:"+t.getVersion());     
            System.out.println("序列號:"+t.getSerialNumber().toString(16));     
            System.out.println("主體名:"+t.getSubjectDN());     
            System.out.println("簽發者:"+t.getIssuerDN());     
            System.out.println("有效期:"+t.getNotBefore());     
            System.out.println("簽名演算法:"+t.getSigAlgName()); 


            byte [] sig=t.getSignature();//簽名值  
            PublicKey pk = t.getPublicKey();   
            byte [] pkenc=pk.getEncoded();     
            System.out.println("公鑰:");     
            for(int i=0;i<pkenc.length;i++){  
                System.out.print(pkenc[i]+",");     
            }  
            System.err.println();
              
            //證書的日期有效性檢查,頒發的證書都有一個有效性的日期區間  
            Date TimeNow=new Date();        
            t.checkValidity(TimeNow);     
            System.out.println("證書的日期有效性檢查:有效的證書日期!");
              
            //驗證證書籤名的有效性,通過數字證書認證中心(CA)機構頒佈給客戶的CA證書,比如:caroot.crt檔案  
            //我手裡沒有CA頒給我的證書,所以下面程式碼執行不了  
            /*FileInputStream in3=new FileInputStream("caroot.crt");    
            //獲取CA證書 
            Certificate cac = cf.generateCertificate(in3);


            //獲取CA的公鑰    
            PublicKey pbk=cac.getPublicKey();    
            //c為本地證書,也就是待檢驗的證書,用CA的公鑰校驗數字證書c的有效性 
            c.verify(pbk);              
        } catch(CertificateExpiredException e){//證書的日期有效性檢查:過期     
            System.out.println("證書的日期有效性檢查:過期");        
        } catch(CertificateNotYetValidException e){ //證書的日期有效性檢查:尚未生效     
            System.out.println("證書的日期有效性檢查:尚未生效");     
        } catch (CertificateException ce) {  
            ce.printStackTrace();  
        } catch (FileNotFoundException fe) {  
            fe.printStackTrace();  
        } /*catch (IOException ioe){ 
             
        } catch (KeyStoreException kse){ 
             
        }*/ catch (Exception e){  
            e.printStackTrace();  
        } 
    }