Use BigInteger

Diffie-Hellman
Diffie-Hellman in C#
using System;
using System.Numerics;

public static class DiffieHellman
{
    public static BigInteger PrivateKey(BigInteger p) =>
        new(Random.Shared.NextInt64(1, (long)p));

    public static BigInteger PublicKey(BigInteger p, BigInteger g, BigInteger privateKey) =>
        BigInteger.ModPow(g, privateKey, p);

    public static BigInteger Secret(BigInteger p, BigInteger publicKey, BigInteger privateKey) =>
        BigInteger.ModPow(publicKey, privateKey, p);
}

Generate random key

The first thing to tackle is to generate a random private key. For that, the Random class can be used, which has a Next() method to generate a random number within a range (lower bound inclusive, upper bound exclusive).

public static BigInteger PrivateKey(BigInteger p)
{
    return Random.Shared.NextInt64(1, (long)p);
}

This will generate a number >= 1 and < p.

Note

The Random.Shared instance if guaranteed to be thread-safe, so is usually preferrable over creating your own Random instance.

Calculate public key and secret

Conveniently, the public key and the secret can both be calculated using just one method: BigInteger.ModPow():

public static BigInteger PublicKey(BigInteger p, BigInteger g, BigInteger privateKey) =>
    BigInteger.ModPow(g, privateKey, p);

public static BigInteger Secret(BigInteger p, BigInteger publicKey, BigInteger privateKey) =>
    BigInteger.ModPow(publicKey, privateKey, p);

And that's it!

Shortening

There are two things we can do to further shorten this method:

  1. Remove the curly braces by converting to an expression-bodied method
  2. Use a target-typed new expression to replace new string with just new (the compiler can figure out the type from the method's return type)

Using this, we end up with:

public static BigInteger PrivateKey(BigInteger p) => new(Random.Shared.Next(1, (int) p - 1));

public static BigInteger PublicKey(BigInteger p, BigInteger g, BigInteger privateKey) =>
    BigInteger.ModPow(g, privateKey, p);

public static BigInteger Secret(BigInteger p, BigInteger publicKey, BigInteger privateKey) =>
    BigInteger.ModPow(publicKey, privateKey, p);
24th Apr 2024 · Found it useful?