Java中的RSA加密

1.簡介

RSA,或者換句話說, Rivest–Shamir–Adleman是一種非對稱密碼算法。它具有兩個密鑰,與諸如DES或AES之類的對稱算法不同。我們可以與任何人共享的公共密鑰用於加密數據。還有一個我們只為自己保留的私有數據,用於解密數據

在本教程中,我們將學習如何在Java中生成,存儲和使用RSA密鑰。

2.生成RSA密鑰對

在開始實際的加密之前,我們需要生成我們的RSA密鑰對。 java.security包中KeyPairGenerator輕鬆地做到這一點:

KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");

 generator.initialize(2048);

 KeyPair pair = generator.generateKeyPair();

生成的密鑰的大小為2048位。

接下來,我們可以提取私鑰和公鑰:

PrivateKey privateKey = pair.getPrivate();

 PublicKey publicKey = pair.getPublic();

我們將使用公共密鑰對數據進行加密,並使用私有密鑰對數據進行解密。

3.在文件中存儲密鑰

將密鑰對存儲在內存中並非總是一個好的選擇。通常,按鍵會長時間保持不變。在這種情況下,將它們存儲在文件中更為方便。

要將密鑰保存在文件中,我們可以使用getEncoded方法,該方法以其主要編碼格式返回密鑰內容:

try (FileOutputStream fos = new FileOutputStream("public.key")) {

 fos.write(publicKey.getEncoded());

 }

要從文件中讀取密鑰,我們首先需要將內容加載為字節數組:

File publicKeyFile = new File("public.key");

 byte[] publicKeyBytes = Files.readAllBytes(publicKeyFile.toPath());

然後使用KeyFactory重新創建實際實例:

KeyFactory keyFactory = KeyFactory.getInstance("RSA");

 EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);

 keyFactory.generatePublic(publicKeySpec);

密鑰字節內容需要用EncodedKeySpec類包裝。在這裡,我們使用X509EncodedKeySpec,它表示用於保存文件的Key::getEncoded方法的默認算法。

在此示例中,我們保存並僅讀取公共密鑰文件。可以使用相同的步驟來處理私鑰。

請記住,使用私鑰將文件保持盡可能安全,並限制訪問。未經授權的訪問可能會帶來安全問題。

4.使用字符串

現在,讓我們看一下如何加密和解密簡單字符串。首先,我們需要一些數據才能使用:

String secretMessage = "Baeldung secret message";

其次,我們需要Cipher對象,以使用之前生成的公鑰進行加密:

Cipher encryptCipher = Cipher.getInstance("RSA");

 encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);

準備好之後,我們可以調用doFinal方法來加密消息。請注意,它僅接受字節數組參數,因此我們需要在轉換字符串之前:

byte[] secretMessageBytes = secretMessage.getBytes(StandardCharsets.UTF_8);)

 byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);

現在,我們的消息已成功編碼。如果我們要將其存儲在數據庫中或通過REST API發送,則使用Base64 Alphabet對其進行編碼會更方便:

String encodedMessage = Base64.getEncoder().encodeToString(encryptedMessageBytes);

這樣,該消息將更具可讀性並且更易於使用。

現在,讓我們看看如何將消息解密為原始形式。為此,我們需要另一個Cipher實例。這次,我們將使用解密模式和私鑰對其進行初始化:

Cipher decryptCipher = Cipher.getInstance("RSA");

 decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);

doFinal方法像以前一樣調用密碼:

byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes);

 String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8);

最後,讓我們驗證加密解密過程是否正確進行:

assertEquals(secretMessage, decryptedMessage);

5.處理文件

也可以加密整個文件。例如,讓我們創建一個包含一些文本內容的臨時文件:

Path tempFile = Files.createTempFile("temp", "txt");

 Files.writeString(tempFile, "some secret message");

在開始加密之前,我們需要將其內容轉換為字節數組:

byte[] fileBytes = Files.readAllBytes(tempFile);

現在,我們可以使用加密密碼:

Cipher encryptCipher = Cipher.getInstance("RSA");

 encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);

 byte[] encryptedFileBytes = encryptCipher.doFinal(fileBytes);

最後,我們可以用新的加密內容覆蓋它:

try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) {

 stream.write(encryptedFileBytes);

 }

解密過程看起來非常相似。唯一的區別是在解密模式下使用私鑰初始化的密碼:

byte[] encryptedFileBytes = Files.readAllBytes(tempFile);

 Cipher decryptCipher = Cipher.getInstance("RSA");

 decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);

 byte[] decryptedFileBytes = decryptCipher.doFinal(encryptedFileBytes);

 try (FileOutputStream stream = new FileOutputStream(tempFile.toFile())) {

 stream.write(decryptedFileBytes);

 }

作為最後一步,我們可以驗證文件內容是否與原始值匹配:

String fileContent = Files.readString(tempFile);

 Assertions.assertEquals("some secret message", fileContent);

6.總結

在本文中,我們學習瞭如何在Java中創建RSA密鑰以及如何使用它們來加密和解密消息和文件。