2011-09-08 196 views
-1

無論我生成多少次,我都希望生成的RSA對於相同的公鑰和私鑰是不變的。生成常量RSA密鑰(Java)

我有一個java源代碼,其中的密鑰(公鑰和私鑰)每次生成時都會更改。

我該如何修改代碼,使其每次生成時都會保持不變?

import java.security.*; 
import java.security.spec.*; 
import javax.crypto.*; 
import javax.crypto.spec.*; 
import java.io.*; 
import java.util.*; 

/** 
* This class encrypts and decrypts a file using CipherStreams 
* and a 256-bit Rijndael key. The key is then encrypted using 
* a 1024-bit RSA key, which is password-encrypted. 
*/ 
public class FileEncryptorRSA { 
/** 
* When files are encrypted, this will be appended to the end 
* of the filename. 
*/ 
private static final String ENCRYPTED_FILENAME_SUFFIX=".encrypted"; 

    /** 
* When files are decrypted, this will be appended to the end 
* of the filename. 
*/ 
private static final String DECRYPTED_FILENAME_SUFFIX=".decrypted"; 

/** 
* Number of times the password will be hashed with MD5 
* when transforming it into a TripleDES key. 
*/ 
private static final int ITERATIONS = 1000; 

/** 
* FileEncryptor is started with one of three options: 
* 
* -c: create key pair and write it to 2 files 
* -e: encrypt a file, given as an argument 
* -d: decrypt a file, given as an argument 
*/ 
public static void main (String[] args) 
throws Exception { 
if ((args.length < 1) || (args.length > 2)) { 
    usage(); 
} else if ("-c".equals(args[0])) { 
    createKey(); 
} else if ("-e".equals(args[0])) { 
    encrypt(args[1]); 
} else if ("-d".equals(args[0])) { 
    decrypt(args[1]); 
} else { 
    usage(); 
} 
} 

private static void usage() { 
System.err.println("Usage: java FileEncryptor -c|-e|-d [filename]"); 
System.exit(1); 
} 

/** 
* Creates a 1024 bit RSA key and stores it to 
* the filesystem as two files. 
*/ 
private static void createKey() 
throws Exception { 
BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); 
System.out.print("Password to encrypt the private key: "); 
String password = in.readLine(); 
System.out.println("Generating an RSA keypair..."); 

// Create an RSA key 
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); 
keyPairGenerator.initialize(1024); 
KeyPair keyPair = keyPairGenerator.genKeyPair(); 

System.out.println("Done generating the keypair.\n"); 

// Now we need to write the public key out to a file 
System.out.print("Public key filename: "); 
String publicKeyFilename = in.readLine(); 

// Get the encoded form of the public key so we can 
// use it again in the future. This is X.509 by default. 
byte[] publicKeyBytes = keyPair.getPublic().getEncoded(); 

// Write the encoded public key out to the filesystem 
FileOutputStream fos = new FileOutputStream(publicKeyFilename); 
fos.write(publicKeyBytes); 
fos.close(); 

// Now we need to do the same thing with the private key, 
// but we need to password encrypt it as well. 
System.out.print("Private key filename: "); 
String privateKeyFilename = in.readLine(); 

// Get the encoded form. This is PKCS#8 by default. 
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded(); 

// Here we actually encrypt the private key 
byte[] encryptedPrivateKeyBytes = 
passwordEncrypt(password.toCharArray(),privateKeyBytes); 

fos = new FileOutputStream(privateKeyFilename); 
fos.write(encryptedPrivateKeyBytes); 
fos.close(); 
} 

/** 
* Encrypt the given file with a session key encrypted with an 
* RSA public key which will be read in from the filesystem. 
*/ 
private static void encrypt(String fileInput) 
throws Exception { 

BufferedReader in = new BufferedReader 
(new InputStreamReader(System.in)); 
System.out.print("Public Key to encrypt with: "); 
String publicKeyFilename = in.readLine(); 

// Load the public key bytes 
FileInputStream fis = new FileInputStream(publicKeyFilename); 
ByteArrayOutputStream baos = new ByteArrayOutputStream(); 

int theByte = 0; 
while ((theByte = fis.read()) != -1) 
{ 
    baos.write(theByte); 
} 
fis.close(); 

byte[] keyBytes = baos.toByteArray(); 
baos.close(); 

// Turn the encoded key into a real RSA public key. 
// Public keys are encoded in X.509. 
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); 
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 
PublicKey publicKey = keyFactory.generatePublic(keySpec); 

// Open up an output file for the output of the encryption 
String fileOutput = fileInput + ENCRYPTED_FILENAME_SUFFIX; 
DataOutputStream output = new DataOutputStream 
(new FileOutputStream(fileOutput)); 

// Create a cipher using that key to initialize it 
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 
rsaCipher.init(Cipher.ENCRYPT_MODE, publicKey); 

