/* ************************************************************************ # # DivConq # # http://divconq.com/ # # Copyright: # Copyright 2014 eTimeline, LLC. All rights reserved. # # License: # See the license.txt file in the project's top-level directory for details. # # Authors: # * Andy White # ************************************************************************ */ package divconq.test.pgp; import io.netty.buffer.ByteBuf; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.MessageDigest; import java.security.SecureRandom; import java.util.Iterator; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import org.bouncycastle.bcpg.ContainedPacket; import org.bouncycastle.bcpg.PacketTags; import org.bouncycastle.jcajce.util.DefaultJcaJceHelper; import org.bouncycastle.openpgp.PGPEncryptedData; import org.bouncycastle.openpgp.PGPLiteralData; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator; import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; import divconq.hub.Hub; import divconq.lang.chars.Utf8Encoder; import divconq.pgp.PGPUtil; import divconq.util.HexUtil; public class PGPWriter2 { @SuppressWarnings("resource") public void test2(String srcpath, String destpath, String keyring) throws Exception { Path src = Paths.get(srcpath); // file data byte[] fileData = Files.readAllBytes(src); // dest OutputStream dest = new BufferedOutputStream(new FileOutputStream(destpath)); // encryption key PGPPublicKey pubKey = null; InputStream keyIn = new BufferedInputStream(new FileInputStream(keyring)); PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(org.bouncycastle.openpgp.PGPUtil.getDecoderStream(keyIn), new JcaKeyFingerprintCalculator()); // // 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. // @SuppressWarnings("rawtypes") Iterator keyRingIter = pgpPub.getKeyRings(); while (keyRingIter.hasNext() && (pubKey == null)) { PGPPublicKeyRing keyRing = (PGPPublicKeyRing)keyRingIter.next(); @SuppressWarnings("rawtypes") Iterator keyIter = keyRing.getPublicKeys(); while (keyIter.hasNext() && (pubKey == null)) { PGPPublicKey key = (PGPPublicKey)keyIter.next(); if (key.isEncryptionKey()) pubKey = key; } } if (pubKey == null) throw new IllegalArgumentException("Can't find encryption key in key ring."); String fileName = src.getFileName().toString(); byte[] encName = Utf8Encoder.encode(fileName); long modificationTime = System.currentTimeMillis(); SecureRandom rand = new SecureRandom(); int algorithm = PGPEncryptedData.AES_256; Cipher cipher = null; ByteBuf leadingbuf = Hub.instance.getBufferAllocator().heapBuffer(1024 * 1024); // 1 mb ByteBuf encbuf = Hub.instance.getBufferAllocator().heapBuffer(1024 * 1024); // 1 mb // ******************************************************************* // public key packet // ******************************************************************* PGPKeyEncryptionMethodGenerator method = new JcePublicKeyKeyEncryptionMethodGenerator(pubKey); byte[] key = org.bouncycastle.openpgp.PGPUtil.makeRandomKey(algorithm, rand); byte[] sessionInfo = new byte[key.length + 3]; // add algorithm sessionInfo[0] = (byte) algorithm; // add key System.arraycopy(key, 0, sessionInfo, 1, key.length); // add checksum int check = 0; for (int i = 1; i != sessionInfo.length - 2; i++) check += sessionInfo[i] & 0xff; sessionInfo[sessionInfo.length - 2] = (byte)(check >> 8); sessionInfo[sessionInfo.length - 1] = (byte)(check); ContainedPacket packet1 = method.generate(algorithm, sessionInfo); byte[] encoded1 = packet1.getEncoded(); leadingbuf.writeBytes(encoded1); // ******************************************************************* // encrypt packet, add IV to encryption though // ******************************************************************* leadingbuf.writeByte(0xC0 | PacketTags.SYM_ENC_INTEGRITY_PRO); this.writePacketLength(leadingbuf, 0); // 0 = we don't know leadingbuf.writeByte(1); // version number String cName = PGPUtil.getSymmetricCipherName(algorithm) + "/CFB/NoPadding"; DefaultJcaJceHelper helper = new DefaultJcaJceHelper(); cipher = helper.createCipher(cName); byte[] iv = new byte[cipher.getBlockSize()]; cipher.init(Cipher.ENCRYPT_MODE, PGPUtil.makeSymmetricKey(algorithm, key), new IvParameterSpec(iv)); // ******************** start encryption ********************** // --- encrypt checksum for encrypt packet, part of the encrypted output --- byte[] inLineIv = new byte[cipher.getBlockSize() + 2]; rand.nextBytes(inLineIv); inLineIv[inLineIv.length - 1] = inLineIv[inLineIv.length - 3]; inLineIv[inLineIv.length - 2] = inLineIv[inLineIv.length - 4]; encbuf.writeBytes(inLineIv); System.out.println("bytes written a: " + encbuf.readableBytes()); // --- data packet --- int chunkpos = 0; int headerlen = 1 // format + 1 // name length + encName.length // file name + 4; // time encbuf.writeByte(0xC0 | PacketTags.LITERAL_DATA); int packetsize = 512 - headerlen; if (fileData.length - chunkpos < packetsize) { packetsize = fileData.length - chunkpos; this.writePacketLength(encbuf, headerlen + packetsize); } else { encbuf.writeByte(0xE9); // 512 packet length } System.out.println("bytes written b: " + encbuf.readableBytes()); encbuf.writeByte(PGPLiteralData.BINARY); // data format encbuf.writeByte((byte)encName.length); // file name encbuf.writeBytes(encName); encbuf.writeInt((int) (modificationTime / 1000)); // mod time System.out.println("bytes written c: " + encbuf.readableBytes()); encbuf.writeBytes(fileData, chunkpos, packetsize); System.out.println("bytes written d: " + encbuf.readableBytes()); chunkpos += packetsize; // write one or more literal packets while (chunkpos < fileData.length) { packetsize = 512; // check if this is the final packet if (fileData.length - chunkpos <= packetsize) { packetsize = fileData.length - chunkpos; this.writePacketLength(encbuf, packetsize); } else { encbuf.writeByte(0xE9); // full 512 packet length } encbuf.writeBytes(fileData, chunkpos, packetsize); chunkpos += packetsize; } // protection packet encbuf.writeByte(0xC0 | PacketTags.MOD_DETECTION_CODE); encbuf.writeByte(20); // packet length MessageDigest md = MessageDigest.getInstance("SHA-1"); md.update(encbuf.array(), encbuf.arrayOffset(), encbuf.writerIndex()); byte[] rv = md.digest(); encbuf.writeBytes(rv); System.out.println("Pre-Encrypted Hex"); this.hexDump(encbuf.array(), encbuf.arrayOffset(), encbuf.writerIndex()); System.out.println(); System.out.println(); // ***** encryption data ready ********* byte[] encdata = cipher.doFinal(encbuf.array(), encbuf.arrayOffset(), encbuf.writerIndex()); // add encrypted data to main buffer leadingbuf.writeBytes(encdata); System.out.println("Final Hex"); this.hexDump(leadingbuf.array(), leadingbuf.arrayOffset(), leadingbuf.writerIndex()); System.out.println(); System.out.println(); // write to file dest.write(leadingbuf.array(), leadingbuf.arrayOffset(), leadingbuf.writerIndex()); dest.flush(); dest.close(); } @SuppressWarnings("resource") public void test1(String srcpath, String destpath, String keyring) throws Exception { Path src = Paths.get(srcpath); // file data byte[] story = Files.readAllBytes(src); // dest OutputStream dest = new BufferedOutputStream(new FileOutputStream(destpath)); // encryption key PGPPublicKey pubKey = null; InputStream keyIn = new BufferedInputStream(new FileInputStream(keyring)); PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(org.bouncycastle.openpgp.PGPUtil.getDecoderStream(keyIn), new JcaKeyFingerprintCalculator()); // // 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. // @SuppressWarnings("rawtypes") Iterator keyRingIter = pgpPub.getKeyRings(); while (keyRingIter.hasNext() && (pubKey == null)) { PGPPublicKeyRing keyRing = (PGPPublicKeyRing)keyRingIter.next(); @SuppressWarnings("rawtypes") Iterator keyIter = keyRing.getPublicKeys(); while (keyIter.hasNext() && (pubKey == null)) { PGPPublicKey key = (PGPPublicKey)keyIter.next(); if (key.isEncryptionKey()) pubKey = key; } } if (pubKey == null) throw new IllegalArgumentException("Can't find encryption key in key ring."); String fileName = src.getFileName().toString(); byte[] encName = Utf8Encoder.encode(fileName); long modificationTime = System.currentTimeMillis(); SecureRandom rand = new SecureRandom(); int algorithm = PGPEncryptedData.AES_256; Cipher cipher = null; ByteBuf leadingbuf = Hub.instance.getBufferAllocator().heapBuffer(1024 * 1024); // 1 mb ByteBuf encbuf = Hub.instance.getBufferAllocator().heapBuffer(1024 * 1024); // 1 mb // ******************************************************************* // public key packet // ******************************************************************* PGPKeyEncryptionMethodGenerator method = new JcePublicKeyKeyEncryptionMethodGenerator(pubKey); byte[] key = org.bouncycastle.openpgp.PGPUtil.makeRandomKey(algorithm, rand); byte[] sessionInfo = new byte[key.length + 3]; // add algorithm sessionInfo[0] = (byte) algorithm; // add key System.arraycopy(key, 0, sessionInfo, 1, key.length); // add checksum int check = 0; for (int i = 1; i != sessionInfo.length - 2; i++) check += sessionInfo[i] & 0xff; sessionInfo[sessionInfo.length - 2] = (byte)(check >> 8); sessionInfo[sessionInfo.length - 1] = (byte)(check); ContainedPacket packet1 = method.generate(algorithm, sessionInfo); byte[] encoded1 = packet1.getEncoded(); leadingbuf.writeBytes(encoded1); // ******************************************************************* // encrypt packet, add IV to // ******************************************************************* String cName = PGPUtil.getSymmetricCipherName(algorithm) + "/CFB/NoPadding"; DefaultJcaJceHelper helper = new DefaultJcaJceHelper(); cipher = helper.createCipher(cName); byte[] iv = new byte[cipher.getBlockSize()]; cipher.init(Cipher.ENCRYPT_MODE, PGPUtil.makeSymmetricKey(algorithm, key), new IvParameterSpec(iv)); // ******************** start encryption ********************** // --- encrypt checksum for encrypt packet, part of the encrypted output --- byte[] inLineIv = new byte[cipher.getBlockSize() + 2]; rand.nextBytes(inLineIv); inLineIv[inLineIv.length - 1] = inLineIv[inLineIv.length - 3]; inLineIv[inLineIv.length - 2] = inLineIv[inLineIv.length - 4]; encbuf.writeBytes(inLineIv); // --- data packet --- encbuf.writeByte(0xC0 | PacketTags.LITERAL_DATA); this.writePacketLength(encbuf, 1 // format + 1 // name length + encName.length // file name + 4 // time + story.length // data ); encbuf.writeByte(PGPLiteralData.BINARY); encbuf.writeByte((byte)encName.length); encbuf.writeBytes(encName); encbuf.writeInt((int) (modificationTime / 1000)); encbuf.writeBytes(story); // protection packet encbuf.writeByte(0xC0 | PacketTags.MOD_DETECTION_CODE); encbuf.writeByte(20); // packet length MessageDigest md = MessageDigest.getInstance("SHA-1"); md.update(encbuf.array(), encbuf.arrayOffset(), encbuf.writerIndex()); byte[] rv = md.digest(); encbuf.writeBytes(rv); System.out.println("Encrypted Hex"); this.hexDump(encbuf.array(), encbuf.arrayOffset(), encbuf.writerIndex()); System.out.println(); System.out.println(); // ***** encryption data ready ********* byte[] encdata = cipher.doFinal(encbuf.array(), encbuf.arrayOffset(), encbuf.writerIndex()); leadingbuf.writeByte(0xC0 | PacketTags.SYM_ENC_INTEGRITY_PRO); /* this.writePacketLength(leadingbuf, 1 // version + encdata.length // encrypted data ); */ this.writePacketLength(leadingbuf, 0); // 0 = we don't know leadingbuf.writeByte(1); // version number // add encrypted data to main buffer leadingbuf.writeBytes(encdata); System.out.println("Final Hex"); this.hexDump(leadingbuf.array(), leadingbuf.arrayOffset(), leadingbuf.writerIndex()); System.out.println(); System.out.println(); // write to file dest.write(leadingbuf.array(), leadingbuf.arrayOffset(), leadingbuf.writerIndex()); dest.flush(); dest.close(); } public void hexDump(byte[] array, int offset, int length) { int d = 0; for (int i = 0; i < length; i++) { System.out.print(HexUtil.charToHex(array[offset + i]) + " "); d++; if (d == 32) { System.out.println(); d = 0; } } } public void writePacketLength(ByteBuf out, int bodyLen) throws IOException { if (bodyLen < 192) { out.writeByte(bodyLen); } else if (bodyLen <= 8383) { bodyLen -= 192; int oct1 = ((bodyLen >> 8) & 0xff) + 192; System.out.print("packet length: " + HexUtil.charToHex(oct1) + " " + HexUtil.charToHex(bodyLen)); System.out.println(); out.writeByte(oct1); out.writeByte(bodyLen); } else { out.writeByte(0xff); out.writeByte(bodyLen >> 24); out.writeByte(bodyLen >> 16); out.writeByte(bodyLen >> 8); out.writeByte(bodyLen); } } /* * Reverse length: * if (newPacket) { tag = hdr & 0x3f; int l = this.read(); if (l < 192) { bodyLen = l; } else if (l <= 223) { int b = in.read(); bodyLen = ((l - 192) << 8) + (b) + 192; } else if (l == 255) { bodyLen = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read(); } else { partial = true; bodyLen = 1 << (l & 0x1f); } } else { int lengthType = hdr & 0x3; tag = (hdr & 0x3f) >> 2; switch (lengthType) { case 0: bodyLen = this.read(); break; case 1: bodyLen = (this.read() << 8) | this.read(); break; case 2: bodyLen = (this.read() << 24) | (this.read() << 16) | (this.read() << 8) | this.read(); break; case 3: partial = true; break; default: throw new IOException("unknown length type encountered"); } } * */ }