2010-10-15 90 views
31

我需要使用bouncycastle提供程序对pgp加密流。我可以找到的所有示例都是关于获取纯文本文件并对其进行加密,但是我不会有文件,重要的是纯文本永远不会写入磁盘。如何使用bouncycastle pgp加密字符串/流而不用文件开始

大多数的方法我已经使用

PGPUtil.writeFileToLiteralData其想要传递的明文看到。我宁愿经过的一个byte []或一个InputStream。

有人点我一个例子,从字符串/字节

  • 开始[]/InputStream的
  • 加密所述字符串/字节[]到outputStrem,我可以写一个文件
  • 从InputStream

在在这种情况下,任何人绊倒解密并希望完整的解决方案

package com.common.security.pgp; 

import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.security.NoSuchProviderException; 
import java.security.SecureRandom; 
import java.security.Security; 
import java.util.Date; 
import java.util.Iterator; 

import org.bouncycastle.bcpg.ArmoredOutputStream; 
import org.bouncycastle.jce.provider.BouncyCastleProvider; 
import org.bouncycastle.openpgp.PGPCompressedData; 
import org.bouncycastle.openpgp.PGPCompressedDataGenerator; 
import org.bouncycastle.openpgp.PGPEncryptedData; 
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator; 
import org.bouncycastle.openpgp.PGPEncryptedDataList; 
import org.bouncycastle.openpgp.PGPException; 
import org.bouncycastle.openpgp.PGPLiteralData; 
import org.bouncycastle.openpgp.PGPLiteralDataGenerator; 
import org.bouncycastle.openpgp.PGPObjectFactory; 
import org.bouncycastle.openpgp.PGPPrivateKey; 
import org.bouncycastle.openpgp.PGPPublicKey; 
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData; 
import org.bouncycastle.openpgp.PGPPublicKeyRing; 
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; 
import org.bouncycastle.openpgp.PGPSecretKey; 
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; 
import org.bouncycastle.openpgp.PGPUtil; 

/** 
* Simple routine to encrypt and decrypt using a Public and Private key with passphrase. This service 
* routine provides the basic PGP services between byte arrays. 
* 
*/ 
public class PgpEncryption { 


    private static PGPPrivateKey findSecretKey(
      PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass) 
      throws PGPException, NoSuchProviderException { 
     PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID); 

     if (pgpSecKey == null) { 
      return null; 
     } 

