你竟然在公钥中下毒!——如何在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 ...
随机推荐
- ---web模型 --mvc和模型--struts2 入门
关于web模型: 早期的web 应用主要是静态页丽的浏览〈如新闻的制监),随着Internet的发展,web应用也变得越来越复杂,不仅要 和数据库进行交互 ,还要和用户进行交互,由此衍生了各种服务器端 ...
- fzyzojP3412 -- [校内训练20171212]奇数
套路地, 考虑dfs树上搞事情 容易发现,对于(x,y)如果dfs树上距离为奇数,或者dfs树上路径中有一条边在某个简单奇环上,那么可以经过奇数条边到达 判断边在某个奇环上: 点双,点双中黑白染色,如 ...
- 使用JS在textarea在光标处插入内容
// 在光标处插入字符串 // myField 文本框对象 // myValue 要插入的值 function insertAtCursor(myField, myValue) { //IE supp ...
- sqlalchemy外键关联
一.创建两张表,并关联外键 导入ForenginKey模块 # -*- coding: UTF-8 -*- from sqlalchemy import create_engine from sqla ...
- arcgis求邻接矩阵
求邻接矩阵 教程链接 http://m.blog.csdn.net/wan_yanyan528/article/details/49175673 (1) 将目标shp文件导出一份副本备用(以省级为 ...
- python 生成器和迭代器介绍
在正式接触生成器之前,我们先来了解一些概念 容器(container) 容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个迭代获取,可以用in.not in关键字判断元素是否包含在容器中. ...
- 简便开发插件 -- lombok
lombok是一款可以精减java代码.提升开发人员生产效率的辅助工具,可以利用注解在编译期自动生成setter/getter/toString()/constructor代码. 彻底将开发人员从繁琐 ...
- 内存操作函数memmove,memcpy,memset
通过字符串的学习,我们知道字符串操作函数的操作对象是字符串,并且它的结束标志是结束符\0,当然这个说的是不 受限制的字符串函数.然而当我们想要将一段内存的数据复制到另一块内存时,我们不能使用字符串操作 ...
- SDOI2017 Round1 起点
第二次打酱油了 高一两次考试以打两瓶酱油告终 来的时候明知自己没戏,却总存有一丝希望 NOIP连200都没考到,是不是有点儿不自量力 如果我真的去争取那一丝希望的话,该有多好 先简单分析下考试 Day ...
- 蓝桥杯 地宫寻宝 DFS 动态规划
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cstdl ...