org.ourfilesystem.security.SecurityTools.java Source code

Java tutorial

Introduction

Here is the source code for org.ourfilesystem.security.SecurityTools.java

Source

package org.ourfilesystem.security;

/*
OurFileSystem is a peer2peer file sharing program.
Copyright (C) 2012  Robert Gass
    
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    
*/

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Date;

import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.ourfilesystem.db.so.Network;
import org.ourfilesystem.db.so.NetworkAuthorization;
import org.ourfilesystem.db.so.Peer;
import org.ourfilesystem.db.so.Post;
import org.ourfilesystem.db.so.PostMessage;
import org.ourfilesystem.db.so.PostTemplate;
import org.ourfilesystem.db.so.PublicPost;
import org.ourfilesystem.db.so.Subscribe;
import org.ourfilesystem.utilities.BBytes;
import org.ourfilesystem.utilities.ByteCounter;
import org.ourfilesystem.utilities.FileUtils;

public class SecurityTools {

    /*
     * SecureRandom should be thread safe. 
     */
    public static SecureRandom Random = new SecureRandom();

    public static AsymmetricCipherKeyPair generateKeyPair() {
        /*
         * The probability that the new BigInteger represents a prime number will exceed (1 - 1/(2^certainty))
         * 2^40 > 1T 
         */
        RSAKeyGenerationParameters parms = new RSAKeyGenerationParameters(BigInteger.valueOf(65537), // exponent
                Random, // Random generator
                2048, // Key size 
                40); // Certainty
        RSAKeyPairGenerator gen = new RSAKeyPairGenerator();
        gen.init(parms);
        return gen.generateKeyPair();
    }

    public static KeySet generateKeySet() {
        AsymmetricCipherKeyPair encpair = SecurityTools.generateKeyPair();
        AsymmetricCipherKeyPair signpair = SecurityTools.generateKeyPair();
        KeySet ks = new KeySet();
        ks.setPrivateEncryptionKey(encpair.getPrivate());
        ks.setPrivateSigningKey(signpair.getPrivate());
        PublicKeySet pub = new PublicKeySet();
        pub.setPublicEncryptionKey(encpair.getPublic());
        pub.setPublicSigningKey(signpair.getPublic());
        ks.setPublicKeySet(pub);
        return ks;
    }

    public static void writeRSAPublicKey(RSAKeyParameters p, OutputStream fos, ByteCounter c) throws IOException {
        byte exp[] = p.getExponent().toByteArray();
        byte mod[] = p.getModulus().toByteArray();
        FileUtils.writeBytes(fos, exp, c);
        FileUtils.writeBytes(fos, mod, c);
    }

    public static RSAKeyParameters readRSAPublicKey(InputStream fis, ByteCounter c) throws IOException {
        byte[] exp = FileUtils.readBytes(fis, c);
        byte[] mod = FileUtils.readBytes(fis, c);
        RSAKeyParameters pub = new RSAKeyParameters(false, //boolean isPrivate,            
                new BigInteger(mod), //java.math.BigInteger modulus, 
                new BigInteger(exp)); //java.math.BigInteger exponent 
        return pub;
    }

    public static void writeRSAPrivateKey(RSAPrivateCrtKeyParameters p, OutputStream fos, ByteCounter c)
            throws IOException {
        FileUtils.writeBytes(fos, p.getDP().toByteArray(), c);
        FileUtils.writeBytes(fos, p.getDQ().toByteArray(), c);
        FileUtils.writeBytes(fos, p.getExponent().toByteArray(), c);
        FileUtils.writeBytes(fos, p.getModulus().toByteArray(), c);
        FileUtils.writeBytes(fos, p.getP().toByteArray(), c);
        FileUtils.writeBytes(fos, p.getPublicExponent().toByteArray(), c);
        FileUtils.writeBytes(fos, p.getQ().toByteArray(), c);
        FileUtils.writeBytes(fos, p.getQInv().toByteArray(), c);
    }

