How to encrypt a string using public key cryptography

I am trying to implement my own RSA encryption engine. Given these RSA parameters :

p = 61. // A prime number.
q = 53. // Also a prime number.
n = 3233. // p * q.
totient = 3120. // (p - 1) * (q - 1)
e = 991. // Co-prime to the totient (co-prime to 3120).
d = 1231. // d * e = 1219921, which is equal to the relation where 1 + k * totient = 1219921 when k = 391.

      

I'm trying to write a method to encrypt every byte in a string and return back the encrypted string:

public string Encrypt(string m, Encoding encoding)
{
    byte[] bytes = encoding.GetBytes(m);
    for (int i = 0; i < bytes.Length; i++)
    {
        bytes[i] = (byte)BigInteger.ModPow(bytes[i], e, n);
    }
    string encryptedString = encoding.GetString(bytes);
    Console.WriteLine("Encrypted {0} as {1}.", m, encryptedString);
    return encryptedString;
}

      

The obvious problem is that it BigInteger.ModPow(bytes[i], e, n)

might be too large to fit into byte space; this can result in values ​​over 8 bits. How do I get around this problem while still being able to decrypt the encrypted byte string back to a regular string?

Update : even encrypting from byte [] to byte [], you reach a case where encrypting that byte using the RSA algorithm goes beyond the byte size:

public byte[] Encrypt(string m, Encoding encoding)
{
    byte[] bytes = encoding.GetBytes(m);
    for (int i = 0; i < bytes.Length; i++)
    {
        bytes[i] = (byte)BigInteger.ModPow(bytes[i], e, n);
    }
    return bytes;
}

      

Update . My problem is that encryption will result in more bytes than the original input string:

public byte[] Encrypt(string m, Encoding encoding)
{
    byte[] bytes = encoding.GetBytes(m);
    byte[] returnBytes = new byte[0];
    for (int i = 0; i < bytes.Length; i++)
    {
        byte[] result = BigInteger.ModPow(bytes[i], (BigInteger)e, n).ToByteArray();
        int preSize = returnBytes.Length;
        Array.Resize(ref returnBytes, returnBytes.Length + result.Length);
        result.CopyTo(returnBytes, preSize);
    }
    return returnBytes;
}

public string Decrypt(byte[] c, Encoding encoding)
{
    byte[] returnBytes = new byte[0];
    for (int i = 0; i < c.Length; i++)
    {
        byte[] result = BigInteger.ModPow(c[i], d, n).ToByteArray();
        int preSize = returnBytes.Length;
        Array.Resize(ref returnBytes, returnBytes.Length + result.Length);
        result.CopyTo(returnBytes, preSize);
    }
    string decryptedString = encoding.GetString(returnBytes);
    return decryptedString;
}

      

If you run this code like this:

byte[] encryptedBytes = engine.Encrypt("Hello, world.", Encoding.UTF8);
Console.WriteLine(engine.Decrypt(encryptedBytes, Encoding.UTF8));

      

The output will be as follows:

?♥D
?♥→☻►♦→☻►♦oD♦8? ?♠oj?♠→☻►♦;♂?♠♂♠?♠

      

Obviously the output is not the original string, because I can't just try to decrypt every byte at a time, as sometimes two or more cypher-text bytes represent the integer value I need to decrypt to one byte of the original string ... so I want to know what is the standard mechanism for handling this.

+1


source to share


3 answers


Note. I have updated this answer. Please scroll down the list before how this should be implemented, because this first way to do it is not the correct way to encrypt RSA.

One way I can think it is (but may not be standards compliant) and also note that this is not a pad:

public byte[] Encrypt(string m, Encoding encoding)
{
    byte[] bytes = encoding.GetBytes(m);
    byte[] returnBytes = new byte[0];
    for (int i = 0; i < bytes.Length; i++)
    {
        byte[] result = BigInteger.ModPow(bytes[i], (BigInteger)e, n).ToByteArray();
        int preSize = returnBytes.Length;
        Array.Resize(ref returnBytes, returnBytes.Length + result.Length + 1);
        (new byte[] { (byte)(result.Length) }).CopyTo(returnBytes, preSize);
        result.CopyTo(returnBytes, preSize + 1);
    }
    return returnBytes;
}

public string Decrypt(byte[] c, Encoding encoding)
{
    byte[] returnBytes = new byte[0];
    for (int i = 0; i < c.Length; i++)
    {
        int dataLength = (int)c[i];
        byte[] result = new byte[dataLength];
        for (int j = 0; j < dataLength; j++)
        {
            i++;
            result[j] = c[i];
        }
        BigInteger integer = new BigInteger(result);
        byte[] integerResult = BigInteger.ModPow(integer, d, n).ToByteArray();
        int preSize = returnBytes.Length;
        Array.Resize(ref returnBytes, returnBytes.Length + integerResult.Length);
        integerResult.CopyTo(returnBytes, preSize);
    }
    string decryptedString = encoding.GetString(returnBytes);
    return decryptedString;
}

      

It can be cross-platform because you have the ability to use a different datatype to represent e or n and pass it to a C # helper service like this one. Here's a test:

