代码之家  ›  专栏  ›  技术社区  ›  dstarh

如何在不以文件开头的情况下使用bouncycastle pgp加密字符串/流

  •  36
  • dstarh  · 技术社区  · 15 年前

    我需要使用BouncyCastle提供程序用PGP加密流。我能找到的所有例子都是关于获取一个纯文本文件并加密,但是我没有一个文件,而且纯文本永远不会被写入磁盘是很重要的。

    我看到的大多数方法都在使用

    pgputil.writefiletoliteraldata,希望传递纯文本。我宁愿输入一个字节[]或一个输入流。

    有人能给我举个例子吗

    • 从字符串/byte[]/inputstream开始
    • 将所说的字符串/字节[]加密到可写入文件的OutputStrem
    • 从输入流解密

    如果有人发现了这一点并希望得到完整的解决方案

    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) + "'");
    
    
        }
    }
    
    4 回复  |  直到 7 年前
        1
  •  12
  •   Thilo    9 年前

    看着 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();
    }
    
        2
  •  2
  •   Dima Katzir    12 年前

    发现于 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);
    }
    
        3
  •  0
  •   JWHardcastle    13 年前

    你的代码对我非常有用。我在CaldFIX(Java)中工作,需要在一个简短的导入文件中为LdifDE(在另一个服务器上的ActiveDirectory中)进行编码,我不希望密码在明文中触摸到磁盘。我只是稍微修改了一下,添加了两个函数,抽象出加密字符串的常见用例。 磁盘和解密 磁盘。谢谢@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 + "'");
    
        }
    }
    
        4
  •  0
  •   Rasheed    7 年前

    这篇文章可能很旧,但这个答案可能对其他人有用。为了避免写入磁盘,如果您只想加密和解密文本。您可以这样做(从原始日志中的代码片段中)…

        // the string you will like to encrypt
        byte[] original = "I love programming".getBytes(); 
    
        // KeyProvider.PUBLIC_KEY this is just a string holding your public key. you can have it stored in memory or something. 
        byte[] encrypted = encrypt(original, readPublicKey(new ByteArrayInputStream(KeyProvider.PUBLIC_KEY.getBytes())), null, true, true); 
    
        System.out.println(new String(encrypted)); // this will output your encrypted message
    
        //encryted here is the encrypted string in byte array you will like to decrypt and also the KeyProvider.PRIVATE_KEY is string holding your private key
        byte[] decrypted = decrypt(encrypted, new ByteArrayInputStream(KeyProvider.PRIVATE_KEY.getBytes()), "".toCharArray());
    
    
        System.out.println(new String(decrypted)); //output the decrypted message
    

    希望这有帮助