2019年3月15日 星期五

[JAVA] 實作:用公鑰將字串加密後存入檔案,再從檔案讀取並用私鑰解密 (part 1/2) -- RSA 非對稱式加密

最近因工作原因,需要寫一些java程式碼,
但遇到了一個問題,
駑鈍的我花了快一個星期才從別人的程式碼中搞出自己想要的部份,

程式的目的是要讓我在程式碼中調用存放的密碼字串時,
不要讓密碼以明碼方式存放在程式或是設定檔中,

那參考了別人寫的程式碼後,終於弄出自己要的程式碼,

程式碼部份分了兩部份,第一部份(此篇)是用來製作加密密碼的文字檔,並產出公私鑰檔案,
另一部份則是用來讀取加密密碼的文字檔及私鑰來解出密碼字串(下篇文章貼上)。



以下附上程式碼供參考。







package PassWordEncrypt;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;

import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import javax.crypto.Cipher;


public class PassWordEncrypt {

 public static void main(String[] args) throws Exception {
  
   // 資料來源參考:https://jimwayne.blogspot.com/2012/06/java.html

   // 檔案存放路徑 (公私鑰及加密後密碼檔)
   String path = "src\\";
   
   SecureRandom random = new SecureRandom();
   random.setSeed("test".getBytes());
   
   // 產生金鑰組    Generate the key pair (public key and private key).
   KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
   // 指定金鑰長度2048-bit   Specify that the key should be 2048-bit.
   keygen.initialize(2048,random);  //若拿掉random就不會每執行一次就變一次金鑰
   KeyPair generatedKeyPair = keygen.generateKeyPair();
   
   // 印出金鑰,以base64編碼  Print the key. The key will be encoded by Base64.
   printKeyPair(generatedKeyPair);
   
   // 儲存金鑰  Store the key as files.
   saveKeyPair(path, generatedKeyPair);
   
   // 讀取金鑰  Load the keys
   KeyPair loadedKeyPair = loadKeyPair(path, "RSA"); // Load the keys from files
   
   // 印出金鑰以確認金鑰相同  Print the loaded key pair to ensure that they are exactly the same.
   printKeyPair(loadedKeyPair);
    
    
   // 字串使用加密金鑰並存檔,再使用解密金鑰讀取存檔
   String password = "123!@#";     //注意! 設定密碼字串,請依需求修改此處
   byte [] passwordB = password.getBytes();
    
   System.out.println("密碼加密前 = " + new String (passwordB) + "\n"  );
   byte [] passwordEncrypt = encrypt (passwordB,generatedKeyPair.getPublic() );
    
   System.out.println("密碼加密後 = " + new String (passwordEncrypt) );
    
   File passwordFile = new File (path + "password.txt");
   try (FileOutputStream foss = new FileOutputStream(passwordFile)) {
    foss.write(passwordEncrypt);
       foss.close();
   }
    
   FileInputStream fileInputStream = new FileInputStream( path + "password.txt" );
   DataInputStream  dis = new DataInputStream(fileInputStream);
   // count the available bytes form the input stream
   int count = dis.available();
   // create buffer
   byte[] bs = new byte[count];
   // read data into buffer
   dis.read(bs);
   dis.close();
       
   byte [] passwordDecrypt= decrypt (bs, loadedKeyPair.getPrivate() );
   String ReadPasswordFile = new String (passwordDecrypt);
   System.out.println("\n" + "密碼從檔案讀取後" + "\n" + "解密取得之字串= " +  ReadPasswordFile );
    
    
 
 
 }
 
 //公鑰加密
 public static byte[] encrypt(byte[] content, PublicKey publicKey) throws Exception{
 Cipher cipher=Cipher.getInstance("RSA");//java預設"RSA"="RSA/ECB/PKCS1Padding"
 cipher.init(Cipher.ENCRYPT_MODE, publicKey);
 return cipher.doFinal(content);
 }
 
 //私鑰解密
 public static byte[] decrypt(byte[] content, PrivateKey privateKey) throws Exception{
 Cipher cipher=Cipher.getInstance("RSA");
 cipher.init(Cipher.DECRYPT_MODE, privateKey);
 return cipher.doFinal(content);
 }
 
 
 
 public static void printKeyPair(KeyPair keyPair) {

        Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
        System.out.println ("public key:  " + Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()));

        Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
        System.out.println ("private key: " + Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()) );
  }
 
 public static void saveKeyPair(String path, KeyPair keyPair) throws IOException {
    PrivateKey privateKey = keyPair.getPrivate();
    PublicKey publicKey = keyPair.getPublic();
   
    // Store Public Key.
    File fileForPublicKey = Paths.get(path, "public.key").toFile();
    //log.trace("Public key will be output to '{}'.", fileForPublicKey);
     
    X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
    try (FileOutputStream fos = new FileOutputStream(fileForPublicKey)) {
      fos.write(x509EncodedKeySpec.getEncoded());
    }
   
    // Store Private Key.
    File fileForPrivateKey = Paths.get(path, "private.key").toFile();
        
    PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
    try (FileOutputStream fos = new FileOutputStream(fileForPrivateKey)) {
      fos.write(pkcs8EncodedKeySpec.getEncoded());
    }
 }
 
 // Load the keys from files.
 public static KeyPair loadKeyPair(String path, String algorithm)
     throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
  
  // Initiate the factory with specified algorithm.
  KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
    
  // Read public key from file.
  File fileForPublicKey = Paths.get(path, "public.key").toFile();
       
  PublicKey publicKey = null;
  try (FileInputStream fis = new FileInputStream(fileForPublicKey)) {
   byte[] loadedBytes = new byte[(int) fileForPublicKey.length()];
   fis.read(loadedBytes);
     
   X509EncodedKeySpec spec = new X509EncodedKeySpec(loadedBytes);
   publicKey = keyFactory.generatePublic(spec);
  }
    
  // Read private key from file.
  File fileForPrivateKey = Paths.get(path, "private.key").toFile();
  //log.trace("Private key will be loaded from '{}'.", fileForPrivateKey);
    
  PrivateKey privateKey = null;
  try (FileInputStream fis = new FileInputStream(fileForPrivateKey)) {
   byte[] loadedBytes = new byte[(int) fileForPrivateKey.length()];
   fis.read(loadedBytes);
      
   PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(loadedBytes);
   privateKey = keyFactory.generatePrivate(privateKeySpec);
  }
  
   return new KeyPair(publicKey, privateKey);
 }
 
 
 
}


沒有留言: