我目前正在用C ++ for Unix编写自己的ASE / RSA加密程序。我现在已经阅读了大约一个星期的文献,我已经开始围绕这一切,但我仍然有一些紧迫的问题:
1)根据我的理解,最基本形式的RSA密钥是所使用的两个素数(R)和指数的乘积的组合。对我来说很明显,以明文形式存储密钥会破坏加密任何东西的目的。因此,我可以以什么形式存储生成的公钥和私钥?询问用户密码并使用ASCII表对密钥的各个数字进行“简单”移位/替换?或者是否有其他标准我还没有遇到过?此外,当生成密钥时,R和相应的指数是否顺序存储?即## primeproduct #### exponent ##?在这种情况下,解密算法如何将密钥解析为两个单独的值?
2)鉴于我决定使用65537作为所有加密的公共指数,我将如何以编程方式生成私有指数?我得到了方程P * Q = 1mod(M),其中P和Q以及指数和M是Euler's Totient函数的结果。这只是产生随机数并测试它们对公众指数的相对素数直到你付出肮脏的问题吗?我知道你不能简单地从1开始增加,直到你找到这样一个数字,因为任何人都可以简单地做同样的事情并自己获得你的私人指数。
3)当生成字符等价集时,我理解集合中使用的数字不能小于P * Q并且相对于P * Q。同样,这是测试数字相对于P * Q的素数的问题。测试相对素数的速度是否与您正在使用的数字的大小无关?或者是必要的特殊算法?
提前感谢任何花时间阅读和回答的人,欢呼!
答案 0 :(得分:7)
存储/交换RSA密钥有一些标准格式,例如RFC 3447。无论好坏,大多数(许多人,无论如何)使用ASN.1编码,这增加了比大多数人喜欢的复杂性,它本身就是一样的。一些使用Base64编码,这更容易实现。
关键是什么构成:在最基本的形式中,你是正确的;公钥包括模数(通常称为n
)和指数(通常称为e
)。
要计算密钥对,您需要从两个大素数开始,通常称为p
和q
。您将模数n
计算为p * q
。您还可以计算r
的数字(通常称为(p-1) * (q-1)
)。
e
是一个或多或少随机选择的数字,相对于r
是素数。警告:您不希望e
非常小 - 尽管日志(e)> = log(n)/ 4是最低限度。
然后将d
(私有解密密钥)计算为满足关系的数字:
d * e = 1 (mod r)
您通常使用Euclid算法计算此值,但还有其他选项(见下文)。同样,你也不希望d
变得非常小,所以如果它的数量很小,你可能想要为e
尝试另一个值,并计算一个新{ {1}}要匹配。
还有另一种方法来计算d
和e
。你可以从找到一个与1 mod r一致的数字K开始,然后考虑它。将素数因子放在一起得到大小相等的两个因子,并将它们用作d
和e
。
对于计算d
的攻击者而言:您需要d
来计算此内容,而了解r
取决于了解r
和p
。这正是为什么/在何处/如何将因子分解为破坏RSA。如果您考虑q
,那么您就知道n
和p
。通过它们,您可以找到q
,并且可以r
从r
计算与已知d
匹配的e
。
所以,让我们通过数学来创建密钥对。我们将使用 太小而不能有效的素数,但应该足以证明所涉及的想法。
因此,让我们从选择p和q开始(当然,两者都需要成为素数):
p = 9999991
q = 11999989
我们计算n
和r
:
n = 119999782000099
r = 119999760000120
接下来,我们需要选择e
或计算K
,然后将其计算为e
和d
。目前,我们将按照你的建议e = 65537(因为65537是素数,唯一的可能性是r
不是相对质数,如果r
是一个精确的倍数65537,我们可以很容易地验证这种情况。)
由此,我们需要计算d
。我们可以相当容易地(尽管不一定非常快)使用"扩展"欧几里德算法的版本,(正如你所提到的)欧拉的商,高斯'方法,或任何其他方法。
目前,我将使用高斯'来计算它。方法:
template <class num>
num gcd(num a, num b) {
num r;
while (b > 0) {
r = a % b;
a = b;
b = r;
}
return a;
}
template <class num>
num find_inverse(num a, num p) {
num g, z;
if (gcd(a, p) > 1) return 0;
z = 1;
while (a > 1) {
z += p;
if ((g=gcd(a, z))> 1) {
a /= g;
z /= g;
}
}
return z;
}
我们得到的结果是:
d = 38110914516113
然后我们可以将它们插入到RSA的实现中,并使用它们来加密和解密消息。
所以,让我们加密&#34;非常秘密的消息!&#34;。使用上面给出的e
和n
加密到:
74603288122996
49544151279887
83011912841578
96347106356362
20256165166509
66272049143842
49544151279887
22863535059597
83011912841578
49544151279887
96446347654908
20256165166509
87232607087245
49544151279887
68304272579690
68304272579690
87665372487589
26633960965444
49544151279887
15733234551614
并且,使用上面给出的d
,解密回原始版本。执行加密/解密的代码(使用硬编码密钥和模数)如下所示:
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
#include <functional>
typedef unsigned long long num;
const num e_key = 65537;
const num d_key = 38110914516113;
const num n = 119999782000099;
template <class T>
T mul_mod(T a, T b, T m) {
if (m == 0) return a * b;
T r = T();
while (a > 0) {
if (a & 1)
if ((r += b) > m) r %= m;
a >>= 1;
if ((b <<= 1) > m) b %= m;
}
return r;
}
template <class T>
T pow_mod(T a, T n, T m) {
T r = 1;
while (n > 0) {
if (n & 1)
r = mul_mod(r, a, m);
a = mul_mod(a, a, m);
n >>= 1;
}
return r;
}
int main() {
std::string msg = "Very Secret Message!";
std::vector<num> encrypted;
std::cout << "Original message: " << msg << '\n';
std::transform(msg.begin(), msg.end(),
std::back_inserter(encrypted),
[&](num val) { return pow_mod(val, e_key, n); });
std::cout << "Encrypted message:\n";
std::copy(encrypted.begin(), encrypted.end(), std::ostream_iterator<num>(std::cout, "\n"));
std::cout << "\n";
std::cout << "Decrypted message: ";
std::transform(encrypted.begin(), encrypted.end(),
std::ostream_iterator<char>(std::cout, ""),
[](num val) { return pow_mod(val, d_key, n); });
std::cout << "\n";
}
为了获得安全性的希望,你需要使用更大的模数 - 至少数百个比特(对于偏执狂来说可能是千分之一或更多)。您可以使用普通的任意精度整数库或专门为手头任务编写的例程来实现。 RSA本质上相当慢,因此大多数实现一度使用具有大量毛发优化的代码来完成工作。如今,硬件足够快,你可以很容易地使用一个相当平均的大整数库(特别是在实际使用中,你只想使用RSA加密/解密一个密钥用于对称算法,而不是加密原始数据)。
即使具有合适尺寸的模数(并且修改了代码以支持所需的大数量),这仍然有时被称为&#34;教科书RSA&#34;,并且它&#39;在真正的加密方式上并不太适合。例如,现在,它一次加密输入的一个字节。这在加密数据中留下了明显的模式。查看上面的加密数据并查看比第二个和第七个单词是相同的是微不足道的 - 因为两者都是e
的加密形式(它也出现在消息中的其他几个位置)
就目前而言,这可以作为简单的替代代码进行攻击。 e
是英语中最常见的字母,因此我们可以(正确地)猜测加密数据中最常见的单词代表e
(并且各种语言中字母的相对频率是众所周知的)。更糟糕的是,我们还可以查看字母对和三元组等内容来改善攻击。例如,如果我们在加密数据中连续两次看到相同的单词,我们知道我们会看到一个双字母,它只能是普通英文文本中的几个字母。结论:尽管RSA本身非常强大,但上面显示的使用方式肯定是而不是。
为了防止出现这个问题,使用(例如)512位密钥,我们还会以512位块的形式处理输入。这意味着如果原始输入中有两个位置一次只有512位,那么我们只有一个重复,这些位置完全相同。即使发生了这种情况,也难以猜测,即使这是不可取的,但它并不像上面所示的逐字节版本那样容易受到攻击。此外,您始终希望将输入填充为加密大小的倍数。
<强>参考强>
https://crypto.stackexchange.com/questions/1448/definition-of-textbook-rsa