1
\$\begingroup\$

This was my first go at any kind of encryption in any kind of programming aspect but my application needed some kind of RSA end encryption for my game packets.

Uses RSA and diffihelman and runs off 3 rsa keys, N D and E. I know it follows a lot of bad practices and doesn't have the best quality of code in the way it does what it does, that's why I've asked this question in hopes to get some sort of guidance.

public class RsaKeys
{
    public string N = "5DE90F8BEB8B72DE3DEA30CD1A8D5BC589CD4DD5FC67761BB2C4F1A3DA94CA3B61F92637A3B52AA5000E9FCB1A3FC390972418BEEB068A81CBABEE844D0879B467F4826F50D91E61B8EB9D8D5BA149073E2DEFD79DF6B023FD3DE358777350D3A8E43A18F6D5B7BD513A11C1961AA95461B32D0B95D03C07449E59F02AAB7E93";
    public string D = "3E9B5FB29D07A1E97E9C2088BC5E3D2E5BDE33E3FD9A4EBD21D8A117E70DDC2796A6197A6D2371C3555F1532117FD7B5BA18107F4759B1ABDD1D49AD88B051221E14045BA0CF694117AD2CF6285C4622CC0CF3A9311C6F56B42E9B5CB82AFF2456223BC5C3B04CB397DD82693B9A30831E180257F5FBCA7968B462181ADE211B";
    public string E = "3";
}

public class Arc4
{
    private int _i;
    private int _j;
    private readonly byte[] _bytes;

    public const int Poolsize = 256;

    public Arc4()
    {
        _bytes = new byte[Poolsize];
    }

    public Arc4(byte[] key)
    {
        _bytes = new byte[Poolsize];
        Initialize(key);
    }

    public void Initialize(byte[] key)
    {
        _i = 0;
        _j = 0;

        for (_i = 0; _i < Poolsize; ++_i)
        {
            _bytes[_i] = (byte)_i;
        }

        for (_i = 0; _i < Poolsize; ++_i)
        {
            _j = (_j + _bytes[_i] + key[_i % key.Length]) & (Poolsize - 1);
            Swap(_i, _j);
        }

        _i = 0;
        _j = 0;
    }

    private void Swap(int a, int b)
    {
        var t = _bytes[a];
        _bytes[a] = _bytes[b];
        _bytes[b] = t;
    }

    public byte Next()
    {
        _i = ++_i & (Poolsize - 1);
        _j = (_j + _bytes[_i]) & (Poolsize - 1);
        Swap(_i, _j);

        return _bytes[(_bytes[_i] + _bytes[_j]) & 255];
    }

    public void Encrypt(ref byte[] src)
    {
        for (var k = 0; k < src.Length; k++)
        {
            src[k] ^= Next();
        }
    }

    public void Decrypt(ref byte[] src)
    {
        Encrypt(ref src);
    }
}

public class DiffieHellman
{
    public readonly int BITLENGTH = 32;

    public BigInteger Prime { get; private set; }
    public BigInteger Generator { get; private set; }

    private BigInteger PrivateKey;
    public BigInteger PublicKey { get; private set; }

    public DiffieHellman()
    {
        Initialize();
    }

    public DiffieHellman(int b)
    {
        BITLENGTH = b;

        Initialize();
    }

    public DiffieHellman(BigInteger prime, BigInteger generator)
    {
        Prime = prime;
        Generator = generator;

        Initialize(true);
    }

    private void Initialize(bool ignoreBaseKeys = false)
    {
        PublicKey = 0;

        var rand = new Random();
        while (PublicKey == 0)
        {
            if (!ignoreBaseKeys)
            {
                Prime = BigInteger.genPseudoPrime(BITLENGTH, 10, rand);
                Generator = BigInteger.genPseudoPrime(BITLENGTH, 10, rand);
            }

            var bytes = new byte[BITLENGTH / 8];
            Randomizer.NextBytes(bytes);
            PrivateKey = new BigInteger(bytes);

            if (Generator > Prime)
            {
                var temp = Prime;
                Prime = Generator;
                Generator = temp;
            }

            PublicKey = Generator.modPow(PrivateKey, Prime);

            if (!ignoreBaseKeys)
            {
                break;
            }
        }
    }

    public BigInteger CalculateSharedKey(BigInteger m)
    {
        return m.modPow(PrivateKey, Prime);
    }
}