// Now create a new 256 bit Rijndael key to encrypt the file itself. 
// This will be the session key. 
KeyGenerator rijndaelKeyGenerator = KeyGenerator.getInstance("Rijndael"); 
rijndaelKeyGenerator.init(256); 
System.out.println("Generating session key..."); 
Key rijndaelKey = rijndaelKeyGenerator.generateKey(); 
System.out.println("Done generating key."); 

// Encrypt the Rijndael key with the RSA cipher 
// and write it to the beginning of the file. 
byte[] encodedKeyBytes= rsaCipher.doFinal(rijndaelKey.getEncoded()); 
output.writeInt(encodedKeyBytes.length); 
output.write(encodedKeyBytes); 

// Now we need an Initialization Vector for the symmetric cipher in CBC mode 
SecureRandom random = new SecureRandom(); 
byte[] iv = new byte[16]; 
random.nextBytes(iv); 

// Write the IV out to the file. 
output.write(iv); 
IvParameterSpec spec = new IvParameterSpec(iv); 

// Create the cipher for encrypting the file itself. 
Cipher symmetricCipher = Cipher.getInstance("Rijndael/CBC/PKCS5Padding"); 
symmetricCipher.init(Cipher.ENCRYPT_MODE, rijndaelKey, spec); 

CipherOutputStream cos = new CipherOutputStream(output, symmetricCipher); 

System.out.println("Encrypting the file..."); 

FileInputStream input = new FileInputStream(fileInput); 

theByte = 0; 
while ((theByte = input.read()) != -1) 
{ 
    cos.write(theByte); 
} 
input.close(); 
cos.close(); 
System.out.println("File encrypted."); 
return; 
} 

/** 
* Decrypt the given file. 
* Start by getting the RSA private key 
* and decrypting the session key embedded 
* in the file. Then decrypt the file with 
* that session key. 
*/ 
private static void decrypt(String fileInput) 
throws Exception { 

BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); 
System.out.print("Private Key to decrypt with: "); 
String privateKeyFilename = in.readLine(); 

System.out.print("Password for the private key: "); 
String password = in.readLine(); 

// Load the private key bytes 
FileInputStream fis = new FileInputStream(privateKeyFilename); 
ByteArrayOutputStream baos = new ByteArrayOutputStream(); 

int theByte = 0; 
while ((theByte = fis.read()) != -1) 
{ 
    baos.write(theByte); 
} 
fis.close(); 

byte[] keyBytes = baos.toByteArray(); 
baos.close(); 

keyBytes = passwordDecrypt(password.toCharArray(), keyBytes); 

// Turn the encoded key into a real RSA private key. 
// Private keys are encoded in PKCS#8. 
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); 
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 
PrivateKey privateKey = keyFactory.generatePrivate(keySpec); 

// Create a cipher using that key to initialize it 
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 

// Read in the encrypted bytes of the session key 
DataInputStream dis = new DataInputStream(new FileInputStream(fileInput)); 
byte[] encryptedKeyBytes = new byte[dis.readInt()]; 
dis.readFully(encryptedKeyBytes); 

// Decrypt the session key bytes. 
rsaCipher.init(Cipher.DECRYPT_MODE, privateKey); 
byte[] rijndaelKeyBytes = rsaCipher.doFinal(encryptedKeyBytes); 

// Transform the key bytes into an actual key. 
SecretKey rijndaelKey = new SecretKeySpec(rijndaelKeyBytes, "Rijndael"); 

// Read in the Initialization Vector from the file. 
byte[] iv = new byte[16]; 
dis.read(iv); 
IvParameterSpec spec = new IvParameterSpec(iv); 

Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS5Padding"); 
cipher.init(Cipher.DECRYPT_MODE, rijndaelKey, spec); 
CipherInputStream cis = new CipherInputStream(dis, cipher); 

System.out.println("Decrypting the file..."); 
FileOutputStream fos = new FileOutputStream(fileInput + DECRYPTED_FILENAME_SUFFIX); 

// Read through the file, decrypting each byte. 
theByte = 0; 
while ((theByte = cis.read()) != -1) 
{ 
    fos.write(theByte); 
} 
cis.close(); 
fos.close(); 
System.out.println("Done."); 
return; 
} 

    /** 
    * Utility method to encrypt a byte array with a given password. 
    * Salt will be the first 8 bytes of the byte array returned. 
    */ 
private static byte[] passwordEncrypt(char[] password, byte[] plaintext) throw Exception  { 

// Create the salt. 
byte[] salt = new byte[8]; 
Random random = new Random(); 
random.nextBytes(salt); 

// Create a PBE key and cipher. 
PBEKeySpec keySpec = new PBEKeySpec(password); 
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithSHAAndTwofish-CBC"); 
SecretKey key = keyFactory.generateSecret(keySpec); 
PBEParameterSpec paramSpec = new PBEParameterSpec(salt, ITERATIONS); 
Cipher cipher = Cipher.getInstance("PBEWithSHAAndTwofish-CBC"); 
cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec); 

