你竟然在公钥中下毒!——如何在RSA公钥中添加后门
原文:http://www.hackdig.com/?01/hack-17893.htm
分享到:
当我知道它是如何运行时,我惊得下巴都掉了。这是一个非常简单的手法,但这篇文章会颠覆你之前对RSA的看法。这并不是一种劫持RSA的方法,但它会为你的想象插上翅膀。
假设你可以修改RSA公钥的生成器,而且你想给别人拿到私钥的机会但不借助因式分解以及其他量子计算机的力量。你会怎么做?
我会用C#、BouncyCastle 以及Chaos.NaCl(这个库实现了Curve25519)。
1)伪随机数生成器(PRNG)
我们需要一个用私密值进行初始化的PRNG。我将会在CTR模式中使用AES。
using System;
using System.ComponentModel;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Security;
namespace RsaBackdoor.Backdoor
{
class SeededGenerator:IRandomGenerator
{
private readonly AesFastEngine _engine = new AesFastEngine();
private readonly byte[] _counter = new byte[16];
private readonly byte[] _buf = new byte[16];
private int bufOffset = 0;
public SeededGenerator(byte[] key)
{
_engine.Init(true, new KeyParameter(key));
MakeBytes();
}
private void MakeBytes()
{
bufOffset = 0;
_engine.ProcessBlock(_counter, 0, _buf, 0);
IncrementCounter();
}
public void IncrementCounter()
{
for (int i = 0; i < _counter.Length; i++)
{
_counter[i]++;
if (_counter[i] != 0)
break;
}
}
public void AddSeedMaterial(byte[] seed)
{
}
public void AddSeedMaterial(long seed)
{
}
public void NextBytes(byte[] bytes)
{
NextBytes(bytes, 0, bytes.Length);
}
public void NextBytes(byte[] bytes, int start, int len)
{
var count = 0;
while (count < len)
{
var amount = Math.Min(_buf.Length - bufOffset, len - count);
Array.Copy(_buf, bufOffset, bytes, start + count, amount);
count += amount;
bufOffset += amount;
if (bufOffset >= _buf.Length)
{
MakeBytes();
}
}
}
}
}
2) 让我们回忆一下强大的Curve25519,也就是任何32位的序列对于私钥来说都是有效的。同时,公钥也是32位。让我们预先生成一下这个主密钥并且将它分配给一个常量:
private const string MY_PRIVATE_STR = "BDB440EBF1A77CFA014A9CD753F3F6335B1BCDD8ABE30049F10C44243BF3B6C8";
private static readonly byte[] MY_PRIVATE = StringToByteArray(MY_PRIVATE_STR);
对于生成的每对RSA公钥,我们同时生成一对Curve25519随机密钥,然后从这对密钥以及我们的公钥中计算出共享的秘密和私钥。这个私密值是第一步中的PRNG。
PRNG的种子生成:
private void MakeSeedAndPayload(out byte[] seed, out byte[] payload)
{
var rnd = new SecureRandom();
var priv = new byte[32];
rnd.NextBytes(priv);
payload = MontgomeryCurve25519.GetPublicKey(priv);
seed = MontgomeryCurve25519.KeyExchange(payload, MY_PRIVATE);
}
Curve25519的公钥将用于计算这个种子是一个有效负载。我们将试着把它放入RSA公钥。
3)通过PRNG以及我们的种子生成RSA密钥对。
var publicExponent = new BigInteger("10001", 16);
var keygen = new RsaKeyPairGenerator();
keygen.Init(new RsaKeyGenerationParameters(publicExponent, new SecureRandom(new SeededGenerator(seed)), 2048, 80));
var pair = keygen.GenerateKeyPair();
可以说基于密钥的RSA总是两个质数p跟q。它们的产出叫做«modulus»,并且是公钥的一部分。在这种情况下,两个2048二进制数字会通过我们的PRNG进行搜索并且从中会搭建一个单独密钥对。
4)现在,用我们的有效负载来代替p*q系数中的一些字节。可以代替最高有效的字节,这样它们就不会被删除。80字节的偏移应该够了。
var paramz = ((RsaPrivateCrtKeyParameters) pair.Private);
var modulus = paramz.Modulus.ToByteArray();
Replace(modulus, payload, 80);
这样,我们拥有了一个新的n' 模数,并且需要重新生成余下的参数,将n' 考虑在内:
5.1) 计算新的q'。我们不知道在目前状况下会出现什么情况,但结果还不至于糟糕。
q' = n' / p
5.2)查找一个新的基于q'的质数。当我们找到它时,最低有效二进制数字将被删除。最高有效二进制数字不会变。这正是我们所需要的。
var p = paramz.P;
var n = new BigInteger(modulus);
var preQ = n.Divide(p);
var q = preQ.NextProbablePrime();
一旦我们有了新的q,我们会再次计算除了p之外的所有密钥对参数。
public AsymmetricCipherKeyPair ComposeKeyPair(BigInteger p, BigInteger q, BigInteger publicExponent)
{
if (p.Max(q).Equals(q))
{
var tmp = p;
p = q;
q = tmp;
}
var modulus = p.Multiply(q);
var p1 = p.Subtract(BigInteger.One);
var q1 = q.Subtract(BigInteger.One);
var phi = p1.Multiply(q1);
var privateExponent = publicExponent.ModInverse(phi);
var dP = privateExponent.Remainder(p1);
var dQ = privateExponent.Remainder(q1);
var qInv = q.ModInverse(p);
var priv = new RsaPrivateCrtKeyParameters(modulus, publicExponent, privateExponent, p, q, dP, dQ, qInv);
return new AsymmetricCipherKeyPair(new RsaKeyParameters(false, priv.Modulus, publicExponent), priv);
}
结果,我们得到了一个有效的密钥对,这个密钥对的公钥中包含我们的有效负载——即如何获取种子以及私钥本身的信息。
我们可以提取有效负载:
public byte[] ExtractPayload(RsaKeyParameters pub)
{
var modulus = pub.Modulus.ToByteArray();
var payload = new byte[32];
Array.Copy(modulus, 80, payload, 0, 32);
return payload;
}
计算种子并将相同的流程再重复一次以便得到这个私钥:
public AsymmetricCipherKeyPair BuildKeyFromPayload(byte[] payload)
{
var seed = MontgomeryCurve25519.KeyExchange(payload, MY_PRIVATE);
return BuildKey(seed, payload);
}
这样,通过拥有一个Curve25519的私钥,只有我们能够获取任何被装后门的RSA的一个私钥。
这能用在什么地方?任何地方!你将永远无法证明银行颁发给你的密钥对没有此类标记。想要证明是不可能的!所以,你可以自己生成密钥。因此,如果可能的话,要停止使用RSA。
感谢https://gist.github.com/ryancdotorg提供的原始文件https://gist.github.com/ryancdotorg/18235723e926be0afbdd
小编解读:
作者没有指出这种攻击的详细攻击手段。这种攻击是生成看上去完全随机的RSA密钥对。但是公钥中存在信息可以令攻击者计算出私钥。存在两种攻击模式:
1. 如果你自己使用离线软件生成RSA密钥对,程序产生看上去是随机的密钥对。你相信一个离线软件是不会泄漏你的私钥,因此你公布你的公钥来给其他人用来验证你的签名。攻击者就可以根据你公开的公钥算出私钥。他们就可以用这个私钥做任何事。
2. 假如说NSA要求SSL证书提供商在key生成算法中加入后门。这个是很容易的,不需要跟NSA有任何联系。NSA只需“看一眼”公钥证书就能计算出私钥证书。所有通过SSL加密的通讯在攻击者看来就是明文传输。
这种攻击攻击早在非对称算法初期就被发现。不只是RSA存在这个问题。所有算法都可以留后门。
本文由 360安全播报 翻译,转载请注明“转自360安全播报”,并附上链接。
原文链接:http://kukuruku.co/hub/infosec/backdoor-in-a-public-rsa-key
你竟然在公钥中下毒!——如何在RSA公钥中添加后门的更多相关文章
- 如何在VUE项目中添加ESLint
如何在VUE项目中添加ESLint 1. 首先在项目的根目录下 新建 .eslintrc.js文件,其配置规则可以如下:(自己小整理了一份),所有的代码如下: // https://eslint.or ...
- 如何在Android Studio中添加注释模板信息?
如何在Android Studio中添加注释模板信息? 在开发程序的时候,我们一般都会给文件自动添加上一些关于文件的注释信息,比如开发者的名字,开发的时间,开发者的联系方式等等.那么在android ...
- 文章翻译:ABP如何在EF core中添加数据过滤器
原文地址:https://aspnetboilerplate.com/Pages/Documents/Articles%5CHow-To%5Cadd-custom-data-filter-ef-cor ...
- 如何在select下拉列表中添加复选框?
近来在给一个公司做考试系统的项目,遇到的问题不少,但其中的几个让我对表单的使用颇为感兴趣,前端程序员都知道,下拉列表有select标签,复选框有checkbox,但是两者合在一起却少有人去研究,当时接 ...
- 如何在RCP程序中添加一个banner栏
前言:这段时间还算比较空闲,我准备把过去做过的有些形形色色,甚至有些奇怪的研究总结一下,也许刚好有人用的着也不一定,不枉为之抓耳挠腮的时光和浪费的电力.以前有个客户提出要在RCP程序中添加一个bann ...
- 如何在Android Studio中添加RecyclerView-v7支持包
1.打开SDK Manager,在Extras树下找到Android Support Library,下载好支持包.RecyclerView在v7-21版本就出来了.我这里不用更新了,说明是最新的,怎 ...
- [译]如何在Unity编辑器中添加你自己的工具
在这篇教程中你会学习如何扩展你的Unity3D编辑器,以便在你的项目中更好的使用它.你将会学习如何绘制你自己的gizmo,用代码来实现创建和删除物体,创建编辑器窗口,使用组件,并且允许用户撤销他们所作 ...
- 如何在Sublime Text中添加代码片段
我们在编写代码的时候,总会遇到一些需要反复使用的代码片段.这时候就需要反复的复制和黏贴,大大影响效率.我们利用Sublime Text的snippet(代码片段)功能,就能很好的解决这一问题.通俗的讲 ...
- 如何在web项目中添加javamelody monitoring 监控。
1.在工程的maven pom中添加依赖javamelody-core <!-- monitoring监控 --><!-- https://mvnrepository.com/art ...
随机推荐
- 【poj2406】 Power Strings
http://poj.org/problem?id=2406 (题目链接) 题意 给定一个字符串 L,已知这个字符串是由某个字符串 S 重复 R 次而得到的, 求 R 的最大值. Solution 后 ...
- Android 资源目录 相关知识 raw、 drawable、 values..
一定要看的 Android 资源目录的相关知识 raw drwable valueshttp://blog.csdn.net/shichexixi/article/details/5985677 ht ...
- 使用 xhprof 进行 php 的性能分析
基于本机环境(php7,macos) 1.xhprof 扩展 php7 下安装 xhprof 扩展: git clone https://github.com/longxinH/xhprof cd x ...
- git的一些常见问题
from:http://hi.baidu.com/mvp_xuan/blog/item/2ba062d8cd2c9fc939012fae.html 关于linux上建库等操作请看文章: http:// ...
- Python 函数01
Python 函数 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你也 ...
- 并发库应用之三 & 线程池与定时器应用
在TCP服务器编程模型的原理,每一个客户端连接用一个单独的线程为之服务,当与客户端的会话结束时,线程也就结束了,即每来一个客户端连接,服务器端就要创建一个新线程.如果访问服务器的客户端很多,那么服务器 ...
- [Java]-Java的版本演化
一.Java SE 8 Java SE 8发行于2014年3月18日,代号culture,这是一个在Java历史上的重大发布 Java SE 8 新特性: Lambda Expressions(Lam ...
- [LeetCode] 398. Random Pick Index ☆☆☆
Given an array of integers with possible duplicates, randomly output the index of a given target num ...
- PHP官方文档之————secure.php.net.while
while 语句的含意很简单,它告诉 PHP 只要 while 表达式的值为 TRUE 就重复执行嵌套中的循环语句.表达式的值在每次开始循环时检查,所以即使这个值在循环语句中改变了,语句也不会停止执行 ...
- android studio run 的时候,报the apk file does not exist on disk,
1.首先 clean rebuild,重启,不能解决的话,再找到这个 然后是这里: 不用填,点ok,ok即可,他喵的,卡我俩小时