public class RsaKey
{
    public int E { get; }
    public BigInteger e { get; private set; }
    public BigInteger N { get; private set; }
    public BigInteger D { get; private set; }
    public BigInteger P { get; private set; }
    public BigInteger Q { get; private set; }
    public BigInteger Dmp1 { get; private set; }
    public BigInteger Dmq1 { get; private set; }
    public BigInteger Coeff { get; private set; }

    protected bool CanDecrypt;
    protected bool CanEncrypt;

    public RsaKey(BigInteger n, int e,
        BigInteger d,
        BigInteger p, BigInteger q,
        BigInteger dmp1, BigInteger dmq1,
        BigInteger coeff)
    {
        E = e;
        this.e = e;
        N = n;
        D = d;
        P = p;
        Q = q;
        Dmp1 = dmp1;
        Dmq1 = dmq1;
        Coeff = coeff;

        CanEncrypt = N != 0 && E != 0;
        CanDecrypt = CanEncrypt && D != 0;
    }

    public void GeneratePair(int b, BigInteger e)
    {
        this.e = e;

        var qs = b >> 1;

        while (true)
        {
            while (true)
            {
                P = BigInteger.genPseudoPrime(b - qs, 1, new Random());

                if ((P - 1).gcd(this.e) == 1 && P.isProbablePrime(10))
                {
                    break;
                }
            }

            while (true)
            {
                Q = BigInteger.genPseudoPrime(qs, 1, new Random());

                if ((Q - 1).gcd(this.e) == 1 && P.isProbablePrime(10))
                {
                    break;
                }
            }

            if (P < Q)
            {
                var t = P;
                P = Q;
                Q = t;
            }

            var phi = (P - 1) * (Q - 1);

            if (phi.gcd(this.e) != 1) continue;

            N = P * Q;
            D = this.e.modInverse(phi);
            Dmp1 = D % (P - 1);
            Dmq1 = D % (Q - 1);
            Coeff = Q.modInverse(P);
            break;
        }

        CanEncrypt = N != 0 && this.e != 0;
        CanDecrypt = CanEncrypt && D != 0;

        Console.WriteLine(N.ToString(16));
        Console.WriteLine(D.ToString(16));
    }

    public static RsaKey ParsePublicKey(string n, string e)
    {
        return new RsaKey(new BigInteger(n, 16), Convert.ToInt32(e, 16), 0, 0, 0, 0, 0, 0);
    }

    public static RsaKey ParsePrivateKey(string n, string e,
        string d,
        string p = null, string q = null,
        string dmp1 = null, string dmq1 = null,
        string coeff = null)
    {
        if (p == null)
        {
            return new RsaKey(new BigInteger(n, 16), Convert.ToInt32(e, 16), new BigInteger(d, 16), 0, 0, 0, 0, 0);
        }

        return new RsaKey(new BigInteger(n, 16), Convert.ToInt32(e, 16), new BigInteger(d, 16), new BigInteger(p, 16), new BigInteger(q, 16), 
            new BigInteger(dmp1, 16), new BigInteger(dmq1, 16), new BigInteger(coeff, 16));
    }

    public int GetBlockSize()
    {
        return (N.bitCount() + 7) / 8;
    }

    public byte[] Encrypt(byte[] src)
    {
        return DoEncrypt(DoPublic, src, Pkcs1PadType.FullByte);
    }

    public byte[] Decrypt(byte[] src)
    {
        return DoDecrypt(DoPublic, src);
    }

    public byte[] Sign(byte[] src)
    {
        return DoEncrypt(DoPrivate, src, Pkcs1PadType.FullByte);
    }

    public byte[] Verify(byte[] src)
    {
        return DoDecrypt(DoPrivate, src);
    }

    private byte[] DoEncrypt(DoCalculateionDelegate method, byte[] src, Pkcs1PadType type)
    {
        try
        {
            var bl = GetBlockSize();

            var paddedBytes = pkcs1pad(src, bl, type);
            var m = new BigInteger(paddedBytes);
            if (m == 0)
            {
                return null;
            }

            var c = method(m);
            if (c == 0)
            {
                return null;
            }

            return c.getBytes();
        }
        catch
        {
            return null;
        }
    }