     return pgpSecKey.extractPrivateKey(pass, "BC"); 
    } 

    /** 
    * decrypt the passed in message stream 
    * 
    * @param encrypted 
    *   The message to be decrypted. 
    * @param passPhrase 
    *   Pass phrase (key) 
    * 
    * @return Clear text as a byte array. I18N considerations are not handled 
    *   by this routine 
    * @exception IOException 
    * @exception PGPException 
    * @exception NoSuchProviderException 
    */ 
    public static byte[] decrypt(byte[] encrypted, InputStream keyIn, char[] password) 
      throws IOException, PGPException, NoSuchProviderException { 
     InputStream in = new ByteArrayInputStream(encrypted); 

     in = PGPUtil.getDecoderStream(in); 

     PGPObjectFactory pgpF = new PGPObjectFactory(in); 
     PGPEncryptedDataList enc = null; 
     Object o = pgpF.nextObject(); 

     // 
     // the first object might be a PGP marker packet. 
     // 
     if (o instanceof PGPEncryptedDataList) { 
      enc = (PGPEncryptedDataList) o; 
     } else { 
      enc = (PGPEncryptedDataList) pgpF.nextObject(); 
     } 



     // 
     // find the secret key 
     // 
     Iterator it = enc.getEncryptedDataObjects(); 
     PGPPrivateKey sKey = null; 
     PGPPublicKeyEncryptedData pbe = null; 
     PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
       PGPUtil.getDecoderStream(keyIn)); 

     while (sKey == null && it.hasNext()) { 
      pbe = (PGPPublicKeyEncryptedData) it.next(); 

      sKey = findSecretKey(pgpSec, pbe.getKeyID(), password); 
     } 

     if (sKey == null) { 
      throw new IllegalArgumentException(
        "secret key for message not found."); 
     } 

     InputStream clear = pbe.getDataStream(sKey, "BC"); 



     PGPObjectFactory pgpFact = new PGPObjectFactory(clear); 

     PGPCompressedData cData = (PGPCompressedData) pgpFact.nextObject(); 

     pgpFact = new PGPObjectFactory(cData.getDataStream()); 

     PGPLiteralData ld = (PGPLiteralData) pgpFact.nextObject(); 

     InputStream unc = ld.getInputStream(); 

     ByteArrayOutputStream out = new ByteArrayOutputStream(); 
     int ch; 

     while ((ch = unc.read()) >= 0) { 
      out.write(ch); 

     } 

     byte[] returnBytes = out.toByteArray(); 
     out.close(); 
     return returnBytes; 
    } 

    /** 
    * Simple PGP encryptor between byte[]. 
    * 
    * @param clearData 
    *   The test to be encrypted 
    * @param passPhrase 
    *   The pass phrase (key). This method assumes that the key is a 
    *   simple pass phrase, and does not yet support RSA or more 
    *   sophisiticated keying. 
    * @param fileName 
    *   File name. This is used in the Literal Data Packet (tag 11) 
    *   which is really inly important if the data is to be related to 
    *   a file to be recovered later. Because this routine does not 
    *   know the source of the information, the caller can set 
    *   something here for file name use that will be carried. If this 
    *   routine is being used to encrypt SOAP MIME bodies, for 
    *   example, use the file name from the MIME type, if applicable. 
    *   Or anything else appropriate. 
    * 
    * @param armor 
    * 
    * @return encrypted data. 
    * @exception IOException 
    * @exception PGPException 
    * @exception NoSuchProviderException 
    */ 
    public static byte[] encrypt(byte[] clearData, PGPPublicKey encKey, 
      String fileName,boolean withIntegrityCheck, boolean armor) 
      throws IOException, PGPException, NoSuchProviderException { 
     if (fileName == null) { 
      fileName = PGPLiteralData.CONSOLE; 
     } 

     ByteArrayOutputStream encOut = new ByteArrayOutputStream(); 

     OutputStream out = encOut; 
     if (armor) { 
      out = new ArmoredOutputStream(out); 
     } 

     ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 

     PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(
       PGPCompressedDataGenerator.ZIP); 
     OutputStream cos = comData.open(bOut); // open it with the final 
     // destination 
     PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); 

     // we want to generate compressed data. This might be a user option 
     // later, 
     // in which case we would pass in bOut. 
     OutputStream pOut = lData.open(cos, // the compressed output stream 
       PGPLiteralData.BINARY, fileName, // "filename" to store 
       clearData.length, // length of clear data 
       new Date() // current time 
       ); 
     pOut.write(clearData); 

     lData.close(); 
     comData.close(); 

     PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(
       PGPEncryptedData.CAST5, withIntegrityCheck, new SecureRandom(), 
       "BC"); 

     cPk.addMethod(encKey); 

     byte[] bytes = bOut.toByteArray(); 

     OutputStream cOut = cPk.open(out, bytes.length); 

     cOut.write(bytes); // obtain the actual bytes from the compressed stream 

     cOut.close(); 

     out.close(); 

     return encOut.toByteArray(); 
    } 

    private static PGPPublicKey readPublicKey(InputStream in) 
      throws IOException, PGPException { 
     in = PGPUtil.getDecoderStream(in); 

     PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in); 

     // 
     // we just loop through the collection till we find a key suitable for 
     // encryption, in the real 
     // world you would probably want to be a bit smarter about this. 
     // 

     // 
     // iterate through the key rings. 
     // 
     Iterator rIt = pgpPub.getKeyRings(); 

     while (rIt.hasNext()) { 
      PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next(); 
      Iterator kIt = kRing.getPublicKeys(); 

      while (kIt.hasNext()) { 
       PGPPublicKey k = (PGPPublicKey) kIt.next(); 

       if (k.isEncryptionKey()) { 
        return k; 
       } 
      } 
     } 

     throw new IllegalArgumentException(
       "Can't find encryption key in key ring."); 
    } 

    public static byte[] getBytesFromFile(File file) throws IOException { 
     InputStream is = new FileInputStream(file); 

     // Get the size of the file 
     long length = file.length(); 

     if (length > Integer.MAX_VALUE) { 
      // File is too large 
     } 

     // Create the byte array to hold the data 
     byte[] bytes = new byte[(int)length]; 

     // Read in the bytes 
     int offset = 0; 
     int numRead = 0; 
     while (offset < bytes.length 
       && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) { 
      offset += numRead; 
     } 

     // Ensure all the bytes have been read in 
     if (offset < bytes.length) { 
      throw new IOException("Could not completely read file "+file.getName()); 
     } 

     // Close the input stream and return bytes 
     is.close(); 
     return bytes; 
    } 

    public static void main(String[] args) throws Exception { 
     Security.addProvider(new BouncyCastleProvider()); 


     byte[] original = "Hello world".getBytes(); 
     System.out.println("Starting PGP test"); 

     FileInputStream pubKey = new FileInputStream("/Users/me/pub.key"); 
     byte[] encrypted = encrypt(original, readPublicKey(pubKey), null, 
       true, true); 

     FileOutputStream dfis = new FileOutputStream("/Users/me/enc.asc"); 
     dfis.write(encrypted); 
     dfis.close(); 

     byte[] encFromFile = getBytesFromFile(new File("/Users/me/enc.asc")); 
     FileInputStream secKey = new FileInputStream("/Users/me/sec.key"); 

     System.out.println("\nencrypted data = '" + new String(encrypted) + "'"); 

     byte[] decrypted = decrypt(encFromFile, secKey, "passphrase".toCharArray()); 

     System.out.println("\ndecrypted data = '" + new String(decrypted) + "'"); 


    } 
} 