// Encrypt the array 
byte[] ciphertext = cipher.doFinal(plaintext); 

// Write out the salt, then the ciphertext and return it. 
ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
baos.write(salt); 
baos.write(ciphertext); 
return baos.toByteArray(); 
} 

/** *使用給定密碼解密字節數組的實用方法。 *鹽將傳入的數組中的前8個字節。 */ 私有靜態字節[] passwordDecrypt(燒焦[]密碼,字節[]密文)拋出異常{

// Read in the salt. 
byte[] salt = new byte[8]; 
ByteArrayInputStream bais = new ByteArrayInputStream(ciphertext); 
bais.read(salt,0,8); 

// The remaining bytes are the actual ciphertext. 
byte[] remainingCiphertext = new byte[ciphertext.length-8]; 
bais.read(remainingCiphertext,0,ciphertext.length-8); 

// Create a PBE cipher to decrypt the byte array. 
PBEKeySpec keySpec = new PBEKeySpec(password); 
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithSHAAndTwofish-CBC"); 
SecretKey key = keyFactory.generateSecret(keySpec); 
PBEParameterSpec paramSpec = new PBEParameterSpec(salt, ITERATIONS); 
Cipher cipher = Cipher.getInstance("PBEWithSHAAndTwofish-CBC"); 

// Perform the actual decryption. 
cipher.init(Cipher.DECRYPT_MODE, key, paramSpec); 
return cipher.doFinal(remainingCiphertext); 
    } 
} 

該文件可以是審查@http://sce.uhcl.edu/yang/teaching/proJavaSecurityCode/Chapter5/FileEncryptorRSA.java

我將不勝感激全面的指導解決方案。非常感謝!

編輯: 任何一種靈魂可以提供我的代碼下載?? 生成器必須每次使用會話密鑰輸入生成相同的密鑰

+0

我明白這是一個不合理的要求,但我正在與時間完成。我希望有一種靈魂可以幫助! – Ezylryb

+0

如何生成一次,複製並將代碼複製到您的代碼中?根據請求只需轉儲該常量。 –

+0

這不允許我的代碼工作:-(對不起 – Ezylryb

回答

2

當您使用-c命令行參數調用您的發佈代碼時,它只會生成一個新的密鑰對。否則它使用通過-c存儲的生成的公鑰/私鑰對來加密和解密。如果只使用-e和-d,則不需要重新生成密鑰對。

密鑰對的生成不是微不足道的。背後的要點是私鑰應該是祕密的。如果您更改代碼以始終生成相同的密鑰,則會破壞加密方案提供的任何安全性。

也許如果你解釋了爲什麼你需要它以這種方式行事,我們可以提供更多的幫助。

+0

我知道這是一個安全漏洞,但我需要它的代碼它的工作方式。謝謝你的歡呼! – Ezylryb

1

一個機會的答案,但看看我的計劃將是什麼,以消除它的隨機性。

// Create an RSA key 
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); 
keyPairGenerator.initialize(1024); 
KeyPair keyPair = keyPairGenerator.genKeyPair(); 

如果你看的javadoc:http://download.oracle.com/javase/6/docs/api/java/security/KeyPairGenerator.html

我想你想通過改變SecureRandom的FUB隨機性改變

隨機性所產生的。 initialize(AlgorithmParameterSpec params,SecureRandom random) - 使用給定的參數集和隨機源初始化密鑰對生成器。

也許實現你自己的不是隨機的?但我同意由mcfinnigan的帖子想知道爲什麼你會這麼做...

+0

因爲我需要融入我的程序,需要運行無鹽開始 – Ezylryb

+0

如果你想使用JCE'KeyPairGenerator'類,你的最後一句是它的工作的唯一方法 –

0

首先,你爲什麼要這樣做?在生成密鑰對時,除了密碼短語之外,通常還需要使用salt,並且salt應該是不可預知的,這樣它可以提供額外的防範暴力攻擊的保護。

我覺得Ragshi是正確的,你需要提供一個SecureRandom實施keyPairGenerator.initialize(),但你可能需要設置一個實例的種子從SecureRandom.getInstance(),而不是實現自己的非隨機算法。

對不起,我最初誤讀了代碼,認爲相關部分是使用Random類生成鹽的方法passwordEncrypt。我無法刪除我的帖子,所以我試圖改正它。

+0

它是正確的任何解決方案的代碼運行時沒有鹽漬? – Ezylryb

+0

你的解決方案將無法正常工作。自己的SecureRandom實例 –

+0

你能說爲什麼它不起作用嗎?我相信你是對的,但我想更好地理解。 – Ben