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的库覆盖原有的库文件 <JAVA_HOME>/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.

  • <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 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]");
     }
    

    } }