package org.oobd.crypt.gpg; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.BufferedInputStream; import java.security.NoSuchProviderException; import java.security.Security; import java.util.Iterator; import org.spongycastle.bcpg.PublicKeyAlgorithmTags; import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.openpgp.PGPCompressedData; import org.spongycastle.openpgp.PGPEncryptedDataList; import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPLiteralData; import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPOnePassSignatureList; import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRingCollection; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRingCollection; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignatureSubpacketVector; import org.spongycastle.openpgp.PGPUtil; import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; import org.spongycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory; public class PGPUtils { private static final int BUFFER_SIZE = 1 << 16; // should always be power of 2 private static final int KEY_FLAGS = 27; private static final int[] MASTER_KEY_CERTIFICATION_TYPES = new int[]{ PGPSignature.POSITIVE_CERTIFICATION, PGPSignature.CASUAL_CERTIFICATION, PGPSignature.NO_CERTIFICATION, PGPSignature.DEFAULT_CERTIFICATION }; public static void init() { Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider()); } //Read more: http://www.aviransplace.com/2004/10/12/using-rsa-encryption-with-java/#ixzz2GYW2AadB @SuppressWarnings("unchecked") public static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException { PGPPublicKeyRingCollection keyRingCollection = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(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. // PGPPublicKey publicKey = null; // // iterate through the key rings. // Iterator<PGPPublicKeyRing> rIt = keyRingCollection.getKeyRings(); while (publicKey == null && rIt.hasNext()) { PGPPublicKeyRing kRing = rIt.next(); Iterator<PGPPublicKey> kIt = kRing.getPublicKeys(); while (publicKey == null && kIt.hasNext()) { PGPPublicKey key = kIt.next(); if (key.isEncryptionKey()) { publicKey = key; } } } if (publicKey == null) { throw new IllegalArgumentException("Can't find public key in the key ring."); } if (!isForEncryption(publicKey)) { throw new IllegalArgumentException("KeyID " + publicKey.getKeyID() + " not flagged for encryption."); } return publicKey; } @SuppressWarnings("unchecked") public static PGPSecretKey readSecretKey(InputStream in) throws IOException, PGPException { PGPSecretKeyRingCollection keyRingCollection = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(in)); // // We just loop through the collection till we find a key suitable for signing. // In the real world you would probably want to be a bit smarter about this. // PGPSecretKey secretKey = null; Iterator<PGPSecretKeyRing> rIt = keyRingCollection.getKeyRings(); while (secretKey == null && rIt.hasNext()) { PGPSecretKeyRing keyRing = rIt.next(); Iterator<PGPSecretKey> kIt = keyRing.getSecretKeys(); while (secretKey == null && kIt.hasNext()) { PGPSecretKey key = kIt.next(); if (key.isSigningKey()) { secretKey = key; } } } // Validate secret key if (secretKey == null) { throw new IllegalArgumentException("Can't find private key in the key ring."); } if (!secretKey.isSigningKey()) { throw new IllegalArgumentException("Private key does not allow signing."); } if (secretKey.getPublicKey().isRevoked()) { throw new IllegalArgumentException("Private key has been revoked."); } if (!hasKeyFlags(secretKey.getPublicKey(), KeyFlags.SIGN_DATA)) { throw new IllegalArgumentException("Key cannot be used for signing."); } return secretKey; } /** * Load a secret key ring collection from keyIn and find the private key corresponding to * keyID if it exists. * * @param keyIn input stream representing a key ring collection. * @param keyID keyID we want. * @param pass passphrase to decrypt secret key with. * @return * @throws IOException * @throws PGPException * @throws NoSuchProviderException */ public static PGPPrivateKey findPrivateKey(InputStream keyIn, long keyID, char[] pass) throws IOException, PGPException, NoSuchProviderException { PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyIn)); return findPrivateKey(pgpSec.getSecretKey(keyID), pass); } /** * Load a secret key and find the private key in it * @param pgpSecKey The secret key * @param pass passphrase to decrypt secret key with * @return * @throws PGPException */ public static PGPPrivateKey findPrivateKey(PGPSecretKey pgpSecKey, char[] pass) throws PGPException, java.security.NoSuchProviderException { if (pgpSecKey == null) return null; PBESecretKeyDecryptor decryptor = new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass); return pgpSecKey.extractPrivateKey(decryptor); // return pgpSecKey.extractPrivateKey(pass, BouncyCastleProvider.PROVIDER_NAME); } /** * decrypt the passed in message stream */ @SuppressWarnings("unchecked") public static void decryptFile(InputStream in, OutputStream out, InputStream keyIn, char[] passwd) throws Exception { InputStream unc = decryptFileStream( in, keyIn, passwd); int ch; while ((ch = unc.read()) >= 0) { out.write(ch); } } //------------- NEW from http://blog.mrjaredpowell.com/2010/Automate_decryption_Bouncy_Castle.htm /** * returns an encrypted input stream * @throws PGPException * @throws IOException * @throws NoSuchProviderException */ @SuppressWarnings("unchecked") public static InputStream decryptFileStream(InputStream in, InputStream keyIn, char[] passwd) throws IOException, PGPException, NoSuchProviderException { InputStream unc =null; in = PGPUtil.getDecoderStream(in); PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyIn)); PGPObjectFactory pgpF = new PGPObjectFactory(in); PGPEncryptedDataList enc; Object o = pgpF.nextObject(); if (o instanceof PGPEncryptedDataList) { enc = (PGPEncryptedDataList) o; } else { enc = (PGPEncryptedDataList) pgpF.nextObject(); } // @SuppressWarnings("rawtypes") Iterator it = enc.getEncryptedDataObjects(); PGPPrivateKey sKey = null; PGPPublicKeyEncryptedData pbe = null; while (sKey == null && it.hasNext()) { pbe = (PGPPublicKeyEncryptedData) it.next(); sKey = KeyReader.findSecretKey(pgpSec, pbe.getKeyID(), passwd); } if (sKey == null) { throw new IllegalArgumentException("Failed to find private key with ID " + pbe.getKeyID()); } //---- added by OOBD InputStream clear = pbe.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey)); //---- instead of //InputStream clear = pbe.getDataStream(sKey, "SC"); PGPObjectFactory plainFact = new PGPObjectFactory(clear); PGPCompressedData cData = (PGPCompressedData) plainFact.nextObject(); InputStream compressedStream = new BufferedInputStream(cData.getDataStream()); PGPObjectFactory pgpFact = new PGPObjectFactory(compressedStream); Object message = pgpFact.nextObject(); if (message instanceof PGPLiteralData) { PGPLiteralData ld = (PGPLiteralData) message; unc = ld.getInputStream(); } 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."); } /* ------------- the commended routine below always caused an EOFreached error 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"); } */ return unc; } /** * From LockBox Lobs PGP Encryption tools. * http://www.lockboxlabs.org/content/downloads * * I didn't think it was worth having to import a 4meg lib for three methods * @param key * @return */ public static boolean isForEncryption(PGPPublicKey key) { if (key.getAlgorithm() == PublicKeyAlgorithmTags.RSA_SIGN || key.getAlgorithm() == PublicKeyAlgorithmTags.DSA || key.getAlgorithm() == PublicKeyAlgorithmTags.EC || key.getAlgorithm() == PublicKeyAlgorithmTags.ECDSA) { return false; } return hasKeyFlags(key, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE); } /** * From LockBox Lobs PGP Encryption tools. * http://www.lockboxlabs.org/content/downloads * * I didn't think it was worth having to import a 4meg lib for three methods * @param key * @return */ @SuppressWarnings("unchecked") private static boolean hasKeyFlags(PGPPublicKey encKey, int keyUsage) { if (encKey.isMasterKey()) { for (int i = 0; i != PGPUtils.MASTER_KEY_CERTIFICATION_TYPES.length; i++) { for (Iterator<PGPSignature> eIt = encKey.getSignaturesOfType(PGPUtils.MASTER_KEY_CERTIFICATION_TYPES[i]); eIt.hasNext();) { PGPSignature sig = eIt.next(); if (!isMatchingUsage(sig, keyUsage)) { return false; } } } } else { for (Iterator<PGPSignature> eIt = encKey.getSignaturesOfType(PGPSignature.SUBKEY_BINDING); eIt.hasNext();) { PGPSignature sig = eIt.next(); if (!isMatchingUsage(sig, keyUsage)) { return false; } } } return true; } /** * From LockBox Lobs PGP Encryption tools. * http://www.lockboxlabs.org/content/downloads * * I didn't think it was worth having to import a 4meg lib for three methods * @param key * @return */ private static boolean isMatchingUsage(PGPSignature sig, int keyUsage) { if (sig.hasSubpackets()) { PGPSignatureSubpacketVector sv = sig.getHashedSubPackets(); if (sv.hasSubpacket(PGPUtils.KEY_FLAGS)) { if ((sv.getKeyFlags() & keyUsage) == 0) { return false; } } } return true; } /*public class EncryptFiles { //* //* Encrypts a file for transfer over standard FTP or via email //* @param out Output Stream containing the file //* @param fileName String representation of the file we want to create //* @param encKey PGPPublicKey to encrypt the file. //* @param armor boolean value decides whether we need a new instance of an ArmoredOutputStream //* @param withIntegrityCheck boolean value for setting the Integrity Packet //* @throws IOException //* @throws NoSuchProviderException // public static void encryptFile(OutputStream out, String fileName, PGPPublicKey encKey, boolean armor, boolean withIntegrityCheck) throws IOException, NoSuchProviderException { if (armor) { out = new ArmoredOutputStream(out); } try { PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, withIntegrityCheck,new SecureRandom(), "BC"); cPk.addMethod(encKey); OutputStream cOut = cPk.open(out, new byte[1 << 16]); PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedData.ZIP); PGPUtil.writeFileToLiteralData(comData.open(cOut), PGPLiteralData.BINARY, new File(fileName), new byte[1 << 16]); comData.close(); cOut.close(); out.close(); } catch (PGPException e) { System.err.println(e); if (e.getUnderlyingException() != null) { e.getUnderlyingException().printStackTrace(); } } } } */ }