7
\$\begingroup\$

The following is a (somewhat messy) implementation of the BouncyCastle cryptography library. I create a self-signed certificate (hopefully correctly), store it in a Java KeyStore, and use it to create an SSL (TLSv1.2) socket session. I am not concerned about efficiency, as this was mainly a proof of concept code to familiarize myself with the library and handle data cryptography in Java.

I mainly want this code to be reviewed from a security standpoint, and whether I am making safe habits or not. If there are any major errors in either security or code usage, feedback would be appreciated.

Main (Used for testing)

//Copyright (C) Leejae Karinja 2016 

import java.net.Inet4Address;

public class Main {

    public static void main(String[] args) {
        new Thread() {

            public void run() {
                Server s = new Server(13579);
                s.start();
                s.sendData("Hello, World!".getBytes());
                s.stop();
            }
        }.start();
        new Thread() {

            public void run() {
                try {
                    Client c = new Client(Inet4Address.getLocalHost().getHostAddress(), 13579);
                    c.start();
                    System.out.println(new String(c.receiveData()));
                    c.stop();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
        return;
    }
}

Server

//Copyright (C) Leejae Karinja 2016 

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

public class Server {

    private int port;
    private byte[] nonce;
    private byte[] keyData;
    private SSLSocket client;
    private SSLServerSocket server;
    private DataOutputStream writer;
    private DataInputStream reader;

    private static final String PROVIDER_NAME = BouncyCastleProvider.PROVIDER_NAME;
    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    private static final String SIGNATURE_ALGORITHM = "SHA512withECDSA";
    private static final String KEY_GENERATION_ALGORITHM = "ECDH";
    private static final String SSL_CONTEXT = "TLSv1.2";
    private static final String KEY_STORE_INSTANCE = "JKS";
    private static final String KMF_INSTANCE = "PKIX";
    private static final Date BEFORE = new Date(System.currentTimeMillis() - 5000);
    private static final Date AFTER = new Date(System.currentTimeMillis() + 600000);

    /**
     * Default constructor
     */
    Server() {

    }

    /**
     * Constructor with specified port
     * 
     * @param port Port to start the server on
     */
    Server(int port) {
        this.port = port;
    }

    /**
     * Generates a one time use secure random number to be used as the password
     * for a keystore
     * 
     * @return Returns void on completion
     */
    private void genNonce() {
        SecureRandom rand = new SecureRandom();
        this.nonce = new byte[2048];
        rand.nextBytes(nonce);
        return;
    }

    /**
     * Generates a one time use keystore for use with an SSL session
     * 
     * @return Returns void on completion
     */
    private void genKeystore() {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_GENERATION_ALGORITHM, PROVIDER_NAME);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();

            X509Certificate cert = createCACert(keyPair.getPublic(), keyPair.getPrivate());

            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(null, (new String(this.nonce)).toCharArray());
            byte[] tempPass = new byte[2048];
            new SecureRandom().nextBytes(tempPass);
            ks.setKeyEntry("foo.bar", keyPair.getPrivate(), new String(tempPass).toCharArray(), new java.security.cert.Certificate[] { cert });
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ks.store(os, (new String(this.nonce)).toCharArray());
            this.keyData = os.toByteArray();
            //System.out.println("Server Key Data: " + new String(this.keyData));
            //System.out.println("Server Public Cert Key: " + cert.getPublicKey());
            //System.out.println("Server Public Key: " + keyPair.getPublic());
            //System.out.println("Server Private Key: " + keyPair.getPrivate());
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return;
    }

    /**
     * Create a certificate to use by a Certificate Authority
     * 
     * Retrieved from http://www.programcreek.com/java-api-examples/index.php?class=org.bouncycastle.cert.X509v3CertificateBuilder&method=addExtension
     * 
     * @param publicKey Public key
     * @param privateKey Private key
     * @return Generated X509 Certificate
     */
    private X509Certificate createCACert(PublicKey publicKey, PrivateKey privateKey) throws Exception {
        X500Name issuerName = new X500Name("CN=127.0.0.1, O=FOO, L=BAR, ST=BAZ, C=QUX");

        X500Name subjectName = issuerName;

        BigInteger serial = BigInteger.valueOf(new SecureRandom().nextInt());

        X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(issuerName, serial, BEFORE, AFTER, subjectName, publicKey);
        builder.addExtension(Extension.subjectKeyIdentifier, false, createSubjectKeyIdentifier(publicKey));
        builder.addExtension(Extension.basicConstraints, true, new BasicConstraints(true));

        KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.dataEncipherment | KeyUsage.cRLSign);
        builder.addExtension(Extension.keyUsage, false, usage);

        ASN1EncodableVector purposes = new ASN1EncodableVector();
        purposes.add(KeyPurposeId.id_kp_serverAuth);
        purposes.add(KeyPurposeId.id_kp_clientAuth);
        purposes.add(KeyPurposeId.anyExtendedKeyUsage);
        builder.addExtension(Extension.extendedKeyUsage, false, new DERSequence(purposes));

        X509Certificate cert = signCertificate(builder, privateKey);
        cert.checkValidity(new Date());
        cert.verify(publicKey);

        return cert;
    }

    /**
     * Helper method
     * 
     * Retrieved from http://www.programcreek.com/java-api-examples/index.php?api=org.bouncycastle.cert.bc.BcX509ExtensionUtils
     * 
     * @param key
     * @return
     * @throws Exception
     */
    private static SubjectKeyIdentifier createSubjectKeyIdentifier(Key key) throws Exception {
        ASN1InputStream is = new ASN1InputStream(new ByteArrayInputStream(key.getEncoded()));
        ASN1Sequence seq = (ASN1Sequence) is.readObject();
        is.close();
        @SuppressWarnings("deprecation")
        SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(seq);
        return new BcX509ExtensionUtils().createSubjectKeyIdentifier(info);
    }

    /**
     * Helper method
     * 
     * Retrieved from http://www.programcreek.com/java-api-examples/index.php?source_dir=mockserver-master/mockserver-core/src/main/java/org/mockserver/socket/KeyStoreFactory.java
     * 
     * @param certificateBuilder
     * @param signedWithPrivateKey
     * @return
     * @throws Exception
     */
    private static X509Certificate signCertificate(X509v3CertificateBuilder certificateBuilder, PrivateKey signedWithPrivateKey) throws Exception {
        ContentSigner signer = new JcaContentSignerBuilder(SIGNATURE_ALGORITHM).setProvider(PROVIDER_NAME).build(signedWithPrivateKey);
        return new JcaX509CertificateConverter().setProvider(PROVIDER_NAME).getCertificate(certificateBuilder.build(signer));
    }

    /**
     * Sets the port for the server to start on
     * 
     * @param port Port to start the server on
     * @return Returns void on completion
     */
    public void setPort(int port) {
        this.port = port;
        return;
    }

    /**
     * Starts the SSL session
     * 
     * @return Returns void on completion
     */
    protected void start() {
        try {
            if (this.nonce == null) this.genNonce();
            if (this.keyData == null) this.genKeystore();

            if (this.server == null) this.startConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return;
    }

    /**
     * Stopps the SSL session
     * 
     * @return Returns void on completion
     */
    protected void stop() {
        this.nonce = null;
        this.keyData = null;
        this.endConnection();
        return;
    }

    /**
     * Creates an SSL session
     * 
     * @return Returns void on completion
     */
    private void startConnection() {
        try {
            KeyStore ks = KeyStore.getInstance(KEY_STORE_INSTANCE);
            ks.load(new ByteArrayInputStream(this.keyData), (new String(this.nonce).toCharArray()));

            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KMF_INSTANCE);
            kmf.init(ks, (new String(this.nonce).toCharArray()));

            SSLContext context = SSLContext.getInstance(SSL_CONTEXT);
            context.init(kmf.getKeyManagers(), null, new SecureRandom());

            SSLServerSocketFactory ssf = context.getServerSocketFactory();

            this.server = (SSLServerSocket) ssf.createServerSocket(this.port);
            this.server.setEnabledCipherSuites(this.server.getSupportedCipherSuites());//new String[]{CIPHER});
            this.server.setEnabledProtocols(this.server.getSupportedProtocols());

            this.client = (SSLSocket) this.server.accept();
            this.client.startHandshake();

            this.writer = new DataOutputStream(this.client.getOutputStream());
            this.reader = new DataInputStream(this.client.getInputStream());

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

    /**
     * Ends the SSL session
     * 
     * @return Returns void on completion
     */
    private void endConnection() {
        try {
            this.writer.close();
            this.reader.close();
            this.server.close();
            this.client.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return;
    }

    /**
     * Sends data to the client
     * 
     * @param data Data to send to the client as a byte array
     * @return Returns void on completion
     */
    public void sendData(byte[] data) {
        try {
            int x = 0;
            for (x = 0; x < data.length; x++) {
                this.writer.writeByte(data[x]);
            }
            this.writer.writeByte(-1);
            this.writer.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return;
    }

    /**
     * Receives data from the client
     * 
     * @return Returns data received from the client as a byte array
     */
    public byte[] receiveData() {
        try {
            List<Byte> dataRead = new ArrayList<Byte>();
            byte temp = this.reader.readByte();
            while (temp != (-1)) {
                dataRead.add(temp);
                temp = this.reader.readByte();
            }
            Byte[] data = new Byte[dataRead.size()];
            data = dataRead.toArray(data);
            byte[] dataFinal = new byte[data.length];
            int x = 0;
            for (x = 0; x < data.length; x++) {
                dataFinal[x] = data[x].byteValue();
            }
            return dataFinal;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

Client

//Copyright (C) Leejae Karinja 2016

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

public class Client {

    private String host;
    private int port;
    private byte[] nonce;
    private byte[] keyData;
    private SSLSocket server;
    private DataOutputStream writer;
    private DataInputStream reader;

    private static final String PROVIDER_NAME = BouncyCastleProvider.PROVIDER_NAME;
    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    private static final String SIGNATURE_ALGORITHM = "SHA512withECDSA";
    private static final String KEY_GENERATION_ALGORITHM = "ECDH";
    private static final String SSL_CONTEXT = "TLSv1.2";
    private static final String KEY_STORE_INSTANCE = "JKS";
    private static final String KMF_INSTANCE = "PKIX";
    private static final Date BEFORE = new Date(System.currentTimeMillis() - 5000);
    private static final Date AFTER = new Date(System.currentTimeMillis() + 600000);

    /**
     * Default constructor
     */
    Client() {

    }

    /**
     * Constructor with specified host and port
     * 
     * @param host Host to connect to
     * @param port Port to connect to
     */
    Client(String host, int port) {
        this.host = host;
        this.port = port;
    }

    /**
     * Generates a one time use secure random number to be used as the password
     * for a keystore
     * 
     * @return Returns void on completion
     */
    private void genNonce() {
        SecureRandom rand = new SecureRandom();
        this.nonce = new byte[2048];
        rand.nextBytes(nonce);
        return;
    }

    /**
     * Generates a one time use keystore for use with an SSL session
     * 
     * @return Returns void on completion
     */
    private void genKeystore() {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_GENERATION_ALGORITHM, PROVIDER_NAME);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();

            X509Certificate cert = createCACert(keyPair.getPublic(), keyPair.getPrivate());

            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(null, (new String(this.nonce)).toCharArray());
            byte[] tempPass = new byte[2048];
            new SecureRandom().nextBytes(tempPass);
            ks.setKeyEntry("foo.bar", keyPair.getPrivate(), new String(tempPass).toCharArray(), new java.security.cert.Certificate[] { cert });
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ks.store(os, (new String(this.nonce)).toCharArray());
            this.keyData = os.toByteArray();
            //System.out.println("Client Key Data: " + new String(this.keyData));
            //System.out.println("Client Public Cert Key: " + cert.getPublicKey());
            //System.out.println("Client Public Key: " + keyPair.getPublic());
            //System.out.println("Client Private Key: " + keyPair.getPrivate());
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return;
    }

    /**
     * Create a certificate to use by a Certificate Authority
     * 
     * Retrieved from http://www.programcreek.com/java-api-examples/index.php?class=org.bouncycastle.cert.X509v3CertificateBuilder&method=addExtension
     * 
     * @param publicKey Public key
     * @param privateKey Private key
     * @return Generated X509 Certificate
     */
    private X509Certificate createCACert(PublicKey publicKey, PrivateKey privateKey) throws Exception {
        X500Name issuerName = new X500Name("CN=127.0.0.1, O=FOO, L=BAR, ST=BAZ, C=QUX");

        X500Name subjectName = issuerName;

        BigInteger serial = BigInteger.valueOf(new SecureRandom().nextInt());

        X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(issuerName, serial, BEFORE, AFTER, subjectName, publicKey);
        builder.addExtension(Extension.subjectKeyIdentifier, false, createSubjectKeyIdentifier(publicKey));
        builder.addExtension(Extension.basicConstraints, true, new BasicConstraints(true));

        KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.dataEncipherment | KeyUsage.cRLSign);
        builder.addExtension(Extension.keyUsage, false, usage);

        ASN1EncodableVector purposes = new ASN1EncodableVector();
        purposes.add(KeyPurposeId.id_kp_serverAuth);
        purposes.add(KeyPurposeId.id_kp_clientAuth);
        purposes.add(KeyPurposeId.anyExtendedKeyUsage);
        builder.addExtension(Extension.extendedKeyUsage, false, new DERSequence(purposes));

        X509Certificate cert = signCertificate(builder, privateKey);
        cert.checkValidity(new Date());
        cert.verify(publicKey);

        return cert;
    }

    /**
     * Helper method
     * 
     * Retrieved from http://www.programcreek.com/java-api-examples/index.php?api=org.bouncycastle.cert.bc.BcX509ExtensionUtils
     * 
     * @param key
     * @return
     * @throws Exception
     */
    private static SubjectKeyIdentifier createSubjectKeyIdentifier(Key key) throws Exception {
        ASN1InputStream is = new ASN1InputStream(new ByteArrayInputStream(key.getEncoded()));
        ASN1Sequence seq = (ASN1Sequence) is.readObject();
        is.close();
        @SuppressWarnings("deprecation")
        SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(seq);
        return new BcX509ExtensionUtils().createSubjectKeyIdentifier(info);
    }

    /**
     * Helper method
     * 
     * Retrieved from http://www.programcreek.com/java-api-examples/index.php?source_dir=mockserver-master/mockserver-core/src/main/java/org/mockserver/socket/KeyStoreFactory.java
     * 
     * @param certificateBuilder
     * @param signedWithPrivateKey
     * @return
     * @throws Exception
     */
    private static X509Certificate signCertificate(X509v3CertificateBuilder certificateBuilder, PrivateKey signedWithPrivateKey) throws Exception {
        ContentSigner signer = new JcaContentSignerBuilder(SIGNATURE_ALGORITHM).setProvider(PROVIDER_NAME).build(signedWithPrivateKey);
        return new JcaX509CertificateConverter().setProvider(PROVIDER_NAME).getCertificate(certificateBuilder.build(signer));
    }

    /**
     * Sets the host to connect to
     * 
     * @param host Host to connect to
     * @return Returns void on completion
     */
    public void setHost(String host) {
        this.host = host;
        return;
    }

    /**
     * Sets the port to connect to
     * 
     * @param port Port to connect to
     * @return Returns void on completion
     */
    public void setPort(int port) {
        this.port = port;
        return;
    }

    /**
     * Starts the SSL session
     * 
     * @return Returns void on completion
     */
    protected void start() {
        try {
            if (this.nonce == null) this.genNonce();
            if (this.keyData == null) this.genKeystore();

            if (this.server == null) this.startConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return;
    }

    /**
     * Stopps the SSL session
     * 
     * @return Returns void on completion
     */
    protected void stop() {
        this.nonce = null;
        this.keyData = null;
        this.endConnection();
        return;
    }

    /**
     * Creates an SSL session
     * 
     * @return Returns void on completion
     */
    private void startConnection() {
        try {
            KeyStore ks = KeyStore.getInstance(KEY_STORE_INSTANCE);
            ks.load(new ByteArrayInputStream(this.keyData), (new String(this.nonce).toCharArray()));

            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KMF_INSTANCE);
            kmf.init(ks, (new String(this.nonce).toCharArray()));

            SSLContext context = SSLContext.getInstance(SSL_CONTEXT);
            context.init(kmf.getKeyManagers(), null, new SecureRandom());

            SSLSocketFactory sf = context.getSocketFactory();

            this.server = (SSLSocket) sf.createSocket(this.host, this.port);
            this.server.setEnabledCipherSuites(this.server.getSupportedCipherSuites());
            this.server.setEnabledProtocols(this.server.getSupportedProtocols());
            this.server.startHandshake();

            this.writer = new DataOutputStream(this.server.getOutputStream());
            this.reader = new DataInputStream(this.server.getInputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return;
    }

    /**
     * Ends the SSL session
     * 
     * @return Returns void on completion
     */
    private void endConnection() {
        try {
            this.writer.close();
            this.reader.close();
            this.server.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return;
    }

    /**
     * Sends data to the server
     * 
     * @param data Data to send to the server as a byte array
     * @return Returns void on completion
     */
    public void sendData(byte[] data) {
        try {
            int x = 0;
            for (x = 0; x < data.length; x++) {
                this.writer.writeByte(data[x]);
            }
            this.writer.writeByte(-1);
            this.writer.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return;
    }

    /**
     * Receives data from the server
     * 
     * @return Returns data received from the server as a byte array
     */
    public byte[] receiveData() {
        try {
            List<Byte> dataRead = new ArrayList<Byte>();
            byte temp = this.reader.readByte();
            while (temp != (-1)) {
                dataRead.add(temp);
                temp = this.reader.readByte();
            }
            Byte[] data = new Byte[dataRead.size()];
            data = dataRead.toArray(data);
            byte[] dataFinal = new byte[data.length];
            int x = 0;
            for (x = 0; x < data.length; x++) {
                dataFinal[x] = data[x].byteValue();
            }
            return dataFinal;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$
  • In general just using printStackTrace to handle exceptions is not good practice. If you don't care about handling it, just don't catch it.
  • return at the end of a method is unnecessary.
  • For string data using plain getBytes/the plain String constructor instead of specifying e.g. UTF-8 as the encoding seems more risky for no benefit. I guess the platform default is useful in some circumstances, but sending data over the network is not one of them.
  • The port number should be a constant.
  • The default empty constructor that doesn't set the port number seems not useful.
  • Streams' close methods should be called no matter what, that is, use try/finally to ensure closing resources.

Sending the data as a sequence terminated by -1 will break immediately once you send a general purpose file, instead send the length first or escape the offending pattern in all sent data.

Also it would be good to use a shared class for the shared methods.

Unfortunately without a deep look into the documentation I can't say anything about the crypto part. You might want to ask on the Information Security site as well. In any case, if you really want to implement this yourself maybe try to run each of the client and server against a second implementation using an established library and compare.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you for your response. I have made some edits, namely code cleanup and resource handling like you suggested. Thank you for your feedback! \$\endgroup\$
    – MrPublic
    Mar 8, 2016 at 12:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.