2010-10-18 75 views
0

當我調用它時,它成功地加密了我的字符串,但解密文本的輸出爲空。我沒有得到任何錯誤,輸出字符串的byteArray是正確的長度(102),但它只有102個零。這是根據KeyBasedFileProcessor示例進行調整的,但是嘗試基於流/字符串而不是基於文件。Bouncycastle pgp解密正確大小但全爲空

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.apache.commons.io.IOUtils; 
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.PGPOnePassSignatureList; 
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; 

/** 
* A simple utility class that encrypts/decrypts public key based encryption 
* files. 
* <p> 
* To encrypt a file: KeyBasedFileProcessor -e [-a|-ai] fileName publicKeyFile.<br> 
* If -a is specified the output file will be "ascii-armored". If -i is 
* specified the output file will be have integrity checking added. 
* <p> 
* To decrypt: KeyBasedFileProcessor -d fileName secretKeyFile passPhrase. 
* <p> 
* Note 1: this example will silently overwrite files, nor does it pay any 
* attention to the specification of "_CONSOLE" in the filename. It also expects 
* that a single pass phrase will have been used. 
* <p> 
* Note 2: if an empty file name has been specified in the literal data object 
* contained in the encrypted packet a file with the name filename.out will be 
* generated in the current working directory. 
*/ 
public class PgpEncryption3 { 
    /** 
    * A simple routine that opens a key ring file and loads the first available 
    * key suitable for encryption. 
    * 
    * @param in 
    * @return 
    * @throws IOException 
    * @throws PGPException 
    */ 
    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."); 
    } 

    /** 
    * Search a secret key ring collection for a secret key corresponding to 
    * keyID if it exists. 
    * 
    * @param pgpSec 
    *   a secret key ring collection. 
    * @param keyID 
    *   keyID we want. 
    * @param pass 
    *   passphrase to decrypt secret key with. 
    * @return 
    * @throws PGPException 
    * @throws NoSuchProviderException 
    */ 
    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 
    */ 
    private static void decryptFile(InputStream in, InputStream keyIn, 
      char[] passwd, String defaultFileName, OutputStream out) throws Exception { 
     in = PGPUtil.getDecoderStream(in); 


     try { 
      PGPObjectFactory pgpF = new PGPObjectFactory(in); 
      PGPEncryptedDataList enc; 

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

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

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

      PGPObjectFactory plainFact = new PGPObjectFactory(clear); 

      Object message = plainFact.nextObject(); 

      if (message instanceof PGPCompressedData) { 
       PGPCompressedData cData = (PGPCompressedData) message; 
       PGPObjectFactory pgpFact = new PGPObjectFactory(cData 
         .getDataStream()); 

       message = pgpFact.nextObject(); 
      } 

      if (message instanceof PGPLiteralData) { 
       System.out.println(message); 
       PGPLiteralData ld = (PGPLiteralData) message; 
       System.out.println(ld.getFileName()); 
       System.out.println(ld.getDataStream()); 
       InputStream unc = ld.getInputStream(); 
       int ch; 
       while ((ch = unc.read()) >= 0) { 
        System.out.println(ch); 
        out.write(ch); 
       } 
      } else if (message instanceof PGPOnePassSignatureList) { 
       throw new PGPException(
         "encrypted message contains a signed message - not literal data."); 
      } else { 
       throw new PGPException(
         "message is not a simple encrypted file - type unknown."); 
      } 

      if (pbe.isIntegrityProtected()) { 
       if (!pbe.verify()) { 
        System.err.println("message failed integrity check"); 
       } else { 
        System.err.println("message integrity check passed"); 
       } 
      } else { 
       System.err.println("no message integrity check"); 
      } 
     } catch (PGPException e) { 
      System.err.println(e); 
      if (e.getUnderlyingException() != null) { 
       e.getUnderlyingException().printStackTrace(); 
      } 
     } 
    } 


    public static void writeStreamToLiteralData(OutputStream out, 
      char fileType, byte[] data, String fileName, Date modDate) throws IOException { 
     PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); 
     OutputStream pOut = lData.open(out, fileType, fileName, data.length, modDate); 

     byte[] buf = new byte[4096]; 
     ByteArrayInputStream in = new ByteArrayInputStream(buf); 
     int len; 

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

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

    private static void encryptFile(OutputStream out, byte[] data,String fileName, 
      PGPPublicKey encKey, boolean armor, boolean withIntegrityCheck, Date modDate) 
      throws IOException, NoSuchProviderException { 
     if (armor) { 
      out = new ArmoredOutputStream(out); 
     } 

     try { 
      ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 




      writeStreamToLiteralData(bOut,PGPLiteralData.TEXT, data, fileName,modDate); 


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

      cOut.close(); 

      out.close(); 
     } catch (PGPException e) { 
      System.err.println(e); 
      if (e.getUnderlyingException() != null) { 
       e.getUnderlyingException().printStackTrace(); 
      } 
     } 
    } 

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


     Date modDate = new Date(); 

     String dataToEncrypt = "THIS IS SOME TEXTTHIS IS SOME TEXTTHIS IS SOME TEXTTHIS IS SOME TEXTTHIS IS SOME TEXTTHIS IS SOME TEXT"; 
     System.out.println(dataToEncrypt.length()); 
     byte[] data = dataToEncrypt.getBytes(); 
     String fileName = "blah.txt"; 
     ByteArrayOutputStream out = new ByteArrayOutputStream(); 
     FileInputStream pKeyIn = new FileInputStream("/Users/me/blah.pub.key"); 
     encryptFile(out, data, fileName,readPublicKey(pKeyIn), true, false,modDate); 
     System.out.println(new String(out.toByteArray())); 

     ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); 
     System.out.println(in); 
     FileInputStream sKeyIn = new FileInputStream("/Users/me/blah.sec.key"); 
     ByteArrayOutputStream decOut = new ByteArrayOutputStream(); 
     decryptFile(in, sKeyIn, "mypass".toCharArray(), "blah.txt", decOut); 
     System.out.println(decOut.toByteArray().length); 
     System.out.println(new String(decOut.toByteArray())); 

    } 
} 

回答

1

完整的工作示例

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) + "'"); 


    } 
}