【发布时间】:2015-05-02 10:49:43
【问题描述】:
我正在使用以下河豚的实现(中间切掉了一些东西)来加密和解密消息。我正在尝试在大端机器(SunOS 5.10)上加密消息,然后通过套接字将其发送到小端机器(linux)。我可以在同一台机器上加密和解密,也可以在不同的 linux 机器上,但我不能让它在这两台机器之间工作。
因为河豚实现似乎将消息分解为双字,每个双字包含两个 32 位整数,我一直将我的加密消息分解为 32 位整数,将 htonl() 应用于每个整数,通过网络发送它们,将 ntohl() 应用于每个,然后尝试解密。结果并不漂亮,但有趣的是,大多数正确的字符在解密后都存在,但它们是乱码,并且在某些地方出现了一些额外的乱码。我真的无法想象我可能会错过什么。
/********** blowfish.h **********/
#ifndef ___BLOWFISH_H___
#define ___BLOWFISH_H___
#define NUM_SUBKEYS 18
#define NUM_S_BOXES 4
#define NUM_ENTRIES 256
#define MAX_STRING 256
#define MAX_PASSWD 56 // 448bits
#ifdef BIG_ENDIAN
struct WordByte
{
unsigned int zero:8;
unsigned int one:8;
unsigned int two:8;
unsigned int three:8;
};
#endif
#ifdef LITTLE_ENDIAN
struct WordByte
{
unsigned int three:8;
unsigned int two:8;
unsigned int one:8;
unsigned int zero:8;
};
#endif
union Word
{
unsigned int word;
WordByte byte;
};
struct DWord
{
Word word0;
Word word1;
};
class Blowfish
{
private:
unsigned int PA[NUM_SUBKEYS];
unsigned int SB[NUM_S_BOXES][NUM_ENTRIES];
void Gen_Subkeys(char *);
inline void BF_En(Word *,Word *);
inline void BF_De(Word *,Word *);
public:
Blowfish();
~Blowfish();
void Reset();
void Set_Passwd(char * = NULL);
void Encrypt(void *,unsigned int);
void Decrypt(void *,unsigned int);
};
#endif
/********** blowfish.cc **********/
#include <iostream.h>
#include <string.h>
#include "blowfish.h"
#define F(x) (((SB[0][x.byte.zero] + SB[1][x.byte.one]) ^ SB[2][x.byte.two]) + SB[3][x.byte.three])
void Blowfish::Gen_Subkeys(char *Passwd)
{
unsigned int i,j,len=strlen(Passwd);
Word Work,null0,null1;
if (len > 0)
{
j = 0;
for (i=0;i<NUM_SUBKEYS;i++)
{
Work.byte.zero = Passwd[(j++)%len];
Work.byte.one = Passwd[(j++)%len];
Work.byte.two = Passwd[(j++)%len];
Work.byte.three = Passwd[(j++)%len];
PA[i] ^= Work.word;
}
null0.word = null1.word = 0;
for (i=0;i<NUM_SUBKEYS;i+=2)
{
BF_En(&null0,&null1);
PA[i] = null0.word;
PA[i+1] = null1.word;
}
for (j=0;j<NUM_S_BOXES;j++)
for (i=0;i<NUM_ENTRIES;i+=2)
{
BF_En(&null0,&null1);
SB[j][i] = null0.word;
SB[j][i+1] = null1.word;
}
}
Work.word = null0.word = null1.word = 0;
Passwd = NULL;
len = 0;
}
void Blowfish::BF_En(Word *x1,Word *x2)
{
Word w1=*x1,w2=*x2;
w1.word ^= PA[0];
w2.word ^= F(w1)^PA[1]; w1.word ^= F(w2)^PA[2];
w2.word ^= F(w1)^PA[3]; w1.word ^= F(w2)^PA[4];
w2.word ^= F(w1)^PA[5]; w1.word ^= F(w2)^PA[6];
w2.word ^= F(w1)^PA[7]; w1.word ^= F(w2)^PA[8];
w2.word ^= F(w1)^PA[9]; w1.word ^= F(w2)^PA[10];
w2.word ^= F(w1)^PA[11]; w1.word ^= F(w2)^PA[12];
w2.word ^= F(w1)^PA[13]; w1.word ^= F(w2)^PA[14];
w2.word ^= F(w1)^PA[15]; w1.word ^= F(w2)^PA[16];
w2.word ^= PA[17];
*x1 = w2;
*x2 = w1;
}
void Blowfish::BF_De(Word *x1,Word *x2)
{
Word w1=*x1,w2=*x2;
w1.word ^= PA[17];
w2.word ^= F(w1)^PA[16]; w1.word ^= F(w2)^PA[15];
w2.word ^= F(w1)^PA[14]; w1.word ^= F(w2)^PA[13];
w2.word ^= F(w1)^PA[12]; w1.word ^= F(w2)^PA[11];
w2.word ^= F(w1)^PA[10]; w1.word ^= F(w2)^PA[9];
w2.word ^= F(w1)^PA[8]; w1.word ^= F(w2)^PA[7];
w2.word ^= F(w1)^PA[6]; w1.word ^= F(w2)^PA[5];
w2.word ^= F(w1)^PA[4]; w1.word ^= F(w2)^PA[3];
w2.word ^= F(w1)^PA[2]; w1.word ^= F(w2)^PA[1];
w2.word ^= PA[0];
*x1 = w2;
*x2 = w1;
}
Blowfish::Blowfish()
{
Reset();
}
Blowfish::~Blowfish()
{
Reset();
}
void Blowfish::Reset()
{
unsigned int i,j;
unsigned int PA_Init[NUM_SUBKEYS] =
{
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
0x9216d5d9, 0x8979fb1b
};
unsigned int SB_Init[NUM_S_BOXES][NUM_ENTRIES] =
{
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
...
...
...
};
for (i=0;i<NUM_SUBKEYS;i++)
PA[i] = PA_Init[i];
for (j=0;j<NUM_S_BOXES;j++)
for (i=0;i<NUM_ENTRIES;i++)
SB[j][i] = SB_Init[j][i];
}
void Blowfish::Set_Passwd(char *Passwd)
{
char New_Passwd[MAX_STRING];
unsigned int i,len;
if (Passwd == NULL)
{
do
{
cout << "\aEnter your password: ";
cin.get(New_Passwd,MAX_STRING,'\n');
len = strlen(New_Passwd);
}
while (len > MAX_PASSWD);
Passwd = New_Passwd;
}
else
len = strlen(Passwd);
Reset();
if (len > 0)
Gen_Subkeys(Passwd);
for (i=0;i<MAX_STRING;i++)
New_Passwd[i] = '\0';
Passwd = NULL;
len = 0;
}
void Blowfish::Encrypt(void *Ptr,unsigned int N_Bytes)
{
unsigned int i;
DWord *Work;
if (N_Bytes%8)
{
cerr << "\aBlowfish requires the input to be a multiple of 8 bytes (64 bits) to work.\n";
return;
}
N_Bytes /= 8;
Work = (DWord *)Ptr;
for (i=0;i<N_Bytes;i++)
{
BF_En(&Work->word0,&Work->word1);
Work++;
}
Work = NULL;
}
void Blowfish::Decrypt(void *Ptr,unsigned int N_Bytes)
{
unsigned int i;
DWord *Work;
if (N_Bytes%8)
{
cerr << "\aBlowfish requires the input to be a multiple of 8 bytes (64 bits) to work.\n";
return;
}
N_Bytes /= 8;
Work = (DWord *)Ptr;
for (i=0;i<N_Bytes;i++)
{
BF_De(&Work->word0,&Work->word1);
Work++;
}
Work = NULL;
}
以下是大端机器上的加密字符串:
11010011 11110110 01100110 10001101 01010011 11000110 00010001 11110110 00011010 11001010 00011111 00100001 10110000 10110100 10110000 11110000 11111100 11000010 00110011 00001111 10011110 01100111 00010101 10000101 00001010 01111110 10000101 00011100 10010010 10111010 10010111 11111001 00111101 01011100 00000010 01010011 11100000 11001101 11111010 00111100 00011101 10001001 01010111 01011010 11100001 10101111 10001100 10111001 00011111 10111100 10010101 01101110 10001101 11010100 11101110 11010001 10111011 11111110 10100111 01111000 11010001 01011101 00110100 01000001 01011011 01111100 10010011 00010001 10100011 10011011 10111100 01100000
这是我通过网络将它发送到 little-endian 机器后的加密字符串:
10001101 01100110 11110110 11010011 11110110 00010001 11000110 01010011 00100001 00011111 11001010 00011010 11110000 10110000 10110100 10110000 00001111 00110011 11000010 11111100 10000101 00010101 01100111 10011110 00011100 10000101 01111110 00001010 11111001 10010111 10111010 10010010 01010011 00000010 01011100 00111101 00111100 11111010 11001101 11100000 01011010 01010111 10001001 00011101 10111001 10001100 10101111 11100001 01101110 10010101 10111100 00011111 11010001 11101110 11010100 10001101 01111000 10100111 11111110 10111011 01000001 00110100 01011101 11010001 00010001 10010011 01111100 01011011 01100000 10111100 10011011 10100011
最后,这是字符串需要的,以便在小端机器上成功解密(即这是我在小端机器上加密原始字符串时得到的,而不是大端机器):
10001101 10111011 10110010 10111000 01110001 11000110 00110110 10101110 11000011 11000100 00001010 10011110 11111101 10101000 01110011 10110110 00011001 10000011 11111000 11010001 01101001 00101100 10110001 00010001 11101101 10001011 01101110 01101011 11110001 01101010 00111010 00010110 11101111 01001111 11001100 00011101 01101010 11110000 00110000 01110100 10111010 01000101 01011001 11111001 11011101 01000011 10000110 01000001 1110100111101011100100
我目前正在尝试比较字符串,但找不到模式。
【问题讨论】:
-
您的
WordByte结构显然是为了处理字节序,但您说您也在使用htonl/ntohl- 它们可能不会发生冲突吗?此外,您可能想检查两个程序是否都是 32 位编译的,例如否则,您在Word中使用unsigned会被破坏。 -
附注:您似乎在 ECB 模式下使用 Blowfish,这是不推荐的做法。
-
@TonyD 当我不使用 htonl/ntohl 时,解密的消息要糟糕得多(即包含伪随机字符)。我继续使用它们,因为我认为如果解密不正确,在解密后取回大部分原始消息字符的几率很低。因此,我必须朝着正确的方向前进??此外,我已将所有“unsigned int”实例更改为“uint32_t”,问题仍然存在。
-
@ztforster:我想我看到了一个问题。当您调用 Encrypt() 时,它在两个平台上都采用相同的字节字符串。虽然您确实纠正了 word.zero 在不同平台上不同的事实,但您没有更正初始输入字符串。也就是说,当您最初调用 BF_En() 时,w1.word 在大端机器和小端机器上是不同的数字。所以第一个 ^= 会在不同的架构上产生不同的数字。在小端机器上调用 BF_En() 之前,在 word0 和 word1 上尝试 htonl(),看看结果是否匹配得更好。
-
@ztforster:如果 w1.word 和 w2.word 是相同的数字,您可以轻松地检查在两个平台上调用 BF_En() 时是否具有相同的输入块。只需 printf("0x%08x 0x%08x\n", w1.word, w2.word);在 BF_En() 的顶部。如果它们不匹配,那就是你的问题。
标签: c++ sockets encryption endianness blowfish