不過用起來實在太頓了,所以後來想說乾脆放棄 HTC Sense,找看看別的 ROM
因此這次選上了今天才剛進入 RC 的 CyanogenMod 9.0.0-RC1
圖片來源:xda-developers
Software entities (class, modules, functions, etc.) should be open for extension, but closed for modification.
- Bertrand Meyer
Junior programmers create simple solutions to simple problems. Senior programmers create complex solutions to complex problems. Great programmers find simple solutions to complex problems.
- Charles Connell
註1:本部落格的範例程式碼在 2015 年以前的文章中,大多是以全型空白做縮排。如需服用,請自行用文字編輯器的取代功能把全型空白取代成半型空白。
註2:本部落格的內容授權請參閱部落格底部的授權宣告。
正在嘗試要實作公開金鑰的加密演算法,目前搜尋了一些資料,原本有在疑惑應該選用哪個演算法
不過在搜尋到 [4] 以後,就決定還是用 RSA 了。
參考資料
1、JAVA 上加密演算法的實作範例
2、Generate Public and Private Keys
3、Save/Load Private and Public Key to/from a file
4、RSA Versus DSA and EL GAMAL
5、有關如何安全地儲存 key 的討論,可以看看:How to securely store a PrivateKey in code
這裡分成三個部分來討論,首先是要如何在 Java 裡面產生 RSA 的 Public Key 和 Private Key
以及把產生的兩把 Key 都儲存成檔案,再把它從檔案裡讀出來,最後才是真正把資料加密和解密。
生成金鑰和讀寫金鑰可參考以下的程式碼,基本的程式碼是參考 [3],只有做小幅度的修改而已。
程式碼分為以下幾個 method:
以下就分別是各個 method 的原始碼以及相關的說明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public static void main(String[] args) throws Throwable { String path = "D:/encrypt" ; SecureRandom random = new SecureRandom(); random.setSeed( "test" .getBytes()); // Generate the key pair (public key and private key). KeyPairGenerator keygen = KeyPairGenerator.getInstance( "RSA" ); // Specify that the key should be 1024-bit. keygen.initialize( 1024 , random); KeyPair generatedKeyPair = keygen.generateKeyPair(); // Print the key. The key will be encoded by Base64. log.trace( "Generated key pair:" ); printKeyPair(generatedKeyPair); // Store the key as files. log.info( "Output the key pair to {}" , path); saveKeyPair(path, generatedKeyPair); // Load the keys log.info( "Loaded Key Pair from {}" , path); KeyPair loadedKeyPair = loadKeyPair(path, "RSA" ); // Load the keys from files // Print the loaded key pair to ensure that they are exactly the same. log.trace( "Loaded key pair:" ); printKeyPair(loadedKeyPair); } |
4~5 行是用指定的字串去產生金鑰,透過這個方法的話,只要指定的字串是同一個,產生的金鑰就會是同一個。
這裡指定的字串是 "test"。
7~11 行是產生金鑰的程式碼,其中第 10 行的 1024 是指要產生的金鑰長度為 1024-bit
長度設得越長,產生金鑰所需要的時間也越長~
我的電腦產生 512 bits 的金鑰一瞬間就出來了,1024-bit 要大概一兩秒
2048-bit 就要稍微等個三秒之類的吧...(秒數是純感覺,沒有做過任何精準的測試)
11 行將 KeyPair 產生出來,也就是同時產生 Private Key 和 Public Key,會被存在同一個物件裡。
然後後面就依序是先把產生的金鑰印出來,接著把金鑰輸出成檔案
(預期輸出位置是 D:\encrypt\public.key 和 D:\encrypt\private.key)
然後再從檔案中把金鑰讀進來,再印出來,可以用眼睛確認跟輸出前是否長得一樣。
1 2 3 4 5 6 |
public static void printKeyPair(KeyPair keyPair) { log.trace( " public key: {}" , Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded())); log.trace( " private key: {}" , Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded())); } |
這裡很單純,就只是利用 Java 原生支援的 Base64 編碼器,把金鑰的內容輸出成文字印出來而已。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
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(); log.trace( "Private key will be output to '{}'." , fileForPrivateKey); PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded()); try (FileOutputStream fos = new FileOutputStream(fileForPrivateKey)) { fos.write(pkcs8EncodedKeySpec.getEncoded()); } } |
5 ~ 7 行是產生路徑,實際執行起來,這裡產生的路徑會是 D:\encrypt\public.key。
9 ~ 12 行是把 Public Key 的內容輸出到檔案 D:\encrypt\public.key。
同樣地,接著 15 ~ 16 行也是產生路徑,實際執行時這個路徑會是 D:\encrypt\private.key。
18 ~ 21 行則是把 Private Key 的內容輸出到檔案 D:\encrypt\private.key。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// 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(); log.trace( "Public key will be loaded from '{}'." , fileForPublicKey); 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); } |
這裡程式碼看起來好像比較長,但其實邏輯跟前一段 saveKeyPair() 沒什麼差別
就是組成路徑,然後讀檔案,再把讀入的 bytes 轉成金鑰的物件。因為流程上大同小異,這裡就先忽略細節了 XD
以上述的 method 全部放在同一個 Class 合併起來執行的話,會跑出類似下面這樣的 log 輸出:
1 2 3 4 5 6 7 8 9 10 11 12 |
2017 - 07 - 19 22 : 58 : 44 , 252 | Generated key pair: 2017 - 07 - 19 22 : 58 : 44 , 254 | public key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvIaeWOTOx0aBWaz+fZNwsbork4e5RW94CbWR9rOtNE2jtKmgC6hPpWz7ANxzGL7jie9D/BpziyuSmhd3aquf4rmftiNdoTp55earfArlBO2D8Z0vaGnEUetDcahn0UD1bCekRC93iLzRNN73dm1T0CynFFrvFVA/CLgS/BvWEZwIDAQAB 2017 - 07 - 19 22 : 58 : 44 , 260 | private key: MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAK8hp5Y5M7HRoFZrP59k3CxuiuTh7lFb3gJtZH2s600TaO0qaALqE+lbPsA3HMYvuOJ70P8GnOLK5KaF3dqq5/iuZ+2I12hOnnl5qt8CuUE7YPxnS9oacRR60NxqGfRQPVsJ6REL3eIvNE03vd2bVPQLKcUWu8VUD8IuBL8G9YRnAgMBAAECgYAMiyaLtfEj7VXEms3lxr2WWRyNpDkDjsbp+ZfXAImh7Z/4TK9Cdi2S6zwlXE0tTMG7Rw8DFSArhki2PKRVQyR2Jvp1m7v3ybwLSbP1GH9APKvfDZvjlNobzTPyut5W7jHFDVcAzSQujFcROKWG4gZk+CF9ywatmm3QaAIpiMgz8QJBAN3RlDj8OdzEcyoUNeYmszMWNEPJjoYMiiwrBYN7GCM5ZhK5fRF67PO38Y //PN1Ve5ViwGlKjyo9cRvIlVyRnnsCQQDKHlV71jCkeMherp0WqdtfAExd0BwzVR//CVHktTtCrutOcXOvEiZlnB92ExPzRCJDCD/gkqnU/h6UOM5tUoQFAkB95RFHNoBwuF7UpxvgQF68xAFt59uoYT2ay+AZO6f7dfxk7Dn7zdTmjqPfonGc/YNiyeWC3PpccvrbVgDPxSY5AkBF7wD8/DuQbQpHWHuaH+N7l4rU2vEnAck0YXEohVyf0g4w8iho5wrKFZ79J9S7U1PXhb80YQrKW7MQ7ibexLJRAkA5TcwKY9IEnYZcQlHI2ZZ0GZEL6TANxMXcFzr3OIk+3CHxBw0FKiG56zussDuRGdopey4KUEsCW19HupLsZamE 2017 - 07 - 19 22 : 58 : 44 , 260 | Output the key pair to D:/encrypt 2017 - 07 - 19 22 : 58 : 44 , 261 | Public key will be output to 'D:\encrypt\public.key' . 2017 - 07 - 19 22 : 58 : 44 , 262 | Private key will be output to 'D:\encrypt\private.key' . 2017 - 07 - 19 22 : 58 : 44 , 263 | Loaded Key Pair from D:/encrypt 2017 - 07 - 19 22 : 58 : 44 , 263 | Public key will be loaded from 'D:\encrypt\public.key' . 2017 - 07 - 19 22 : 58 : 44 , 264 | Private key will be loaded from 'D:\encrypt\private.key' . 2017 - 07 - 19 22 : 58 : 44 , 264 | Loaded key pair: 2017 - 07 - 19 22 : 58 : 44 , 264 | public key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvIaeWOTOx0aBWaz+fZNwsbork4e5RW94CbWR9rOtNE2jtKmgC6hPpWz7ANxzGL7jie9D/BpziyuSmhd3aquf4rmftiNdoTp55earfArlBO2D8Z0vaGnEUetDcahn0UD1bCekRC93iLzRNN73dm1T0CynFFrvFVA/CLgS/BvWEZwIDAQAB 2017 - 07 - 19 22 : 58 : 44 , 264 | private key: MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAK8hp5Y5M7HRoFZrP59k3CxuiuTh7lFb3gJtZH2s600TaO0qaALqE+lbPsA3HMYvuOJ70P8GnOLK5KaF3dqq5/iuZ+2I12hOnnl5qt8CuUE7YPxnS9oacRR60NxqGfRQPVsJ6REL3eIvNE03vd2bVPQLKcUWu8VUD8IuBL8G9YRnAgMBAAECgYAMiyaLtfEj7VXEms3lxr2WWRyNpDkDjsbp+ZfXAImh7Z/4TK9Cdi2S6zwlXE0tTMG7Rw8DFSArhki2PKRVQyR2Jvp1m7v3ybwLSbP1GH9APKvfDZvjlNobzTPyut5W7jHFDVcAzSQujFcROKWG4gZk+CF9ywatmm3QaAIpiMgz8QJBAN3RlDj8OdzEcyoUNeYmszMWNEPJjoYMiiwrBYN7GCM5ZhK5fRF67PO38Y //PN1Ve5ViwGlKjyo9cRvIlVyRnnsCQQDKHlV71jCkeMherp0WqdtfAExd0BwzVR//CVHktTtCrutOcXOvEiZlnB92ExPzRCJDCD/gkqnU/h6UOM5tUoQFAkB95RFHNoBwuF7UpxvgQF68xAFt59uoYT2ay+AZO6f7dfxk7Dn7zdTmjqPfonGc/YNiyeWC3PpccvrbVgDPxSY5AkBF7wD8/DuQbQpHWHuaH+N7l4rU2vEnAck0YXEohVyf0g4w8iho5wrKFZ79J9S7U1PXhb80YQrKW7MQ7ibexLJRAkA5TcwKY9IEnYZcQlHI2ZZ0GZEL6TANxMXcFzr3OIk+3CHxBw0FKiG56zussDuRGdopey4KUEsCW19HupLsZamE |