    public static RSAPrivateCrtKeyParameters readRSAPrivateKey(InputStream fis, ByteCounter c) throws IOException {
        byte[] dp = FileUtils.readBytes(fis, c);
        byte[] dq = FileUtils.readBytes(fis, c);
        byte[] exp = FileUtils.readBytes(fis, c);
        byte[] mod = FileUtils.readBytes(fis, c);
        byte[] p = FileUtils.readBytes(fis, c);
        byte[] pe = FileUtils.readBytes(fis, c);
        byte[] q = FileUtils.readBytes(fis, c);
        byte[] qi = FileUtils.readBytes(fis, c);
        RSAPrivateCrtKeyParameters priv = new RSAPrivateCrtKeyParameters(new BigInteger(mod), //java.math.BigInteger modulus,          
                new BigInteger(pe), //java.math.BigInteger publicExponent,   
                new BigInteger(exp), //java.math.BigInteger privateExponent,  
                new BigInteger(p), //java.math.BigInteger p,                
                new BigInteger(q), //java.math.BigInteger q,                
                new BigInteger(dp), //java.math.BigInteger dP,               
                new BigInteger(dq), //java.math.BigInteger dQ,               
                new BigInteger(qi)); //java.math.BigInteger qInv              
        return priv;
    }

    public static void writePublicKeySet(PublicKeySet k, OutputStream fos, ByteCounter c) throws IOException {
        writeRSAPublicKey((RSAKeyParameters) k.getPublicEncryptionKey(), fos, c);
        writeRSAPublicKey((RSAKeyParameters) k.getPublicSigningKey(), fos, c);
    }

    public static PublicKeySet readPublicKeySet(InputStream fis, ByteCounter c) throws IOException {
        PublicKeySet p = new PublicKeySet();
        p.setPublicEncryptionKey(readRSAPublicKey(fis, c));
        p.setPublicSigningKey(readRSAPublicKey(fis, c));
        return p;
    }

    public static void writeSignedDigest(SignedDigest s, OutputStream fos, ByteCounter c) throws IOException {
        FileUtils.writeBytes(fos, ((BBytes) s.getDigest()).getBytes(), c);
        if (s.getPeerIdentifier() == null) {
            fos.write(2);
            c.add(1);
        } else {
            fos.write(1);
            c.add(1);
            FileUtils.writeBytes(fos, ((BBytes) s.getPeerIdentifier()).getBytes(), c);
        }
        FileUtils.writeBytes(fos, ((BBytes) s.getSignature()).getBytes(), c);
    }

    public static SignedDigest readSignedDigest(InputStream fis, ByteCounter c) throws IOException {
        SignedDigest s = new SignedDigest();
        s.setDigest(new BBytes(FileUtils.readBytes(fis, c)));
        int v = fis.read();
        c.add(1);
        if (v == 2) {
            s.setPeerIdentifier(null);
        } else {
            s.setPeerIdentifier(new BBytes(FileUtils.readBytes(fis, c)));
        }
        s.setSignature(new BBytes(FileUtils.readBytes(fis, c)));
        return s;
    }

    public static void writeKeySet(KeySet k, OutputStream fos, ByteCounter c) throws IOException {
        writeRSAPrivateKey((RSAPrivateCrtKeyParameters) k.getPrivateEncryptionKey(), fos, c);
        writeRSAPrivateKey((RSAPrivateCrtKeyParameters) k.getPrivateSigningKey(), fos, c);
        writePublicKeySet(k.getPublicKeySet(), fos, c);
    }

    public static KeySet readKeySet(InputStream fis, ByteCounter c) throws IOException {
        KeySet k = new KeySet();
        k.setPrivateEncryptionKey(readRSAPrivateKey(fis, c));
        k.setPrivateSigningKey(readRSAPrivateKey(fis, c));
        k.setPublicKeySet(readPublicKeySet(fis, c));
        return k;
    }

