divconq.test.pgp.PGPWriter2.java Source code

Java tutorial

Introduction

Here is the source code for divconq.test.pgp.PGPWriter2.java

Source

/* ************************************************************************
#
#  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");
        }
    }
     * 
     */
}