2

I want to write a complete diffie Hellman example for bouncy castle that includes key generation, key exchange, encryption, and decryption. I also want to verify that if Alice is initiating a connection to Bob, that she should send her public key, Parameter P, and Parameter G.

I posted a Code Review. However, that is more about style, syntax, and grammar. I'm still trying to make sure my logic is correct.

This is also a good reference.

Namespaces:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Agreement;
using Org.BouncyCastle.Crypto.Agreement.Kdf;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Asn1.X509;
using System.Diagnostics;
using Org.BouncyCastle.OpenSsl;

Some constants for this test case:

const string Algorithm = "ECDH"; //What do you think about the other algorithms?
const int KeyBitSize = 256;
const int NonceBitSize = 128;
const int MacBitSize = 128;
const int DefaultPrimeProbability = 30;

Main method that starts the pairing and handles verifying encryption:

public static void TestMethod() {
    //BEGIN SETUP ALICE
    IAsymmetricCipherKeyPairGenerator aliceKeyGen = GeneratorUtilities.GetKeyPairGenerator (Algorithm);
    DHParametersGenerator aliceGenerator = new DHParametersGenerator ();
    aliceGenerator.Init (KeyBitSize, DefaultPrimeProbability, new SecureRandom ());
    DHParameters aliceParameters = aliceGenerator.GenerateParameters ();

    KeyGenerationParameters aliceKGP = new DHKeyGenerationParameters (new SecureRandom (), aliceParameters);
    aliceKeyGen.Init (aliceKGP);

    AsymmetricCipherKeyPair aliceKeyPair = aliceKeyGen.GenerateKeyPair ();
    IBasicAgreement aliceKeyAgree = AgreementUtilities.GetBasicAgreement (Algorithm);
    aliceKeyAgree.Init (aliceKeyPair.Private);

    //END SETUP ALICE

    /////AT THIS POINT, Alice's Public Key, Alice's Parameter P and Alice's Parameter G are sent unsecure to BOB

    //BEGIN SETUP BOB
    IAsymmetricCipherKeyPairGenerator bobKeyGen = GeneratorUtilities.GetKeyPairGenerator (Algorithm);
    DHParameters bobParameters = new DHParameters( aliceParameters.P, aliceParameters.G );

    KeyGenerationParameters bobKGP = new DHKeyGenerationParameters (new SecureRandom (), bobParameters);
    bobKeyGen.Init (bobKGP);

    AsymmetricCipherKeyPair bobKeyPair = bobKeyGen.GenerateKeyPair ();
    IBasicAgreement bobKeyAgree = AgreementUtilities.GetBasicAgreement (Algorithm);
    bobKeyAgree.Init (bobKeyPair.Private);
    //END SETUP BOB

    BigInteger aliceAgree = aliceKeyAgree.CalculateAgreement (bobKeyPair.Public);
    BigInteger bobAgree = bobKeyAgree.CalculateAgreement (aliceKeyPair.Public);

    if (!aliceAgree.Equals (bobAgree)) {
        throw new Exception ("Keys do not match.");
    }

    byte[] nonSecretMessage = GetBytes ("HeaderMessageForASDF");
    byte[] secretMessage = GetBytes ("Secret message contents");
    byte[] decNonSecretBytes;

    KeyParameter sharedKey = new KeyParameter (aliceAgree.ToByteArrayUnsigned ());

    var encMessage = EncryptMessage( sharedKey, nonSecretMessage, secretMessage );
    var decMessage = DecryptMessage( sharedKey, encMessage, out decNonSecretBytes );

    var decNonSecretMessage = GetString( decNonSecretBytes );
    var decSecretMessage = GetString( decMessage );

    Debug.WriteLine( decNonSecretMessage + " - " + decSecretMessage );

    return;
}

Wrapper method to Encrypt a message with given data:

public static byte[] EncryptMessage (string sharedKey, string nonSecretMessage, string secretMessage)
{
    return EncryptMessage( new KeyParameter( Convert.FromBase64String( sharedKey ) ), GetBytes( nonSecretMessage ), GetBytes( secretMessage ) );
}

Helper method to Encrypt a message with given data

