2011-09-08 164 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