Java – 使用现有公钥文件加密字符串

我在过去的4-5个小时里一直在研究这个问题,尽管找到的答案使用了从几种方法到整个~100系列的所有内容,但似乎无法找到真正有效的答案。 我无法想象没有一些简单的function可以做这么微不足道的事情:P

我有一套预先存在的公钥/私钥(实际上是两套 – 一个是由ssh-keygen生成的,另一个是由openssl生成的……所以无论什么格式都很酷)。

我所追求的只是一个简单的java,相当于我在python中写的东西 –

key_object = someModule.KeyObject(nameOfPublicKeyFile) def encrypt (SomePlainText) : return someOtherModule.encrypt(key_object, SomePlainText) 

任何帮助都是极好的!

shell中的这些openssl命令创建一个RSA密钥对,并将公钥和私钥写入DER格式的文件。

这里,私钥文件没有密码保护(-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 

现在您已拥有DER文件,您可以使用Java读取它们并使用KeySpecKeyFactory来创建PublicKeyPrivateKey对象。

 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); } 

使用公钥和私钥,您可以加密和解密少量数据(符合您的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); } 

在这里,它与简单的加密和解密绑定在一起:

 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(); } } 

我想分享一段代码..实际上,如果你根据自己的需要定制它,可以做你需要的全class。 我在我的一个应用程序中使用过这个,我用它来生成公钥/私钥来加密/解密文件。 同样也可以应用于字符串。

 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); } } 

编辑:

您需要将JVM的Java策略更改为Java Cryptography Extension(JCE)Unlimited Strength Jurisdiction以使用此代码。 有关JAVA政策变更的所有相关信息可在此处找到

这是一个很好的例子:

还有更多。 (谷歌的“java加密RSA示例”,如果此链接中断。)


我似乎无法找到真正有效的答案

试试上面链接的那个。 如果它不起作用,请跟进编辑或评论,说明出了什么问题。

我无法想象没有一些简单的function来做这么微不足道的事情。 :P

对不起,但你的想象力必须打破:-)

事实上,这不是一件小事。 由于Java试图使用单个统一API来支持各种加密function和加密技术堆栈,因此更加困难。