The League of Bouncy Castle Cryptography library is chock-full of goodies but it is hard to convert what is in there to more practical examples.
The example files are a solid basis but I seam to need to fiddle quite a bit until it something is usable for me. The PGP Single Pass Sign and Encrypt process is one of these things that took me for a long time to figure out. I owe much of the actual solution impementation to John Opincar who solved this puzzle for C#.
Here is my implementation for Java:
/** * This is the primary function that will create encrypt a file and sign it * with a one pass signature. This leans on an C# example by John Opincar * @author Bilal Soylu * @param targetFileName * -- file name on drive systems that will contain encrypted content * @param embeddedFileName * -- the original file name before encryption * @param secretKeyRingInputStream * -- Private Key Ring File * @param targetFileStream * -- The stream for the encrypted target file * @param secretKeyPassphrase * -- The private key password for the key retrieved from * collection used for signing * @param signPublicKeyInputStream * -- the public key of the target recipient to be used to * encrypt the file * @throws Exception */ public void fEncryptOnePassSignatureLocal(String targetFileName, String embeddedFileName, InputStream secretKeyRingInputStream, OutputStream targetFileStream, String secretKeyPassphrase, InputStream signPublicKeyInputStream, InputStream contentStream) throws Exception { // ** INIT // read public Key from stream (file, if keyring we use the first working key) PGPPublicKey encKey = readPublicKey(signPublicKeyInputStream); // need to convert the password to a character array char[] password = secretKeyPassphrase.toCharArray(); int BUFFER_SIZE = 1 << 16; // should always be power of 2(one shifted bitwise 16 places) //for now we will always do integrity checks and armor file boolean armor = true; boolean withIntegretyCheck = true; //set default provider, we will pass this along BouncyCastleProvider bcProvider = new BouncyCastleProvider(); // armor stream if set if (armor) targetFileStream = new ArmoredOutputStream(targetFileStream); // Init encrypted data generator PGPEncryptedDataGenerator encryptedDataGenerator = new PGPEncryptedDataGenerator( SymmetricKeyAlgorithmTags.CAST5, withIntegretyCheck, new SecureRandom(), bcProvider); encryptedDataGenerator.addMethod(encKey); OutputStream encryptedOut = encryptedDataGenerator.open(targetFileStream,new byte[BUFFER_SIZE]); // start compression PGPCompressedDataGenerator compressedDataGenerator = new PGPCompressedDataGenerator( CompressionAlgorithmTags.ZIP); OutputStream compressedOut = compressedDataGenerator.open(encryptedOut); //start signature //PGPSecretKeyRingCollection pgpSecBundle = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(secretKeyRingInputStream)); //PGPSecretKey pgpSecKey = pgpSecBundle.getSecretKey(keyId); PGPSecretKey pgpSecKey = readSecretKey(secretKeyRingInputStream); if (pgpSecKey == null) throw new Exception("No secret key could be found in specified key ring collection."); PGPPrivateKey pgpPrivKey = pgpSecKey.extractPrivateKey(password,bcProvider); PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator( pgpSecKey.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1, bcProvider); signatureGenerator.initSign(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); // iterate to find first signature to use for (@SuppressWarnings("rawtypes") Iterator i = pgpSecKey.getPublicKey().getUserIDs(); i.hasNext();) { String userId = (String) i.next(); PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); spGen.setSignerUserID(false, userId); signatureGenerator.setHashedSubpackets(spGen.generate()); // Just the first one! break; } signatureGenerator.generateOnePassVersion(false).encode(compressedOut); // Create the Literal Data generator output stream PGPLiteralDataGenerator literalDataGenerator = new PGPLiteralDataGenerator(); // get file handle File actualFile = new File(targetFileName); // create output stream OutputStream literalOut = literalDataGenerator.open(compressedOut, PGPLiteralData.BINARY, embeddedFileName, new Date(actualFile.lastModified()), new byte[BUFFER_SIZE]); // read input file and write to target file using a buffer byte[] buf = new byte[BUFFER_SIZE]; int len; while ((len = contentStream.read(buf, 0, buf.length)) > 0) { literalOut.write(buf, 0, len); signatureGenerator.update(buf, 0, len); } // close everything down we are done literalOut.close(); literalDataGenerator.close(); signatureGenerator.generate().encode(compressedOut); compressedOut.close(); compressedDataGenerator.close(); encryptedOut.close(); encryptedDataGenerator.close(); if (armor) targetFileStream.close(); } /** * Try to find a public key in the Key File or Key Ring File * We will use the first one for now. * @author Bilal Soylu * @param in -- File Stream to KeyRing or Key * @return first public key * @throws IOException * @throws PGPException */ private static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException { in = PGPUtil.getDecoderStream(in); PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in); // // we are only looking for the first key that matches // // // 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."); } /** * Find first secret key in key ring or key file. * A secret key contains a private key that can be accessed with a password. * @author Bilal Soylu * @param in -- input Key file or key ring file * @param passwd -- password for key * @return matching private key * @throws IOException * @throws PGPException * @throws NoSuchProviderException */ private static PGPSecretKey readSecretKey(InputStream in) throws IOException, PGPException, NoSuchProviderException { PGPSecretKey sKey = null; try { in = PGPUtil.getDecoderStream(in); PGPSecretKeyRingCollection pgpPriv = new PGPSecretKeyRingCollection(in); // we just loop through the collection till we find a key suitable for // decrypt Iterator it = pgpPriv.getKeyRings(); PGPSecretKeyRing pbr = null; while (sKey == null && it.hasNext()) { Object readData = it.next(); if (readData instanceof PGPSecretKeyRing) { pbr = (PGPSecretKeyRing)readData; sKey = pbr.getSecretKey(); } } if (sKey == null) { throw new IllegalArgumentException("secret key for message not found."); } } catch (PGPException e) { System.err.println(e); if (e.getUnderlyingException() != null) { e.getUnderlyingException().printStackTrace(); } } return sKey; }
B.
1 comment:
Really useful!! This is what I was looking for, you just saved my day and code works very smooth, thanks!!
Post a Comment