Java - 使用现有的公钥文件加密字符串
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24338108/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java - Encrypt String with existing public key file
提问by user2877564
I've been researching this for the past 4-5 hours now and can't seem to find an answer that actually worksdespite finding 'answers' that used everything from a few methods to an entire ~100 line class. I can't imagine that there isn't some simple function to do such a trivial thing :P
在过去的 4-5 个小时里,我一直在研究这个问题,尽管找到了使用从几种方法到整个约 100 行类的所有方法的“答案”,但似乎找不到真正有效的答案。我无法想象没有一些简单的功能可以做这样一件微不足道的事情:P
I have a pre-existing set of public / private keys (actually, two sets - one generated by ssh-keygen and another by openssl so .. whatever format works is cool).
我有一组预先存在的公钥/私钥(实际上,两组 - 一组由 ssh-keygen 生成,另一组由 openssl 生成,所以......无论哪种格式都很好)。
All I am after is a simple java equivalent to what I write in python like -
我所追求的只是一个简单的 java,相当于我在 python 中编写的内容,例如 -
key_object = someModule.KeyObject(nameOfPublicKeyFile)
def encrypt (SomePlainText) :
return someOtherModule.encrypt(key_object, SomePlainText)
Any help would be awesome!
任何帮助都是极好的!
采纳答案by Jim Flood
These openssl commands in the shell create an RSA key pair and write the public and private keys to DERformatted files.
shell 中的这些 openssl 命令会创建一个 RSA 密钥对,并将公钥和私钥写入DER格式的文件。
Here, the private key file is not password-protected (-nocrypt) to keep things simple.
在这里,私钥文件不受密码保护 (-nocrypt) 以保持简单。
$ openssl genrsa -out keypair.pem 2048
Generating RSA private key, 2048 bit long modulus
............+++
................................+++
e is 65537 (0x10001)
$ openssl rsa -in keypair.pem -outform DER -pubout -out public.der
writing RSA key
$ openssl pkcs8 -topk8 -nocrypt -in keypair.pem -outform DER -out private.der
Now that you have the DER files, you can read them in Java and use KeySpecand KeyFactoryto create PublicKeyand PrivateKeyobjects.
现在您有了 DER 文件,您可以用 Java 读取它们并使用KeySpec和KeyFactory创建PublicKey和PrivateKey对象。
public byte[] readFileBytes(String filename) throws IOException
{
Path path = Paths.get(filename);
return Files.readAllBytes(path);
}
public PublicKey readPublicKey(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException
{
X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(readFileBytes(filename));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(publicSpec);
}
public PrivateKey readPrivateKey(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException
{
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(readFileBytes(filename));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}
With the public and private keys, you can encrypt and decrypt small amounts of data (that fit within your RSA modulus.) I recommend OAEPpadding.
使用公钥和私钥,您可以加密和解密少量数据(适合您的 RSA 模数)。我建议使用OAEP填充。
public byte[] encrypt(PublicKey key, byte[] plaintext) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(plaintext);
}
public byte[] decrypt(PrivateKey key, byte[] ciphertext) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(ciphertext);
}
Here it is tied together with a simple encryption and decryption:
这里是通过简单的加密和解密绑定在一起的:
public void Hello()
{
try
{
PublicKey publicKey = readPublicKey("public.der");
PrivateKey privateKey = readPrivateKey("private.der");
byte[] message = "Hello World".getBytes("UTF8");
byte[] secret = encrypt(publicKey, message);
byte[] recovered_message = decrypt(privateKey, secret);
System.out.println(new String(recovered_message, "UTF8"));
}
catch (Exception e)
{
e.printStackTrace();
}
}
回答by Sharad Tank
I would like to share a piece of code.. well actually a whole class which can do what you require if you customize it to your own needs. I have used this in one of my application where I used to encrypt/decrypt a file with the generated public/private keys. Same can be applied to Strings as well.
我想分享一段代码..实际上是一个完整的类,如果您根据自己的需要对其进行自定义,它可以满足您的要求。我在我的一个应用程序中使用了它,我曾经在其中使用生成的公钥/私钥加密/解密文件。同样可以应用于字符串。
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) throws 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();
}
/**
* Utility method to decrypt a byte array with a given password.
* Salt will be the first 8 bytes in the array passed in.
*/
private static byte[] passwordDecrypt(char[] password, byte[] ciphertext) throws Exception {
// 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);
}
}
EDIT:
编辑:
You will require to change Java policy of you JVM to Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction for using this code. All related information regarding change in JAVA Policy can be found HERE
您需要将 JVM 的 Java 策略更改为 Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction 才能使用此代码。可以在此处找到有关 JAVA 政策变更的所有相关信息
回答by Stephen C
Here is a good example:
这是一个很好的例子:
and there are many more. (Google for "java encrypt RSA example" if this link breaks.)
还有更多。(如果此链接中断,请搜索“java encrypt RSA example”。)
I can't seem to find an answer that actually works
我似乎找不到真正有效的答案
Try the one linked above. If it doesn't work, please follow up with an edit or comment to say what is going wrong.
试试上面链接的那个。如果它不起作用,请跟进编辑或评论以说明发生了什么问题。
I can't imagine that there isn't some simple function to do such a trivial thing. :P
我无法想象没有一些简单的功能可以做这样一件微不足道的事情。:P
Well sorry, but your imagination must be broken :-)
对不起,但你的想象力一定被打破了:-)
In fact, it is not a trivial thing. And it is made more difficult by the fact that Java is trying to support a wide range of crypto functionality and crypto technology stacks using a single unified API.
事实上,这不是一件小事。由于 Java 试图使用单个统一 API 来支持广泛的加密功能和加密技术堆栈,因此变得更加困难。