在数据包有效载荷内发送PublicKey [英] Sending PublicKey within packet payload

查看:516
本文介绍了在数据包有效载荷内发送PublicKey的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于学术网络应用程序,我想在2个虚拟机之间建立RSA密钥交换。我使用Crypto ++生成 RSA :: PublicKey ,我现在必须在自定义的第二层框架内发送它(数据包将被制作与 libcrafter )。



事情是,我不知道如何在网络中写密钥,例如接收器,嗅探数据包,能够重新构建,不知何故, code> RSA :: PublicKey 。



我尝试在字符串中保存 raw 如他们在这里说, PublicKey 类包含其他数据,然后简单的原始键(我不需要的数据)。然而,我设法成功,但在接待我不能简单地重建PublicKey ...



有可能,以某种方式,连接的模数,以便在接待处重建 publicKey



>发件人



这里是我在发件人使用的代码。这是必要的线,但我的程序有其他功能,这太长,完全在这里张贴)。

  AutoSeededRandomPool ; 
RSA :: PrivateKey privateKey;
privateKey.GenerateRandomWithKeySize(rng,3072);
RSA :: PublicKey publicKey(privateKey);
cout<< >> Key generated<< endl;

/ *将键转换为字符串,然后转换为const char * * /
std :: string publicKeyString;
publicKey.BEREncode(StringSink(publicKeyString).Ref());

const char * publicKeyChar = publicKeyString.c_str();
cout<<key size:<< publicKeyString.size()< endl

/ *发送数据包* /
Crafter :: RawLayer type1(K);
Crafter :: RawLayer key_send(publicKeyChar);
// Crafter :: RawLayer key_send(publicKeyString.c_str(),publicKeyString.length());
Crafter :: Packet packet_key(ether_header / type1 / key_send);
packet_key.Send(iface);


$ b

这是我尝试恢复密钥。

  / *抽取有效负载* / 
PayloadLayer * payload_rcv = pack_recu.getLayerOfType< PayloadLayer>();
size_t payload_size = payload_rcv-> getPayloadLen();
Crafter :: byte * payload = payload_rcv-> getPayload();

cout<< >>>>公钥接收<< endl;

//转换为RSA :: PublicKey
Stringstream ss;
for(int i = 0; i< payload_size; i ++)
ss<有效载荷[i];
string payload_string = ss.str();
cout<< Payload Size:<< payload_size<< endl;
cin.get();
StringSource stringSource(payload_string,true);
RSA :: PublicKey publicKey2;
publicKey2.BERDecode(stringSource);

data-> publicKey = publicKey2;

这里是运行程序的结果:

 在抛出CryptoPP :: BERDecodeErr的实例后调用终止
what():BER解码错误

我确定错误是从字符串转换到 publicKey ... $ c> BERDecode function war最初认为是从文件中恢复键

...



一个办法 ?我认为发送所有的元素来重建密钥可能会更好,但我不能算出如何做...

解决方案



const char * publicKeyChar = publicKeyString.c_str() ;


BER编码可能具有嵌入的 NULL ,因此您无法使用常用的C字符串操作:

  const char * publicKeyChar = publicKeyString.c_str 
...
Crafter :: RawLayer key_send(publicKeyChar);

编写编码公钥时,以下内容正确。你应该取消注释并使用它(我使用 data size ,因为它在逻辑上与C字符串和长度分离) 。

  Crafter :: RawLayer key_send(publicKeyString.data(),publicKeyString.size()); 

因此,整个Crypto ++的东西可能如下所示:

  //主机的私钥,生成或Load()它... 
RSA :: PrivateKey privKey;
...

//从私钥创建一个公钥
RSA :: PublicKey pubKey(privKey);

//暂时
string spki;
StringSink ss(spki);

//使用保存到DER编码主题公钥信息(SPKI)
pubKey.Save(ss);

Crafter :: RawLayer key_send(spki.data(),spki.size());

然后,重建它:

  //键有效载荷
const PayloadLayer& payload_rcv = * pack_recu.getLayerOfType< PayloadLayer>();

//获取连续数组(我不知道这是在Crafter中调用)
payload_rcv.PullUp();

//直接使用数组避免复制
ArraySource as(payload_rcv.data(),payload_rcv.size(),true / * pumpAll * /);
RSA :: PublicKey pubKey;

//使用Load to BER解码主题公钥信息(SPKI)
pubKey.Load(as);

//使用前验证它
AutoSeededRandomPool prng;
pubKey.ThrowIfInvalid(prng);

我认为使用主题公钥信息(SPKI)而不仅仅是公钥非常重要。 SPKI通过OID包括算法标识符。这将使算法敏捷性稍后更容易一点。之后,您可以切换到ECDSA密钥或ed25519密钥,并且它们的密钥类型将是密钥有效内容的一部分。