    private byte[] DoDecrypt(DoCalculateionDelegate method, byte[] src)
    {
        try
        {
            var c = new BigInteger(src);
            var m = method(c);
            if (m == 0)
            {
                return null;
            }

            var bl = GetBlockSize();

            var bytes = pkcs1unpad(m.getBytes(), bl);

            return bytes;
        }
        catch
        {
            return null;
        }
    }

    private byte[] pkcs1pad(byte[] src, int n, Pkcs1PadType type)
    {
        var bytes = new byte[n];

        var i = src.Length - 1;
        while (i >= 0 && n > 11)
        {
            bytes[--n] = src[i--];
        }

        bytes[--n] = 0;
        while (n > 2)
        {
            byte x = 0;
            switch (type)
            {
                case Pkcs1PadType.FullByte: x = 0xFF; break;
                case Pkcs1PadType.RandomByte: x = Randomizer.NextByte(1, 255); break;
            }
            bytes[--n] = x;
        }

        bytes[--n] = (byte)type;
        bytes[--n] = 0;
        return bytes;
    }

    private byte[] pkcs1unpad(byte[] src, int n)
    {
        var i = 0;

        while (i < src.Length && src[i] == 0)
        {
            ++i;
        }

        if (src.Length - i != n - 1 || src[i] > 2)
        {
            Console.WriteLine("PKCS#1 unpad: i={0}, expected src[i]==[0,1,2], got src[i]={1}", i, src[i].ToString("X"));
            return null;
        }

        ++i;

        while (src[i] != 0)
        {
            if (++i >= src.Length)
            {
                Console.WriteLine("PKCS#1 unpad: i={0}, src[i-1]!=0 (={1})", i, src[i - 1].ToString("X"));
            }
        }

        var bytes = new byte[src.Length - i - 1];
        for (var p = 0; ++i < src.Length; p++)
        {
            bytes[p] = src[i];
        }

        return bytes;
    }

    protected BigInteger DoPublic(BigInteger m)
    {
        return m.modPow(E, N);
    }

    protected BigInteger DoPrivate(BigInteger m)
    {
        if (P == 0 && Q == 0)
        {
            return m.modPow(D, N);
        }
        else
        {
            return 0;
        }
    }
}

public delegate BigInteger DoCalculateionDelegate(BigInteger m);

public enum Pkcs1PadType
{
    FullByte = 1,
    RandomByte = 2
}

public static class Encryption
{
    private static RsaKey _rsa;
    private static DiffieHellman _diffieHellman;

    public static void Initialize(RsaKeys keys)
    {
        _rsa = RsaKey.ParsePrivateKey(keys.N, keys.E, keys.D);
        _diffieHellman = new DiffieHellman();
    }

    private static string GetRsaStringEncrypted(string message)
    {
        try
        {
            var m = Encoding.Default.GetBytes(message);
            var c = _rsa.Sign(m);

            return Converter.BytesToHexString(c);
        }
        catch
        {
            return "0";
        }
    }

    public static string GetRsaDiffieHellmanPrimeKey()
    {
        var key = _diffieHellman.Prime.ToString(10);
        return GetRsaStringEncrypted(key);
    }

    public static string GetRsaDiffieHellmanGeneratorKey()
    {
        var key = _diffieHellman.Generator.ToString(10);
        return GetRsaStringEncrypted(key);
    }

    public static string GetRsaDiffieHellmanPublicKey()
    {
        var key = _diffieHellman.PublicKey.ToString(10);
        return GetRsaStringEncrypted(key);
    }

    public static BigInteger CalculateDiffieHellmanSharedKey(string publicKey)
    {
        try
        {
            var cbytes = Converter.HexStringToBytes(publicKey);
            var publicKeyBytes = _rsa.Verify(cbytes);
            var publicKeyString = Encoding.Default.GetString(publicKeyBytes);
            return _diffieHellman.CalculateSharedKey(new BigInteger(publicKeyString, 10));
        }
        catch
        {
            return 0;
        }
    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ What is the intended usage? To do Diffie-Helllman key exchange with RSA-signed keys? Why? \$\endgroup\$ – 200_success Dec 1 '17 at 6:27
  • \$\begingroup\$ What is your logic in "rolling your own" encryption rather than using .NET crypto libraries? \$\endgroup\$ – Aron Dec 3 '17 at 23:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.