string stringToEncrypt = "Mary had a little lamb.";
Console.WriteLine("Encrypting the string: {0}", stringToEncrypt);
byte[] encryptedBytes = engine.Encrypt(stringToEncrypt, Encoding.UTF8);
Console.WriteLine("Encrypted text: {0}", Encoding.UTF8.GetString(encryptedBytes));
Console.WriteLine("Decrypted text: {0}", engine.Decrypt(encryptedBytes, Encoding.UTF8));

      

Output:

Encrypting the string: Mary had a little lamb.
Encrypted text: ☻6☻1♦☻j☻☻&♀☻g♦☻t☻☻1♦☻?  ☻g♦☻1♦☻g♦☻?♥☻?☻☻7☺☻7☺☻?♥☻?♂☻g♦☻?♥☻1♦☻$☺☻
c       ☻?☻
Decrypted text: Mary had a little lamb.

      



Update . Everything I said earlier is completely wrong in the RSA implementation. Wrong, wrong, wrong! This is the correct way to encrypt RSA:

  • Convert the string to BigInteger data type.
  • Make sure your integer is less than the n value you calculated for your algorithm, otherwise you won't be able to decrypt it.
  • Encrypt an integer. RSA only works for integer encryption. This is clear.
  • Decrypt it from an encrypted whole.
  • I can't help but wonder that the BigInteger class was primarily created for cryptography.

As an example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace BytePadder
{
    class Program
    {
        const int p = 61;
        const int q = 53;
        const int n = 3233;
        const int totient = 3120;
        const int e = 991;
        const int d = 1231;

        static void Main(string[] args)
        {
            // ---------------------- RSA Example I ----------------------
            // Shows how an integer gets encrypted and decrypted.
            BigInteger integer = 1000;
            BigInteger encryptedInteger = Encrypt(integer);
            Console.WriteLine("Encrypted Integer: {0}", encryptedInteger);
            BigInteger decryptedInteger = Decrypt(encryptedInteger);
            Console.WriteLine("Decrypted Integer: {0}", decryptedInteger);
            // --------------------- RSA Example II ----------------------
            // Shows how a string gets encrypted and decrypted.
            string unencryptedString = "A";
            BigInteger integer2 = new BigInteger(Encoding.UTF8.GetBytes(unencryptedString));
            Console.WriteLine("String as Integer: {0}", integer2);
            BigInteger encryptedInteger2 = Encrypt(integer2);
            Console.WriteLine("String as Encrypted Integer: {0}", encryptedInteger2);
            BigInteger decryptedInteger2 = Decrypt(encryptedInteger2);
            Console.WriteLine("String as Decrypted Integer: {0}", decryptedInteger2);
            string decryptedIntegerAsString = Encoding.UTF8.GetString(decryptedInteger2.ToByteArray());
            Console.WriteLine("Decrypted Integer as String: {0}", decryptedIntegerAsString);
            Console.ReadLine();
        }

        static BigInteger Encrypt(BigInteger integer)
        {
            if (integer < n)
            {
                return BigInteger.ModPow(integer, e, n);
            }
            throw new Exception("The integer must be less than the value of n in order to be decypherable!");
        }

        static BigInteger Decrypt(BigInteger integer)
        {
            return BigInteger.ModPow(integer, d, n);
        }
    }
}

      

Output example:

Encrypted Integer: 1989
Decrypted Integer: 1000
String as Integer: 65
String as Encrypted Integer: 1834
String as Decrypted Integer: 65
Decrypted Integer as String: A

      

0


source


Your basic code for encrypting and decrypting every byte - the challenge ModPow

- works, but you go on to say that "splitting the message and encrypting every part" is irrelevant.

To show that the part ModPow

- that is, the math - is okay, here's a code based on yours that encrypts string

up BigInteger[]

and back:

using System;
using System.Linq;
using System.Numerics;
using System.Text;

class Test
{
    const int p = 61;
    const int q = 53;
    const int n = 3233;
    const int totient = 3120;
    const int e = 991;
    const int d = 1231;

    static void Main()
    {
        var encrypted = Encrypt("Hello, world.", Encoding.UTF8);
        var decrypted = Decrypt(encrypted, Encoding.UTF8);
        Console.WriteLine(decrypted);
    }

    static BigInteger[] Encrypt(string text, Encoding encoding)
    {
        byte[] bytes = encoding.GetBytes(text);
        return bytes.Select(b => BigInteger.ModPow(b, (BigInteger)e, n))
                    .ToArray();
    }

    static string Decrypt(BigInteger[] encrypted, Encoding encoding)
    {
        byte[] bytes = encrypted.Select(bi => (byte) BigInteger.ModPow(bi, d, n))
                                .ToArray();
        return encoding.GetString(bytes);
    }
}

      



Next, you need to know more about how is byte[]

encrypted in another byte[]

using RSA, including all the different padding schemes, etc. There's a lot more to it than just a call ModPow

for each byte.

But to reiterate, you don't have to do this in order to eventually implement RSA. The chances of you doing this without any security flaws are really very small. It's fine to do this for academic interest to learn more about the principles of cryptography, but leave the real implementation to the experts. (I am far from an expert in this area - I would not implement my own encryption ...)

+2


source


If you want to use RSA encryption in C #, you shouldn't try to create your own. For starters, the primes you chose are probably small. P and Q must be large first numbers.

You should check other questions / answers:

how to use RSA to encrypt files (huge data) in c #

RSA Big Data Encryption in C #

And other links: http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.encrypt(v=vs.110).aspx

http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx

0


source







All Articles