回答

12

纵观source of PGPUtil你可以看到流或阵列直接工作时所说的API:

public static void writeFileToLiteralData(OutputStream out, 
       char fileType, File file, byte[] buffer) throws IOException { 
    PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); 
    OutputStream pOut = lData.open(out, fileType, file.getName(), 
        new Date(file.lastModified()), buffer); 
    FileInputStream in = new FileInputStream(file); 
    byte[] buf = new byte[buffer.length]; 
    int len; 

    while ((len = in.read(buf)) > 0) { 
     pOut.write(buf, 0, len); 
    } 

    lData.close(); 
    in.close(); 
} 
+0

是的,我看到这只是张贴不过我宁愿不重写有人现在已经写和共享:)东西之后。我会写,如果我必须,但我不进入整个车轮重塑现场 – dstarh 2010-10-15 10:20:22

+1

原来有一个字节数组的例子,使用密码短语,而不是酒吧/私人,但我abele结合公共私人示例与字节数组的例子,并得到它的工作。有一件事要注意,如果没有文件,你可以直接使用文件名PGPLiteralData.CONSOLE而不是 – dstarh 2010-10-19 02:16:54

+0

中断链接。请检查。 – 2017-05-16 13:12:31

0

您的代码对我来说是非常有用的。我正在ColdFusion(使用Java)工作,需要获取密码,并将它们编码为LDIFDE的短文件(用于Active Directory中的另一台服务器),我从不希望密码以明文方式接触磁盘。我稍微修改它以添加两个函数,抽象出加密字符串磁盘和从磁盘解密的常见用例。谢谢@dstarh。

import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.security.NoSuchProviderException; 
import java.security.SecureRandom; 
import java.security.Security; 
import java.util.Date; 
import java.util.Iterator; 

import org.bouncycastle.bcpg.ArmoredOutputStream; 
import org.bouncycastle.jce.provider.BouncyCastleProvider; 
import org.bouncycastle.openpgp.PGPCompressedData; 
import org.bouncycastle.openpgp.PGPCompressedDataGenerator; 
import org.bouncycastle.openpgp.PGPEncryptedData; 
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator; 
import org.bouncycastle.openpgp.PGPEncryptedDataList; 
import org.bouncycastle.openpgp.PGPException; 
import org.bouncycastle.openpgp.PGPLiteralData; 
import org.bouncycastle.openpgp.PGPLiteralDataGenerator; 
import org.bouncycastle.openpgp.PGPObjectFactory; 
import org.bouncycastle.openpgp.PGPPrivateKey; 
import org.bouncycastle.openpgp.PGPPublicKey; 
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData; 
import org.bouncycastle.openpgp.PGPPublicKeyRing; 
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; 
import org.bouncycastle.openpgp.PGPSecretKey; 
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; 
import org.bouncycastle.openpgp.PGPUtil; 

/** 
* Simple routine to encrypt and decrypt using a Public and Private key with passphrase. This service 
* routine provides the basic PGP services between byte arrays. 
* 
*/ 
public class PgpEncryption { 

    public PgpEncryption() { 
     // Empty constructor 
    } 