    public static void DigestRSAPublicKey(Digest dig, RSAKeyParameters pub) {
        byte[] a = null;
        a = pub.getExponent().toByteArray();
        dig.update(a, 0, a.length);
        a = pub.getModulus().toByteArray();
        dig.update(a, 0, a.length);
    }

    public static byte[] digestPublicKey(PublicKeySet k) {
        SHA512Digest dig = new SHA512Digest();
        DigestRSAPublicKey(dig, (RSAKeyParameters) k.getPublicEncryptionKey());
        DigestRSAPublicKey(dig, (RSAKeyParameters) k.getPublicSigningKey());
        byte sig[] = new byte[dig.getDigestSize()];
        dig.doFinal(sig, 0);
        return sig;
    }

    public static void genIdentity(Peer p) {
        byte pdig[] = digestPublicKey(p.getPeerKeys());
        BBytes pbb = new BBytes(pdig);
        p.setIdentity(pbb);
    }

    public static boolean verifyIdentity(Peer p) {
        byte pdig[] = digestPublicKey(p.getPeerKeys());
        BBytes pbb = new BBytes(pdig);
        return pbb.equals(p.getIdentity());
    }

    public static SignedDigest signDigest(byte dig[], BBytes peerid, RSAPrivateCrtKeyParameters key) {
        SignedDigest s = new SignedDigest();
        RSAEngine eng = new RSAEngine();
        byte pai[] = peerid.getBytes();
        byte signblock[] = new byte[dig.length + pai.length];
        System.arraycopy(dig, 0, signblock, 0, dig.length);
        System.arraycopy(pai, 0, signblock, dig.length, pai.length);
        PKCS1Encoding enc = new PKCS1Encoding(eng);
        enc.init(true, key);
        try {
            byte sig[] = enc.processBlock(signblock, 0, signblock.length);
            s.setDigest(new BBytes(dig));
            s.setPeerIdentifier(peerid);
            s.setSignature(new BBytes(sig));
            return s;
        } catch (InvalidCipherTextException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }

    public static boolean verifySignedDigest(SignedDigest s, byte checkdig[], PublicKeySet pub) {
        if (!Arrays.equals(((BBytes) s.getDigest()).getBytes(), checkdig)) {
            return false;
        }
        RSAEngine eng = new RSAEngine();
        PKCS1Encoding enc = new PKCS1Encoding(eng);
        enc.init(false, (CipherParameters) pub.getPublicSigningKey());
        byte sig[] = ((BBytes) s.getSignature()).getBytes();
        try {
            byte decsig[] = enc.processBlock(sig, 0, sig.length);
            byte encdig[] = new byte[checkdig.length];
            System.arraycopy(decsig, 0, encdig, 0, encdig.length);
            byte encpeer[] = new byte[decsig.length - checkdig.length];
            System.arraycopy(decsig, checkdig.length, encpeer, 0, encpeer.length);
            byte[] genpeer = digestPublicKey(pub);
            boolean signmatch = Arrays.equals(encdig, checkdig);
            boolean idmatch = Arrays.equals(genpeer, encpeer);
            return signmatch && idmatch;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public static BBytes digestFile(File f, long offset, long size) throws IOException {
        SHA512Digest d = new SHA512Digest();
        digestFile(d, f, offset, size);
        byte sig[] = new byte[d.getDigestSize()];
        d.doFinal(sig, 0);
        return new BBytes(sig);
    }

    public static void digestFile(Digest d, File f, long offset, long size) throws IOException {
        FileInputStream fis = new FileInputStream(f);
        fis.skip(offset);
        byte buf[] = new byte[1024];
        int isize = Integer.MAX_VALUE;
        if (size < (long) isize) {
            isize = (int) size;
        }
        int len = fis.read(buf, 0, Math.min(buf.length, isize));
        while (len != -1 && size > 0) {
            size -= len;
            if (len > 0) {
                d.update(buf, 0, len);
            }
            isize = Integer.MAX_VALUE;
            if (size < (long) isize) {
                isize = (int) size;
            }
            len = fis.read(buf, 0, Math.min(buf.length, isize));
        }
        fis.close();
    }

    public static void digestLong(Digest d, long val) {
        byte b[] = new byte[Long.SIZE / Byte.SIZE];
        ByteBuffer buf = ByteBuffer.wrap(b);
        buf.putLong(val);
        d.update(b, 0, b.length);
    }

    public static void digestDouble(Digest d, double db) {
        byte b[] = new byte[Double.SIZE / Byte.SIZE];
        ByteBuffer buf = ByteBuffer.wrap(b);
        buf.putDouble(db);
        d.update(b, 0, b.length);
    }

    public static void digestString(Digest d, String str) {
        if (str != null) {
            byte strb[] = str.getBytes(Charset.forName("UTF-16BE"));
            d.update(strb, 0, strb.length);
        } else {
            d.update((byte) 0);
        }
    }

    public static void digestBBytes(Digest d, BBytes b) {
        if (b != null) {
            byte bb[] = b.getBytes();
            d.update(bb, 0, bb.length);
        } else {
            d.update((byte) 0);
        }
    }

    public static void digestBoolean(Digest d, boolean b) {
        if (b) {
            d.update((byte) 1);
        } else {
            d.update((byte) 0);
        }
    }

    public static void digestDate(Digest d, Date dt) {
        if (dt == null) {
            d.update((byte) 2);
        } else {
            d.update((byte) 1);
            digestLong(d, dt.getTime());
        }
    }

    public static BBytes digestPeerLocation(Peer p) {
        SHA512Digest d = new SHA512Digest();
        digestString(d, p.getIntroduction());
        digestString(d, (String) p.getLocation());
        digestString(d, (String) p.getNickname());
        digestLong(d, p.getUpdateCount());
        digestDate(d, p.getRDate());
        byte id[] = p.getIdentity().getBytes();
        d.update(id, 0, id.length);
        byte sig[] = new byte[d.getDigestSize()];
        d.doFinal(sig, 0);
        return new BBytes(sig);
    }

    public static void digestPostTemplate(Digest d, PostTemplate p) {
        if (p == null) {
            d.update((byte) 2);
        } else {
            for (int c = 0; c < 10; c++) {
                String s = p.getBool(c);
                digestString(d, s);
            }
            for (int c = 0; c < 10; c++) {
                String s = p.getDouble(c);
                digestString(d, s);
            }
            for (int c = 0; c < 10; c++) {
                String s = p.getNum(c);
                digestString(d, s);
            }
            for (int c = 0; c < 3; c++) {
                String s = p.getRef(c);
                digestString(d, s);
            }
            for (int c = 0; c < 8; c++) {
                String s = p.getString(c);
                digestString(d, s);
            }
            digestString(d, p.getTemplateName());
            digestString(d, p.getTemplateDescription());
        }
    }

    public static void digestPostMessage(Digest d, PostMessage m) {
        if (m == null) {
            d.update((byte) 2);
        } else {
            d.update((byte) 1);
            digestString(d, m.getComment());
            digestString(d, m.getString0());
            digestString(d, m.getString1());
            digestString(d, m.getString2());
            digestString(d, m.getString3());
            digestString(d, m.getString4());
            digestString(d, m.getString5());
            digestString(d, m.getString6());
            digestString(d, m.getString7());
            digestString(d, m.getFileName());
            digestString(d, m.getSubject());
            digestDouble(d, m.getDouble0());
            digestDouble(d, m.getDouble1());
            digestDouble(d, m.getDouble2());
            digestDouble(d, m.getDouble3());
            digestDouble(d, m.getDouble4());
            digestDouble(d, m.getDouble5());
            digestDouble(d, m.getDouble6());
            digestDouble(d, m.getDouble7());
            digestDouble(d, m.getDouble8());
            digestDouble(d, m.getDouble9());
            digestLong(d, m.getNum0());
            digestLong(d, m.getNum1());
            digestLong(d, m.getNum2());
            digestLong(d, m.getNum3());
            digestLong(d, m.getNum4());
            digestLong(d, m.getNum5());
            digestLong(d, m.getNum6());
            digestLong(d, m.getNum7());
            digestLong(d, m.getNum8());
            digestLong(d, m.getNum9());
            digestBBytes(d, m.getRef0());
            digestBBytes(d, m.getRef1());
            digestBBytes(d, m.getRef2());
            digestBBytes(d, m.getUseTemplate());
            digestBoolean(d, m.isBool0());
            digestBoolean(d, m.isBool1());
            digestBoolean(d, m.isBool2());
            digestBoolean(d, m.isBool3());
            digestBoolean(d, m.isBool4());
            digestBoolean(d, m.isBool5());
            digestBoolean(d, m.isBool6());
            digestBoolean(d, m.isBool7());
            digestBoolean(d, m.isBool8());
            digestBoolean(d, m.isBool9());
        }
    }

    public static BBytes digestPost(Post p, BBytes peerid) {
        SHA512Digest d = new SHA512Digest();
        digestBBytes(d, p.getFileReferenceDigest());
        digestBBytes(d, p.getNetworkId());
        digestLong(d, p.getPostNumber());
        if (p.getMessage() != null && p.getMessage() instanceof PostMessage) {
            PostMessage pm = (PostMessage) p.getMessage();
            digestPostMessage(d, pm);
        } else if (p.getMessage() != null && p.getMessage() instanceof PostTemplate) {
            PostTemplate pt = (PostTemplate) p.getMessage();
            digestPostTemplate(d, pt);
        } else if (p.getMessage() == null) {
            d.update((byte) 0);
        } else {
            throw new RuntimeException("Unknown message type!");
        }
        digestBoolean(d, p.isPosterHasFile());
        digestBBytes(d, peerid);
        digestDate(d, p.getRDate());
        byte sig[] = new byte[d.getDigestSize()];
        d.doFinal(sig, 0);
        return new BBytes(sig);
    }

    public static BBytes digestPublicPost(PublicPost p, BBytes peerid) {
        SHA512Digest d = new SHA512Digest();
        digestBBytes(d, p.getNetworkId());
        digestLong(d, p.getPostNumber());
        digestBoolean(d, p.isEncrypted());
        if (p.getMessage() instanceof BBytes) {
            BBytes mb = (BBytes) p.getMessage();
            digestBBytes(d, mb);
        } else {
            String str = (String) p.getMessage();
            digestString(d, str);
        }
        digestBBytes(d, peerid);
        digestDate(d, p.getRDate());
        byte sig[] = new byte[d.getDigestSize()];
        d.doFinal(sig, 0);
        return new BBytes(sig);
    }

    public static BBytes digestSubscribe(Subscribe s, BBytes peerid) {
        SHA512Digest d = new SHA512Digest();
        digestBBytes(d, s.getNetworkID());
        digestDate(d, s.getRDate());
        digestLong(d, s.getSubNumber());
        digestBBytes(d, peerid);
        digestBoolean(d, s.isSubscribe());
        byte sig[] = new byte[d.getDigestSize()];
        d.doFinal(sig, 0);
        return new BBytes(sig);
    }

    public static void signSubscriber(Subscribe s, BBytes peerid, RSAPrivateCrtKeyParameters key) {
        BBytes dig = digestSubscribe(s, peerid);
        s.setSignature(signDigest(dig.getBytes(), peerid, key));
    }

    public static boolean verifySubscriber(Subscribe s, PublicKeySet pub) {
        BBytes dig = digestSubscribe(s, s.getSignature().getPeerIdentifier());
        return verifySignedDigest(s.getSignature(), dig.getBytes(), pub);
    }

    public static BBytes digestNetwork(Network n, BBytes peerid) {
        SHA512Digest d = new SHA512Digest();
        digestString(d, n.getDescription());
        digestLong(d, n.getNetworkNumber());
        digestString(d, n.getTitle());
        digestBBytes(d, peerid);
        digestDate(d, n.getRDate());
        if (n.getPublic() != null) {
            digestBoolean(d, n.getPublic());
        }
        byte sig[] = new byte[d.getDigestSize()];
        d.doFinal(sig, 0);
        return new BBytes(sig);
    }

    public static void signNetwork(Network n, BBytes peerid, RSAPrivateCrtKeyParameters key) {
        BBytes dig = digestNetwork(n, peerid);
        n.setSignature(signDigest(dig.getBytes(), peerid, key));
    }

    public static boolean verifyNetwork(Network n, PublicKeySet pub) {
        BBytes dig = digestNetwork(n, n.getSignature().getPeerIdentifier());
        return verifySignedDigest(n.getSignature(), dig.getBytes(), pub);
    }

    public static void signPublicPost(PublicPost p, BBytes peerid, RSAPrivateCrtKeyParameters key) {
        BBytes dig = digestPublicPost(p, peerid);
        p.setSignature(signDigest(dig.getBytes(), peerid, key));
    }

    public static boolean verifyPublicPost(PublicPost p, PublicKeySet pub) {
        BBytes dig = digestPublicPost(p, (BBytes) p.getSignature().getPeerIdentifier());
        return verifySignedDigest(p.getSignature(), dig.getBytes(), pub);
    }

    public static void signPost(Post p, BBytes peerid, RSAPrivateCrtKeyParameters key) {
        BBytes dig = digestPost(p, peerid);
        p.setSignedDigest(signDigest(dig.getBytes(), peerid, key));
    }

    public static boolean verifyPost(Post p, PublicKeySet pub) {
        BBytes dig = digestPost(p, (BBytes) p.getSignedDigest().getPeerIdentifier());
        return verifySignedDigest(p.getSignedDigest(), dig.getBytes(), pub);
    }

    public static boolean verifyPeerIdentity(Peer p) {
        byte cdig[] = digestPublicKey(p.getPeerKeys());
        return Arrays.equals(cdig, p.getIdentity().getBytes());
    }

    public static void signNetworkAuth(NetworkAuthorization a, BBytes peerid, RSAPrivateCrtKeyParameters key) {
        SHA512Digest d = new SHA512Digest();
        byte b[] = a.getNetworkId().getBytes();
        d.update(b, 0, b.length);
        b = a.getPeerId().getBytes();
        d.update(b, 0, b.length);
        digestLong(d, a.getSignatureNumber());
        digestDate(d, a.getRDate());
        byte sig[] = new byte[d.getDigestSize()];
        d.doFinal(sig, 0);
        a.setSignature(signDigest(sig, peerid, key));
    }

    public static boolean verifyNetworkAuth(NetworkAuthorization a, PublicKeySet pub) {
        SHA512Digest d = new SHA512Digest();
        byte b[] = a.getNetworkId().getBytes();
        d.update(b, 0, b.length);
        b = a.getPeerId().getBytes();
        d.update(b, 0, b.length);
        digestLong(d, a.getSignatureNumber());
        digestDate(d, a.getRDate());
        byte sig[] = new byte[d.getDigestSize()];
        d.doFinal(sig, 0);
        return verifySignedDigest(a.getSignature(), sig, pub);
    }

    public static void signPeerLocation(Peer p, RSAPrivateCrtKeyParameters key) {
        BBytes dig = SecurityTools.digestPeerLocation(p);
        SignedDigest sg = SecurityTools.signDigest(dig.getBytes(), p.getIdentity(),
                (RSAPrivateCrtKeyParameters) key);
        p.setLocationSignature(sg);
    }

    public static boolean verifyPeerLocation(Peer p, PublicKeySet key) {
        BBytes dig = SecurityTools.digestPeerLocation(p);
        return verifySignedDigest(p.getLocationSignature(), dig.getBytes(), key);
    }

    public static String decodePublicPost(BBytes enc, RSAPrivateCrtKeyParameters key) {
        try {
            byte encdata[] = enc.getBytes();

            //Extract the header
            ByteBuffer bbuf = ByteBuffer.wrap(encdata);
            int headlen = bbuf.getInt();
            if (headlen > 4096) {
                throw new Exception("Header is too long: " + headlen);
            }
            byte head[] = new byte[headlen];
            System.arraycopy(encdata, Integer.SIZE / Byte.SIZE, head, 0, headlen);

            //Decode the header
            RSAEngine eng = new RSAEngine();
            PKCS1Encoding eng2 = new PKCS1Encoding(eng);
            eng2.init(false, key);

            byte dechead[] = eng2.processBlock(head, 0, head.length);
            if (dechead.length < (2 * Long.SIZE / Byte.SIZE)) {
                //Magic number can't be there, not enough data.
                return null;
            }
            ByteBuffer mbuf = ByteBuffer.wrap(dechead);
            long m0 = mbuf.getLong();
            long m1 = mbuf.getLong();
            if (m0 != 0x01234567) {
                return null;
            }
            if (m1 != 0x76543210) {
                return null;
            }
            //Exctract the symmetric key.
            byte skey[] = new byte[dechead.length - (2 * Long.SIZE / Byte.SIZE)];
            System.arraycopy(dechead, 2 * Long.SIZE / Byte.SIZE, skey, 0, skey.length);

            KeyParameter kp = new KeyParameter(skey);

            CBCBlockCipher aes = new CBCBlockCipher(new AESEngine());
            PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(aes, new PKCS7Padding());
            cipher.init(false, kp);

            int foffset = (Integer.SIZE / Byte.SIZE) + headlen;
            int enclen = encdata.length - foffset;
            byte[] output = new byte[cipher.getOutputSize(enclen)];
            int len = cipher.processBytes(encdata, foffset, enclen, output, 0);
            int len2 = cipher.doFinal(output, len);

            byte rawdata[] = new byte[len + len2];

            System.arraycopy(output, 0, rawdata, 0, rawdata.length);

            return new String(rawdata, Charset.forName("UTF-16BE"));
        } catch (Exception e) {
            //e.printStackTrace();         
        }
        return null;
    }

    public static BBytes encodePublicPost(String raw, PublicKeySet pubkey) {
        try {
            byte strb[] = raw.getBytes(Charset.forName("UTF-16BE"));

            byte[] key = new byte[32];
            Random.nextBytes(key);
            KeyParameter kp = new KeyParameter(key);

            CBCBlockCipher aes = new CBCBlockCipher(new AESEngine());
            PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(aes, new PKCS7Padding());
            cipher.init(true, kp);

            byte[] output = new byte[cipher.getOutputSize(strb.length)];
            int len = cipher.processBytes(strb, 0, strb.length, output, 0);
            cipher.doFinal(output, len);

            RSAEngine eng = new RSAEngine();

            byte headblk[] = new byte[(2 * Long.SIZE / Byte.SIZE) + key.length];
            ByteBuffer bbuf = ByteBuffer.wrap(headblk);
            bbuf.putLong(0x01234567); //magic number
            bbuf.putLong(0x76543210); //magic number
            System.arraycopy(key, 0, headblk, (2 * Long.SIZE / Byte.SIZE), key.length);

            PKCS1Encoding enc = new PKCS1Encoding(eng);
            enc.init(true, (CipherParameters) pubkey.getPublicEncryptionKey());

            byte enchead[] = enc.processBlock(headblk, 0, headblk.length);

            byte rslt[] = new byte[(Integer.SIZE / Byte.SIZE) + enchead.length + output.length];

            ByteBuffer rbuf = ByteBuffer.wrap(rslt);
            rbuf.putInt(enchead.length);
            System.arraycopy(enchead, 0, rslt, Integer.SIZE / Byte.SIZE, enchead.length);
            System.arraycopy(output, 0, rslt, (Integer.SIZE / Byte.SIZE) + enchead.length, output.length);

            BBytes orslt = new BBytes(rslt);
            return orslt;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}