public static byte[] EncryptMessage( KeyParameter sharedKey, byte[] nonSecretMessage, byte[] secretMessage ) {
    if( nonSecretMessage != null && nonSecretMessage.Length > 255 ) throw new Exception( "Non Secret Message Too Long!" );
    byte nonSecretLength = nonSecretMessage == null ? (byte)0 : (byte)nonSecretMessage.Length;

    var nonce = new byte[NonceBitSize / 8];
    var rand = new SecureRandom();
    rand.NextBytes(nonce, 0, nonce.Length);

    var cipher = new GcmBlockCipher(new AesFastEngine());
    var aeadParameters = new AeadParameters(sharedKey, MacBitSize, nonce, nonSecretMessage );
    cipher.Init(true, aeadParameters);

    //Generate Cipher Text With Auth Tag
    var cipherText = new byte[cipher.GetOutputSize(secretMessage.Length)];
    var len = cipher.ProcessBytes(secretMessage, 0, secretMessage.Length, cipherText, 0);
    cipher.DoFinal(cipherText, len);

    using (var combinedStream = new MemoryStream())
    {
        using (var binaryWriter = new BinaryWriter(combinedStream))
        {
            //Prepend Authenticated Payload
            binaryWriter.Write(nonSecretLength);
            binaryWriter.Write(nonSecretMessage);

            //Prepend Nonce
            binaryWriter.Write(nonce);
            //Write Cipher Text
            binaryWriter.Write(cipherText);
        }
        return combinedStream.ToArray();
    }
}        

Wrapper method to Decrypt a message

public static string DecryptMessage (string sharedKey, byte[] encryptedMessage, out string nonSecretPayload)
{
    byte[] nonSecretPayloadBytes;
    byte[] payload = DecryptMessage( new KeyParameter( Convert.FromBase64String( sharedKey ) ), encryptedMessage, out nonSecretPayloadBytes );

    nonSecretPayload = GetString( nonSecretPayloadBytes );
    return GetString( payload );
}

Helper method to decrypt a message

public static byte[] DecryptMessage( KeyParameter sharedKey, byte[] encryptedMessage, out byte[] nonSecretPayloadBytes )
{
    using (var cipherStream = new MemoryStream(encryptedMessage))
    using (var cipherReader = new BinaryReader(cipherStream))
    {
        //Grab Payload
        int nonSecretLength = (int)cipherReader.ReadByte();
        nonSecretPayloadBytes = cipherReader.ReadBytes(nonSecretLength);

        //Grab Nonce
        var nonce = cipherReader.ReadBytes(NonceBitSize / 8);

        var cipher = new GcmBlockCipher(new AesFastEngine());
        var parameters = new AeadParameters(sharedKey, MacBitSize, nonce, nonSecretPayloadBytes);
        cipher.Init(false, parameters);

        //Decrypt Cipher Text
        var cipherText = cipherReader.ReadBytes(encryptedMessage.Length - nonSecretLength - nonce.Length);
        var plainText = new byte[cipher.GetOutputSize(cipherText.Length)];  

        try
        {
            var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0);
            cipher.DoFinal(plainText, len);
        }
        catch (InvalidCipherTextException)
        {
            //Return null if it doesn't authenticate
            return null;
        }

        return plainText;
    }
}

Returns byte data for a given string:

static byte[] GetBytes(string str)
{
    if( str == null ) return null;
    return System.Text.Encoding.Unicode.GetBytes( str );
}

Returns a string for given byte data:

static string GetString(byte[] bytes)
{
    if( bytes == null ) return null;
    return System.Text.Encoding.Unicode.GetString( bytes, 0, bytes.Length );
}
Community
  • 1
  • 1
texel
  • 184
  • 9
  • So what is the question? Do you expect us to copy this all into a program and verify it for you? Have you tried doing unit testing? – Ron Beyer Nov 19 '15 at 19:54
  • I've read it twice, but what is your question? – slava Nov 19 '15 at 19:58
  • The program works, in the sense that you can run it and it will generate a shared key and use it to encrypt and decrypt a message. I'm concerned I'm not using the library correctly or as securely as I should be (The process took a while for me to get right). Can someone confirm that if Alice is initiating a connection to Bob, that she should send her public key, Parameter P, and Parameter G? – texel Nov 19 '15 at 22:13

0 Answers0