    private static PGPPrivateKey findSecretKey(
      PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass) 
      throws PGPException, NoSuchProviderException { 
     PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID); 

     if (pgpSecKey == null) { 
      return null; 
     } 

     return pgpSecKey.extractPrivateKey(pass, "BC"); 
    } 

    /** 
    * decrypt the passed in message stream 
    * 
    * @param encrypted 
    *   The message to be decrypted. 
    * @param passPhrase 
    *   Pass phrase (key) 
    * 
    * @return Clear text as a byte array. I18N considerations are not handled 
    *   by this routine 
    * @exception IOException 
    * @exception PGPException 
    * @exception NoSuchProviderException 
    */ 
    public static byte[] decrypt(byte[] encrypted, InputStream keyIn, char[] password) 
      throws IOException, PGPException, NoSuchProviderException { 
     InputStream in = new ByteArrayInputStream(encrypted); 

     in = PGPUtil.getDecoderStream(in); 

     PGPObjectFactory pgpF = new PGPObjectFactory(in); 
     PGPEncryptedDataList enc = null; 
     Object o = pgpF.nextObject(); 

     // 
     // the first object might be a PGP marker packet. 
     // 
     if (o instanceof PGPEncryptedDataList) { 
      enc = (PGPEncryptedDataList) o; 
     } else { 
      enc = (PGPEncryptedDataList) pgpF.nextObject(); 
     } 



     // 
     // find the secret key 
     // 
     Iterator it = enc.getEncryptedDataObjects(); 
     PGPPrivateKey sKey = null; 
     PGPPublicKeyEncryptedData pbe = null; 
     PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
       PGPUtil.getDecoderStream(keyIn)); 

     while (sKey == null && it.hasNext()) { 
      pbe = (PGPPublicKeyEncryptedData) it.next(); 

      sKey = findSecretKey(pgpSec, pbe.getKeyID(), password); 
     } 

     if (sKey == null) { 
      throw new IllegalArgumentException(
        "secret key for message not found."); 
     } 

     InputStream clear = pbe.getDataStream(sKey, "BC"); 



     PGPObjectFactory pgpFact = new PGPObjectFactory(clear); 

     PGPCompressedData cData = (PGPCompressedData) pgpFact.nextObject(); 

     pgpFact = new PGPObjectFactory(cData.getDataStream()); 

     PGPLiteralData ld = (PGPLiteralData) pgpFact.nextObject(); 

     InputStream unc = ld.getInputStream(); 

     ByteArrayOutputStream out = new ByteArrayOutputStream(); 
     int ch; 

     while ((ch = unc.read()) >= 0) { 
      out.write(ch); 

     } 

     byte[] returnBytes = out.toByteArray(); 
     out.close(); 
     return returnBytes; 
    } 

    /** 
    * Simple PGP encryptor between byte[]. 
    * 
    * @param clearData 
    *   The test to be encrypted 
    * @param passPhrase 
    *   The pass phrase (key). This method assumes that the key is a 
    *   simple pass phrase, and does not yet support RSA or more 
    *   sophisiticated keying. 
    * @param fileName 
    *   File name. This is used in the Literal Data Packet (tag 11) 
    *   which is really inly important if the data is to be related to 
    *   a file to be recovered later. Because this routine does not 
    *   know the source of the information, the caller can set 
    *   something here for file name use that will be carried. If this 
    *   routine is being used to encrypt SOAP MIME bodies, for 
    *   example, use the file name from the MIME type, if applicable. 
    *   Or anything else appropriate. 
    * 
    * @param armor 
    * 
    * @return encrypted data. 
    * @exception IOException 
    * @exception PGPException 
    * @exception NoSuchProviderException 
    */ 
    public static byte[] encrypt(byte[] clearData, PGPPublicKey encKey, 
      String fileName,boolean withIntegrityCheck, boolean armor) 
      throws IOException, PGPException, NoSuchProviderException { 
     if (fileName == null) { 
      fileName = PGPLiteralData.CONSOLE; 
     } 

     ByteArrayOutputStream encOut = new ByteArrayOutputStream(); 

     OutputStream out = encOut; 
     if (armor) { 
      out = new ArmoredOutputStream(out); 
     } 

     ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 

     PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(
       PGPCompressedDataGenerator.ZIP); 
     OutputStream cos = comData.open(bOut); // open it with the final 
     // destination 
     PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); 

     // we want to generate compressed data. This might be a user option 
     // later, 
     // in which case we would pass in bOut. 
     OutputStream pOut = lData.open(cos, // the compressed output stream 
       PGPLiteralData.BINARY, fileName, // "filename" to store 
       clearData.length, // length of clear data 
       new Date() // current time 
       ); 
     pOut.write(clearData); 

     lData.close(); 
     comData.close(); 

     PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(
       PGPEncryptedData.CAST5, withIntegrityCheck, new SecureRandom(), 
       "BC"); 

     cPk.addMethod(encKey); 

     byte[] bytes = bOut.toByteArray(); 

     OutputStream cOut = cPk.open(out, bytes.length); 

     cOut.write(bytes); // obtain the actual bytes from the compressed stream 

     cOut.close(); 

     out.close(); 

     return encOut.toByteArray(); 
    } 

    private static PGPPublicKey readPublicKey(InputStream in) 
      throws IOException, PGPException { 
     in = PGPUtil.getDecoderStream(in); 

     PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in); 

     // 
     // we just loop through the collection till we find a key suitable for 
     // encryption, in the real 
     // world you would probably want to be a bit smarter about this. 
     // 

     // 
     // iterate through the key rings. 
     // 
     Iterator rIt = pgpPub.getKeyRings(); 

     while (rIt.hasNext()) { 
      PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next(); 
      Iterator kIt = kRing.getPublicKeys(); 

      while (kIt.hasNext()) { 
       PGPPublicKey k = (PGPPublicKey) kIt.next(); 

       if (k.isEncryptionKey()) { 
        return k; 
       } 
      } 
     } 

     throw new IllegalArgumentException(
       "Can't find encryption key in key ring."); 
    } 

    public static byte[] getBytesFromFile(File file) throws IOException { 
     InputStream is = new FileInputStream(file); 

     // Get the size of the file 
     long length = file.length(); 

     if (length > Integer.MAX_VALUE) { 
      // File is too large 
     } 

     // Create the byte array to hold the data 
     byte[] bytes = new byte[(int)length]; 

     // Read in the bytes 
     int offset = 0; 
     int numRead = 0; 
     while (offset < bytes.length 
       && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) { 
      offset += numRead; 
     } 

     // Ensure all the bytes have been read in 
     if (offset < bytes.length) { 
      throw new IOException("Could not completely read file "+file.getName()); 
     } 

     // Close the input stream and return bytes 
     is.close(); 
     return bytes; 
    } 

    public static String encryptToFile(String inputStr, String keyFile, String outFile) throws Exception { 
     Security.addProvider(new BouncyCastleProvider()); 

     byte[] original = inputStr.getBytes(); 

     FileInputStream pubKey = new FileInputStream(keyFile); 
     byte[] encrypted = encrypt(original, readPublicKey(pubKey), null, 
       true, true); 

     FileOutputStream dfis = new FileOutputStream(outFile); 
     dfis.write(encrypted); 
     dfis.close(); 

     return new String(encrypted); 
    } 

    public static String decryptFromFile(String passphrase, String keyFile, String inputFile) throws Exception { 
     Security.addProvider(new BouncyCastleProvider()); 

     byte[] encFromFile = getBytesFromFile(new File(inputFile)); 
     FileInputStream secKey = new FileInputStream(keyFile); 

     byte[] decrypted = decrypt(encFromFile, secKey, passphrase.toCharArray()); 

     return new String(decrypted); 
    } 

    public static void main(String[] args) throws Exception { 
     String encrypted = encryptToFile("Hello world","pub.asc","enc.asc"); 
     System.out.println("\nencrypted data = '" + new String(encrypted) + "'"); 

     String decrypted = decryptFromFile("open sesame", "secret.asc", "enc.asc"); 
     System.out.println("\ndecrypted data = '" + decrypted + "'"); 

    } 
} 
+2

这不是对我的问题的真正答案,也不适用于SO指导方针。这个问题具体是关于不写入文件,你的例子增加了写入文件和写入文件的能力,其中有很多例子。似乎没有一个关于如何在内存中做到这一点的典型例子。 – dstarh 2012-09-12 19:34:22

2

找到在grepcode repository - payneteasy superfly sources

private static void writeBytesToLiteralData(OutputStream out, 
    char fileType, String name, byte[] bytes) throws IOException { 
     PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); 
     OutputStream pOut = lData.open(out, fileType, name,bytes.length, new Date()); 
     pOut.write(bytes); 
}