< blockquote>

  terminate在抛出CryptoPP :: BERDecodeErr的实例后调用
what():BER解码错误


显然,你应该设置一个 try / catch ,并抓住 BERDecodeErr

  try 
{
//使用Load to BER解码主题公钥信息(SPKI)
pubKey.Load(as);

//使用前验证它
AutoSeededRandomPool prng;
pubKey.ThrowIfInvalid(prng);
}
catch(const BERDecodeErr& ex)
{
cerr< ex.what()<< endl
}
catch(const InvalidMaterial& ex)
{
cerr< ex.what()<< endl
}






公开金钥资讯如下:

  $ cat cryptopp-test.cpp 
.. 。
int main(int argc,char * argv [])
{
AutoSeededRandomPool prng;

RSA :: PrivateKey rsaPrivate;
rsaPrivate.GenerateRandomWithKeySize(prng,3072);

RSA :: PublicKey rsaPublic(rsaPrivate);
FileSink sink(rsa-public.der);
rsaPublic.Save(sink);

return 0;
}

然后使用像Peter Gutmann的 dumpasn1

  $ dumpasn1 rsa-public.der 
0 416:SEQUENCE {
4 13:SEQUENCE {
6 9:OBJECT IDENTIFIER rsaEncryption(1 2 840 113549 1 1 1)
17 0:NULL
:}
19 397:BIT STRING,封装{
24 392:SEQUENCE {
28 385:INTEGER
:00 CE B0 19 0D 0C EB 87 BD 6B 51 6C BB 00 9C EE
:1D 75 9C 28 DC 0E 8E 88 9A 95 8A 3B 6C BD 1F 3F
:03 05 22 8E 3D 19 33 D7 C5 A3 28 4F 13 3D 9E BF
:5A 54 51 AE D6 DA C3 AC 1D 9C 4C A3 47 C0 04 8F
:9D 0A DD 38 60 56 E3 9C DB 7C EA A8 3F 52 93 99
:40 90 14 41 0A 3B 58 F2 13 9F 38 64 18 DD 62 55
:D2 32 53 A0 D5 1A 54 E7 8D 23 01 E0 97 ED F9 C7
:68 9F E2 00 48 99 53 40 6E 7E 5C DA 47 39 4A 41
:[另外257个字节被跳过]
417 1:INTEGER 17
: }
:}
:}

0个警告,0个错误。


For an academic network application, I'd like to set up an RSA key exchange between 2 virtual machines. I am using Crypto++ to generate the RSA::PublicKey, and I must now send it within a custom layer-2 frame (the packet will be crafted with libcrafter).

The thing is, I have no idea of how write the key in the network, such as the receiver, sniffing the packet, is able to re-build, somehow, the RSA::PublicKey.

