繁体   English   中英

如何使用公钥密码术加密字符串

[英]How to encrypt a string using public key cryptography

我正在尝试实现自己的RSA加密引擎。 给定这些RSA算法值:

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.

我正在尝试编写一种方法来加密字符串中的每个字节并返回加密的字符串:

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;
}

这里的明显问题是BigInteger.ModPow(bytes[i], e, n)可能太大而无法容纳一个字节空间。 它可能导致值超过8位。 在仍然能够将加密的字节字符串解密回常规字符串的同时,如何解决此问题?

更新 :即使从byte []到byte []进行加密,您也会遇到使用RSA算法加密该字节超出字节大小限制的情况:

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;
}

更新 :我的问题是,加密将导致比初始输入字符串更多的字节数:

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;
}

如果您像这样运行此代码:

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

输出将是这样的:

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

显然,输出不是原始字符串,因为我不能一次尝试解密每个字节,因为有时两个或多个字节的密文代表一个整数的值,我需要将其解密回一个字节。原始字符串...所以我想知道处理这个问题的标准机制是什么。

您用于加密和解密每个字节的基本代码(对ModPow的调用)正在工作,但是您将不恰当地“拆分消息并加密每个片段”。

为了证明ModPow部分(即数学)很好,这是基于您的代码,该代码将string加密为BigInteger[]并返回:

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);
    }
}

接下来,您需要阅读更多有关如何使用RSA将一个byte[]加密为另一个byte[] ,包括所有不同的填充方案等。除了对每个字节调用ModPow ,还有很多其他功能。

但是重申一下,您应该这样做来结束生产RSA实施。 做到这一点而没有任何安全漏洞的机会确实很小。 这样做是出于学术兴趣,可以学习更多有关密码学的原理,但将真正的实现方法留给专家是可以的。 (我与该领域的专家相去甚远-无法开始实施自己的加密...)

注意:我更新了此答案。 请向下滚动至该更新,以了解应如何实际实施该更新,因为此第一种方法不是进行RSA加密的正确方法。

我认为可以这样做的一种方法是这样的(但可能不符合标准),而且请注意,这不能填充:

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;
}

这有可能成为跨平台的,因为您可以选择使用其他数据类型来表示e或n,并将其传递给这样的C#后端服务。 这是一个测试:

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));

输出:

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.

更新 :我前面说的一切在RSA的实现中都是完全错误的。 错,错,错! 这是进行RSA加密的正确方法:

  • 将您的字符串转换为BigInteger数据类型。
  • 确保您的整数小于您为算法计算出的n的值,否则您将无法对其解密。
  • 加密整数。 RSA仅适用于整数加密。 这很清楚。
  • 从加密的整数解密它。
  • 我不禁怀疑BigInteger类主要是为密码学创建的。

举个例子:

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);
        }
    }
}

输出示例:

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

如果您希望在C#中使用RSA加密,那么您不应该尝试自己构建。 对于初学者来说,您选择的素数可能很小。 P和Q应该是大质数。

您应该查看其他一些问题/答案:

如何在C#中使用RSA加密文件(大量数据)

C#中大数据的RSA加密

和其他参考: http : //msdn.microsoft.com/zh-cn/library/system.security.cryptography.rsacryptoserviceprovider.encrypt(v=vs.110).aspx

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM