OpenPGP 号称是世界上使用最广泛的邮件加密标准. OpenPGP is the most widely used email encryption standard in the world. ( http://www.openpgp.org/ )
这篇例子介绍如何使用这个标准进行文件的加密解密 (https://www.bouncycastle.org/latest_releases.html, 需要下载: bcprov-jdk15on-151.jar, bcpg-jdk15on-151.jar).

主要是使用bouncycastle提供的OpenPGP的库来完成这个功能,参照了其提供的示例程序,进行了部分改动 ( Bouncy Castle 是一种用于 Java 平台的开放源码的轻量级密码术包。它支持大量的密码术算法,并提供 JCE 1.2.1 的实现。因为 Bouncy Castle 被设计成轻量级的,所以从 J2SE 1.4 到 J2ME(包括 MIDP)平台,它都可以运行。它是在 MIDP 上运行的唯一完整的密码术包。)

  1. 添加循环遍历来查找第一个可用的message
  2. 需要注意的是在main函数中的,如果不添加这一句的话 Security.addProvider(new BouncyCastleProvider()); 程序运行中会报错:No such Provider "BC"
  3. 错误Exception in thread "main" java.security.InvalidKeyException: Illegal key size or default parameters , 这是因为java缺省的库支持的key长度比较短,需要到oracle的网站上去下载一个支持更长key的库覆盖原有的库文件
    /lib/securty/ 目录下的两个jar文件
    local_policy.jar and US_export_policy.jar
    搜索这个文件: Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files
    下载页面(以JDK6为例):http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html

package com.test.test;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
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.Iterator;

import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedData;
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.PGPOnePassSignatureList;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.bouncycastle.util.io.Streams;

/**

  • A simple utility class that encrypts/decrypts public key based
  • encryption files.
  • To encrypt a file: KeyBasedFileProcessor -e [-a|-ai] fileName publicKeyFile.
  • If -a is specified the output file will be "ascii-armored".
  • If -i is specified the output file will be have integrity checking added.
  • To decrypt: KeyBasedFileProcessor -d fileName secretKeyFile passPhrase.
  • 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.
  • 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 KeyBasedFileProcessor
    {
    private static void decryptFile(
    String inputFileName,
    String keyFileName,
    char[] passwd,
    String defaultFileName)
    throws IOException, NoSuchProviderException
    {
    InputStream in = new BufferedInputStream(new FileInputStream(inputFileName));
    InputStream keyIn = new BufferedInputStream(new FileInputStream(keyFileName));
    decryptFile(in, keyIn, passwd, defaultFileName);
    keyIn.close();
    in.close();
    }

    /**

    • decrypt the passed in message stream
      */
      private static void decryptFile(
      InputStream in,
      InputStream keyIn,
      char[] passwd,
      String defaultFileName)
      throws IOException, NoSuchProviderException
      {
      in = PGPUtil.getDecoderStream(in);

      try
      {
      JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(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), new JcaKeyFingerprintCalculator());
      
      while (sKey == null && it.hasNext())
      {
          pbe = (PGPPublicKeyEncryptedData)it.next();
      
          sKey = PGPExampleUtil.findSecretKey(pgpSec, pbe.getKeyID(), passwd);
      }
      
      if (sKey == null)
      {
          throw new IllegalArgumentException("secret key for message not found.");
      }
      
      InputStream         clear = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("BC").build(sKey));
      
      JcaPGPObjectFactory    plainFact = new JcaPGPObjectFactory(clear);
      
      Object              message = plainFact.nextObject();
      
      while ( true ) {
          if (message instanceof PGPCompressedData)
          {
              PGPCompressedData   cData = (PGPCompressedData)message;
              JcaPGPObjectFactory    pgpFact = new JcaPGPObjectFactory(cData.getDataStream());
      
              message = pgpFact.nextObject();
          }
      
          if (message instanceof PGPLiteralData)
          {
              PGPLiteralData ld = (PGPLiteralData)message;
      
              String outFileName = ld.getFileName();
              if (outFileName.length() == 0)
              {
                  outFileName = defaultFileName;
              }
      
              InputStream unc = ld.getInputStream();
              OutputStream fOut = new BufferedOutputStream(new FileOutputStream(outFileName));
      
              Streams.pipeAll(unc, fOut);
      
              fOut.close();
              break;
          }
          else if (message instanceof PGPOnePassSignatureList)
          {
              System.out.println("encrypted message contains a signed message - not literal data.");
          }
          else if (message instanceof PGPSignatureList)
          {
              System.out.println("encrypted message contains a signed message - not literal data.");
          }
          else
          {
              throw new PGPException("message is not a simple encrypted file - type unknown.");
          }
          message = plainFact.nextObject();
      }
      
      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();
      }
      }
      }

    private static void encryptFile(
    String outputFileName,
    String inputFileName,
    String encKeyFileName,
    boolean armor,
    boolean withIntegrityCheck)
    throws IOException, NoSuchProviderException, PGPException
    {
    OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName));
    PGPPublicKey encKey = PGPExampleUtil.readPublicKey(encKeyFileName);
    encryptFile(out, inputFileName, encKey, armor, withIntegrityCheck);
    out.close();
    }

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

    try
    {
        byte[] bytes = PGPExampleUtil.compressFile(fileName, CompressionAlgorithmTags.ZIP);
    
        PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
            new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(new SecureRandom()).setProvider("BC"));
    
        encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encKey).setProvider("BC"));
    
        OutputStream cOut = encGen.open(out, bytes.length);
    
        cOut.write(bytes);
        cOut.close();
    
        if (armor)
        {
            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());

    if (args.length == 0)
    {
        System.err.println("usage: KeyBasedFileProcessor -e|-d [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]");
        return;
    }
    
    if (args[0].equals("-e"))
    {
        if (args[1].equals("-a") || args[1].equals("-ai") || args[1].equals("-ia"))
        {
            encryptFile(args[2] + ".asc", args[2], args[3], true, (args[1].indexOf('i') > 0));
        }
        else if (args[1].equals("-i"))
        {
            encryptFile(args[2] + ".bpg", args[2], args[3], false, true);
        }
        else
        {
            encryptFile(args[1] + ".bpg", args[1], args[2], false, false);
        }
    }
    else if (args[0].equals("-d"))
    {
        decryptFile(args[1], args[2], args[3].toCharArray(), new File(args[1]).getName() + ".out");
    }
    else
    {
        System.err.println("usage: KeyBasedFileProcessor -d|-e [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]");
    }

    }
    }