I tried to save it raw in a string, but as they say here, the PublicKey class contains other data, then simply the raw key (data that I don't need). nevertheless, I manage to success that, but at the reception I can't simply rebuild the PublicKey...

Could it be possible, somehow, to concatenate the modulus, the primes and the public exponent, in order to rebuild the publicKey at the reception?

Sender

Here is the code I use at the sender. It's the essential lines, but my program has other functionality, and it would be too long to post it entirely here).

AutoSeededRandomPool rng;
RSA::PrivateKey privateKey;
privateKey.GenerateRandomWithKeySize(rng, 3072);
RSA::PublicKey publicKey(privateKey);
cout << ">> Key generated" <<endl;

/* Convert key to string then to const char* */
std::string publicKeyString;
publicKey.BEREncode( StringSink(publicKeyString).Ref() );

const char * publicKeyChar = publicKeyString.c_str();
cout <<"key size : "<<publicKeyString.size()<< endl;

/* Send Packet */
Crafter::RawLayer type1("K");
Crafter::RawLayer key_send(publicKeyChar);
//Crafter::RawLayer key_send(publicKeyString.c_str(), publicKeyString.length());
Crafter::Packet packet_key (ether_header / type1 / key_send);
packet_key.Send(iface);

Receiver

And here is my attempt to recover the key.

/* Extract Payload */
PayloadLayer *payload_rcv = pack_recu.getLayerOfType<PayloadLayer>();
size_t payload_size  = payload_rcv->getPayloadLen() ; 
Crafter::byte *payload = payload_rcv->getPayload();

cout << ">> Public Key recieved"<<endl;

// Convert into RSA::PublicKey
stringstream ss;
for (int i=0; i< payload_size; i++)
    ss << payload[i];
string payload_string = ss.str();
cout << "Payload Size: "<<payload_size<<endl;
cin.get();
StringSource stringSource(payload_string, true);
RSA::PublicKey publicKey2;
publicKey2.BERDecode(stringSource);

data->publicKey = publicKey2;

And here is the result of running the program:

terminate called after throwing an instance of 'CryptoPP::BERDecodeErr'
what():  BER decode error

I'm sure the error comes from the conversion from string to publicKey... The BERDecode function war originally thought to recover the key from a file...

Does anyone has a solution ? I think that sending apart all the elements to rebuild the key could be better, but I can't figure how to do it...

解决方案

publicKey.BEREncode( StringSink(publicKeyString).Ref() );

const char * publicKeyChar = publicKeyString.c_str();

A BER encoding likely has an embedded NULL, so you cannot use customary C-string operations on it:

const char * publicKeyChar = publicKeyString.c_str();
...
Crafter::RawLayer key_send(publicKeyChar);

When writing the encoded public key, the following looks correct. You should uncomment it and use it (I use data and size because it logically separates from C-strings and length).

Crafter::RawLayer key_send(publicKeyString.data(), publicKeyString.size());

So the whole Crypto++ thing might look like the following:

// Host's private key, generate or Load() it...
RSA::PrivateKey privKey;
...

// Create a public key from the private key
RSA::PublicKey pubKey(privKey);

// Temporaries
string spki;
StringSink ss(spki);

// Use Save to DER encode the Subject Public Key Info (SPKI)
pubKey.Save(ss);

Crafter::RawLayer key_send(spki.data(), spki.size());

Then, to reconstruct it:

// Key payload
const PayloadLayer& payload_rcv = *pack_recu.getLayerOfType<PayloadLayer>();

// Get a contiguous array (I don't know what this is called in Crafter)
payload_rcv.PullUp();

// Use the array directly to avoid the copy
ArraySource as(payload_rcv.data(), payload_rcv.size(), true /*pumpAll*/);
RSA::PublicKey pubKey;

// Use Load to BER decode the Subject Public Key Info (SPKI)
pubKey.Load(as);

// Validate it before using it
AutoSeededRandomPool prng;
pubKey.ThrowIfInvalid(prng);

I think its important to use the Subject Public Key Info (SPKI) rather than just the Public Key. The SPKI includes an algorithm identifier by way of an OID. It will make algorithm agility a little easier later on. Later, you can switch to a ECDSA key or an ed25519 key, and they key type will be part of the key payload.


terminate called after throwing an instance of 'CryptoPP::BERDecodeErr'
what():  BER decode error

Obviously, you should set up a try/catch, and catch a BERDecodeErr:

try
{
    // Use Load to BER decode the Subject Public Key Info (SPKI)
    pubKey.Load(as);

    // Validate it before using it
    AutoSeededRandomPool prng;
    pubKey.ThrowIfInvalid(prng);
}
catch(const BERDecodeErr& ex)
{
    cerr << ex.what() << endl;
}
catch(const InvalidMaterial& ex)
{
    cerr << ex.what() << endl;
}


And here's what the Subject Public Key Info looks like:

$ cat cryptopp-test.cpp
...
int main(int argc, char* argv[])
{
    AutoSeededRandomPool prng;

    RSA::PrivateKey rsaPrivate;
    rsaPrivate.GenerateRandomWithKeySize(prng, 3072);

    RSA::PublicKey rsaPublic(rsaPrivate);
    FileSink sink("rsa-public.der");
    rsaPublic.Save(sink);

    return 0;
}

And then use something like Peter Gutmann's dumpasn1:

$ dumpasn1 rsa-public.der
  0 416: SEQUENCE {
  4  13:   SEQUENCE {
  6   9:     OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
 17   0:     NULL
       :     }
 19 397:   BIT STRING, encapsulates {
 24 392:     SEQUENCE {
 28 385:       INTEGER
       :         00 CE B0 19 0D 0C EB 87 BD 6B 51 6C BB 00 9C EE
       :         1D 75 9C 28 DC 0E 8E 88 9A 95 8A 3B 6C BD 1F 3F
       :         03 05 22 8E 3D 19 33 D7 C5 A3 28 4F 13 3D 9E BF
       :         5A 54 51 AE D6 DA C3 AC 1D 9C 4C A3 47 C0 04 8F
       :         9D 0A DD 38 60 56 E3 9C DB 7C EA A8 3F 52 93 99
       :         40 90 14 41 0A 3B 58 F2 13 9F 38 64 18 DD 62 55
       :         D2 32 53 A0 D5 1A 54 E7 8D 23 01 E0 97 ED F9 C7
       :         68 9F E2 00 48 99 53 40 6E 7E 5C DA 47 39 4A 41
       :                 [ Another 257 bytes skipped ]
417   1:       INTEGER 17
       :       }
       :     }
       :   }

0 warnings, 0 errors.

这篇关于在数据包有效载荷内发送PublicKey的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