可變參數(shù):分組大小、
密鑰大小和加密輪數(shù)
相關(guān)介紹
編輯
簡(jiǎn)介
RC5是種比較新的算法,Rivest設(shè)計(jì)了RC5的一種特殊的實(shí)現(xiàn)方式,因此RC5算法有一個(gè)面向字的結(jié)構(gòu):RC5-w/r/b,這里w是
字長(zhǎng)其值可以是16、32或64對(duì)于不同的字長(zhǎng)明文和密文塊的分組長(zhǎng)度為2w位,r是加密輪數(shù),b是密鑰
字節(jié)長(zhǎng)度。由于RC5一個(gè)分組長(zhǎng)度可變的
密碼算法,為了便于說(shuō)明在本文中主要是針對(duì)64位的分組w=32進(jìn)行處理的,下面詳細(xì)說(shuō)明了RC5加密解密的處理過(guò)程:
加密解密
RC5算法加密時(shí)使用了2r+2個(gè)密鑰相關(guān)的的32位字: ,這里r表示加密的輪數(shù)。創(chuàng)建這個(gè)密鑰組的過(guò)程是非常復(fù)雜的但也是直接的,首先將密鑰字節(jié)拷貝到32位字的數(shù)組L中(此時(shí)要注意處理器是
little-endian順序還是big-endian順序),如果需要,最后一個(gè)字可以用零填充。然后利用線性同余發(fā)生器模2初始化數(shù)組S:
對(duì)于i=1到2(r+1)-1: (本應(yīng)模 ,本文中令w=32)
其中對(duì)于16位字32位分組的RC5,P=0xb7e1 Q=0x9e37
對(duì)于32位字和64位分組的RC5,P=0xb7e15163 Q=0x9e3779b9
對(duì)于64位字和128位分組,P=0xb7151628aed2a6b Q=0x9e3779b97f4a7c15
最后將L與S混合,混合過(guò)程如下:
i=j=0
A=B=0
處理3n次(這里n是2(r+1)和c中的最大值,其中c表示輸入的密鑰字的個(gè)數(shù))
2、加密處理
在創(chuàng)建完
密鑰組后開(kāi)始進(jìn)行對(duì)明文的加密,加密時(shí),首先將明文分組劃分為兩個(gè)32位字:A和B(在假設(shè)處理器
字節(jié)順序是
little-endian、w=32的情況下,第一個(gè)明文字節(jié)進(jìn)入A的最低字節(jié),第四個(gè)明文字節(jié)進(jìn)入A的最高字節(jié),第五個(gè)明文字節(jié)進(jìn)入B的最低字節(jié),以此類推),其中
操作符<<<表示循環(huán)左移,加運(yùn)算是模 (本應(yīng)模 ,本文中令w=32)的。
輸出的密文是在寄存器A和B中的內(nèi)容
3、解密處理
解密也是很容易的,把密文分組劃分為兩個(gè)字:A和B(存儲(chǔ)方式和加密一樣),這里符合>>>是循環(huán)右移,減運(yùn)算也是模 (本應(yīng)模 ,本文中令w=32)的。
RSA試驗(yàn)室花費(fèi)了相當(dāng)?shù)臅r(shí)間來(lái)分析64位分組的RC5算法,在5輪后統(tǒng)計(jì)特性看起來(lái)非常好。在8輪后,每一個(gè)明文位至少影響一個(gè)循環(huán)。對(duì)于5輪的RC5,
差分攻擊需要 個(gè)選擇明文;對(duì)10輪需要 個(gè);對(duì)于12輪需要 個(gè);對(duì)15輪需要 個(gè)。而對(duì)于64位的分組只有 個(gè)可能的明文,所以對(duì)于15輪或以上的RC5的差分攻擊是失敗的。在6輪后線性分析就是安全的了,Rivest推薦至少12輪,甚至可能是16輪。這個(gè)輪數(shù)可以進(jìn)行選擇。
參考例子
編輯
rfc 2040文檔中列出了RC5算法
密鑰生成和加密實(shí)現(xiàn)的C代碼,在此筆者參照文檔中定義的算法結(jié)構(gòu),編寫(xiě)了用于對(duì)密文解密的程序代碼(此代碼經(jīng)多次測(cè)試運(yùn)行良好),供讀者參考。
宏定義
#define SHL1(x,s,w) ((RC5_WORD)((x)<<((w)-((s)&ROT_MASK))))
#define ROTR(x,s,w) ((RC5_WORD)(SHR1((x),(s))|SHL1((x),(s),(w))))
函數(shù)定義
2、解密函數(shù)定義如下:
void RC5_Block_Decrypt (RC5_WORD *S,int R,char *in,char *out)
{
int i;
RC5_WORD A,B;
A = in[0] & 0xFF;
A += (in[1] & 0xFF) << 8;
A += (in[2] & 0xFF) << 16;
A += (in[3] & 0xFF) << 24;
B = in[4] & 0xFF;
B += (in[5] & 0xFF) << 8;
B += (in[6] & 0xFF) << 16;
B += (in[7] & 0xFF) << 24;
for(i=R;i>=1;i--)
{
B=ROTR((B-S[2*i+1]),A,W);
B=B^A;
A=ROTR((A-S[2*i]),B,W);
A=A^B;
}
B=B-S[1];
A=A-S[0];
out[0] = (A >> 0) & 0xFF;
out[1] = (A >> 8) & 0xFF;
out[2] = (A >> 16) & 0xFF;
out[3] = (A >> 24) & 0xFF;
out[4] = (B >> 0) & 0xFF;
out[5] = (B >> 8) & 0xFF;
out[6] = (B >> 16) & 0xFF;
out[7] = (B >> 24) & 0xFF;
return;
}/*End of RC5_Block_Decrypt */
int RC5_CBC_Decrypt_Init (pAlg, pKey)
rc5CBCAlg *pAlg;
rc5UserKey *pKey;
{
if ((pAlg == ((rc5CBCAlg *) 0)) || (pKey == ((rc5UserKey *) 0)))
return (0);
RC5_Key_Expand (pKey->keyLength, pKey->keyBytes,pAlg->R, pAlg->S);
return (RC5_CBC_SetIV(pAlg, pAlg->I));
}
int RC5_CBC_Decrypt_Update(rc5CBCAlg *pAlg,int N,char *C,int *plainLen,char *P)
{
int plainIndex,cipherIndex,j;
plainIndex=cipherIndex=0;
for(j=0;j<BB;j++)
{
P[plainIndex]=pAlg->chainBlock[j];
plainIndex++;
}
plainIndex=0;
{
if (pAlg->inputBlockIndex<BB)
{
pAlg->inputBlock[pAlg->inputBlockIndex]=C[cipherIndex];
pAlg->inputBlockIndex++;
cipherIndex++;
}
if (pAlg->inputBlockIndex==BB)
{
pAlg->inputBlockIndex=0;
RC5_Block_Decrypt (pAlg->S,pAlg->R,pAlg->inputBlock,pAlg->chainBlock);
for(j=0;j<BB;j++)
{
if (plainIndex<BB)
P[plainIndex]^=pAlg->chainBlock[j];
else
P[plainIndex]=C[cipherIndex-16+j]^pAlg->chainBlock[j];
plainIndex++;
}
}
}
*plainLen=plainIndex;
return (1);
} /*End of RC5_CBC_Decrypt_Update*/
常見(jiàn)模式
編輯
以上的操作只是針對(duì)的一個(gè)明文分組的,對(duì)于分組加密算法有以下幾種比較常見(jiàn)的
分組密碼模式:
電子密碼本
電子密碼本(Electronic Code Book,,ECB)模式是使用分組
密碼算法的最明顯的方式,其使用方式是一個(gè)明文分組加密成一個(gè)密文分組,相同的明文分組永遠(yuǎn)被加密成相同的密文分組,在理論上制造這樣的一個(gè)密碼本是可行的,但實(shí)際上要進(jìn)行大量的預(yù)計(jì)算耗費(fèi)存儲(chǔ)空間,最容易的運(yùn)行模式是每個(gè)明文分組可被獨(dú)立地進(jìn)行加密,但受分組
重放攻擊。
密碼分組鏈接
密碼分組鏈接模式(Cipher Block Chaining,CBC)模式中,明文被加密之前要與前面的密文進(jìn)行異或運(yùn)算,如果前面的明文分組不同才能將完全相同的明文分組加密成不同的密文分組,這會(huì)給密碼分析者提供有用的線索,為了防止這種情況發(fā)生使用一個(gè)隨機(jī)
數(shù)據(jù)分組作為加密的第一個(gè)分組叫作初始化向量(initialization Vector,IV),這樣就可以把完全相同的信息加密成不同的密文消息。
密碼反饋
密碼反饋模式(Cipher-Feedback,
CFB)是把分組
密碼算法用于自
同步序列密碼的一種方法,在CBC模式下,整個(gè)
數(shù)據(jù)分組在接收完之后才能進(jìn)行加密,在此模式下數(shù)據(jù)可以在比分組小的多的單元里進(jìn)行處理。
輸出反饋
輸出反饋模式(Output-Feedback,
OFB)是將
分組密碼用于
同步序列密碼運(yùn)行的一種方法,它有一個(gè)很好的特性就是在明文存在前的大部分工作可以離線進(jìn)行。以上幾種模式中密碼分組鏈接模式是在
安全協(xié)議中使用的最為普遍,在無(wú)線應(yīng)用協(xié)議中安全層定義的分組
加密算法都是CBC模式。幾大手機(jī)廠家如Nokia,Motorala,Erison的WAP手機(jī)的首選的分組加密算法就是RC5。
自制基于rc5的一個(gè)文件加密小程序
//#include<time.h>
#include<afx.h>
#include <stdio.h>
#define KEYSIZE 16 /* size of key, in bytes */
#if !defined UINT
typedef unsigned int UINT; /* Should be 32-bit = 4 bytes */
#endif
typedef enum { ShiftLeft, ShiftRight } ShiftDir;
typedef enum { KeyWords = KEYSIZE / 4,
NumRounds = 15, /* Number of cryptographic rounds */
TableSize = 32 /* size of table = 2 * (NumRounds + 1) */
} bogus;
UINT Table[ TableSize ];
UINT L[KeyWords];
UINT ROT(const UINT x, const UINT y, const ShiftDir dir)
{
const unsigned int ShiftAmt = (y & (unsigned int)0x1f);
const unsigned int ShiftBack = 0x20 - ShiftAmt;
unsigned int result;
if (dir == ShiftLeft)
result = (x << ShiftAmt) | (x >> ShiftBack);
else
result = (x >> ShiftAmt) | (x << ShiftBack);
return result;
} /* ROT */
//設(shè)置密鑰
void SetKey( unsigned char KeyChar )
{
static unsigned int KeyCntr;
static unsigned int Shift;
int ix = KeyCntr >> 2;
/* this is simply a machine independent way of setting L[i] to
KeyChar[i], without being affect by "endianess". */
L[ ix ] = (L[ ix ] & (~(0xff << Shift))) | (KeyChar << Shift);
Shift = (Shift + 8) & 0x1f;
KeyCntr = (KeyCntr + 1) & (KEYSIZE - 1);
/* This Depends on KEYSIZE being a power of two. The & will cause the
KeyCntr to wrap and only have values in the range 0..KEYSIZE-1. */
} /* RC5_Crypto */
//雙字加密函數(shù)
/* 2 UINT: input plain text, output encrypted text */
void encrypt(UINT *PlainText, UINT *CryptoText)
{
UINT i, temp;
UINT A;
UINT B;
A = PlainText[0] + Table[0];
B = PlainText[1] + Table[1];
for (i = 1; i <= NumRounds; i++) {
temp = i << 1;
A = ROT(A^B, B, ShiftLeft) + Table[temp];
B = ROT(A^B, A, ShiftLeft) + Table[temp+1];
}
CryptoText[0] = A;
CryptoText[1] = B;
} /* RC5_Cypto::encrypt */
//雙字解密函數(shù)
/* 2 UINT input encrypted text, output plain text */
void decrypt(UINT *CryptoText, UINT *PlainText)
{
UINT i, temp;
UINT B;
UINT A;
B = CryptoText[1];
A = CryptoText[0];
for (i=NumRounds; i > 0; i--) {
temp = i << 1;
B = ROT(B - Table[temp+1],A, ShiftRight)^A;
A = ROT(A - Table[temp], B, ShiftRight)^B;
}
PlainText[1] = B-Table[1];
PlainText[0] = A-Table[0];
} /* decrypt */
//初始化函數(shù)
void setup() /* secret input key K[0...KEYSIZE-1] */
{
/* magic constants (courtesty of RSA) */
static const UINT ROM[ TableSize ] = { 0xb7e15163, 0x5618cb1c, 0xf45044d5,
0x9287be8e, 0x30bf3847, 0xcef6b200,
0x6d2e2bb9, 0x0b65a572, 0xa99d1f2b,
0x47d498e4, 0xe60c129d, 0x84438c56,
0x227b060f, 0xc0b27fc8, 0x5ee9f981,
0xfd21733a, 0x9b58ecf3, 0x399066ac,
0xd7c7e065, 0x75ff5a1e, 0x1436d3d7,
0xb26e4d90, 0x50a5c749, 0xeedd4102,
0x8d14babb, 0x2b4c3474, 0xc983ae2d,
0x67bb27e6, 0x05f2a19f, 0xa42a1b58,
0x42619511, 0xe0990eca };
UINT i;
UINT A;
UINT B;
UINT j;
UINT k;
/* Copy "ROM" into "RAM" */
for (i=0; i < TableSize; i++)
Table[i] = ROM[i];
/* 3*t > 3*KeyWords */
A = 0;
B = 0;
i = 0;
j = 0;
for (k=0; k < 3*TableSize; k++) {
Table[i] = ROT(Table[i]+(A+B),3, ShiftLeft);
A = Table[i];
L[j] = ROT(L[j]+(A+B),(A+B), ShiftLeft);
B = L[j];
i= (i+1) & (TableSize-1); /* using '&' for % only works for powers of 2 */
j= (j+1) & (KeyWords-1);
}
} /* setup */
//文件加密函數(shù)
bool EncryptFile(char *pchFileName)
{
CFile fileb4;
CFile encryptfile;
CFileException e;
char pchEncryptFileName[100];
sprintf(pchEncryptFileName, "$%s", pchFileName);
//處理文件打開(kāi)的異常情形。
if( !fileb4.Open( pchFileName, CFile::modeRead | CFile::shareDenyNone, &e ) )
{
printf("%s could not be opened %d/n", pchFileName, e.m_cause);
return false;
}
if ( !encryptfile.Open( pchEncryptFileName, CFile::modeCreate | CFile::modeWrite, &e ))
{
printf("%s could not be created %d/n", pchEncryptFileName, e.m_cause);
fileb4.Close();
return false;
}
BYTE bBuffer[8];
BYTE bEncryptFileHead[8];
UINT uFileLen = fileb4.GetLength(); /* 文件長(zhǎng)度 */
memcpy(bEncryptFileHead, "ISDN", 4);
memcpy(bEncryptFileHead + 4, &uFileLen, 4);
encryptfile.Write(bEncryptFileHead, 8); /* 寫(xiě)文件頭 */
DWORD dwRead;
UINT uPlainData[2];
UINT uCryptoData[2];
do
{
memset(bBuffer, 0, 8);
dwRead = fileb4.Read(bBuffer, 8);
if ( dwRead > 0 && dwRead <= 8 )
{
memcpy(&uPlainData[0], bBuffer , 4);
memcpy(&uPlainData[1], bBuffer + 4, 4);
encrypt(uPlainData, uCryptoData); /* 加密雙字 */
encryptfile.Write(&uCryptoData[0], 4); /* 寫(xiě)入加密文件 */
encryptfile.Write(&uCryptoData[1], 4);
}
//else if ( 8 > dwRead && 1 <= dwRead )
//{
//
//}
}
while (dwRead > 0); /* 循環(huán)條件 */
// Close both files
encryptfile.Close();
fileb4.Close();
//刪除明文文件,重命名加密文件。
try
{
CFile::Remove(pchFileName);
try
{
CFile::Rename( pchEncryptFileName, pchFileName );
}
catch(CFileException* pExRename )
{
printf( "File %s not found %d/n", pchEncryptFileName, pExRename->m_cause);
pExRename->Delete();
}
}
catch (CFileException* pExRemove)
{
printf( "File %s cannot be removed/n", pchFileName);
pExRemove->Delete();
}
return true;
}
//文件解密函數(shù)
bool DecryptFile(char *pchFileName, unsigned int uiOffset, void *pMem, unsigned int &uiMemSize)
{
CFile fileb4;
CFile decryptfile;
CFileException e;
char pchDecryptFileName[100]; //文件的全路徑名稱。
sprintf(pchDecryptFileName, "$%s", pchFileName); //賦值新文件名。
// 處理文件打開(kāi)的異常情形。
if( !fileb4.Open( pchFileName, CFile::modeRead | CFile::shareDenyNone, &e ) )
{
printf("%s could not be opened %d/n", pchFileName, e.m_cause);
return false;
}
if ( !decryptfile.Open( pchDecryptFileName, CFile::modeCreate | CFile::modeWrite, &e ))
{
printf("%s could not be created %d/n", pchDecryptFileName, e.m_cause);
fileb4.Close();
return false;
}
UINT uFileLen = fileb4.GetLength(); /* 文件長(zhǎng)度 */
if ( uFileLen % 8 != 0 )
{
printf("%s 's length mod 8 is not 0/n", pchFileName);
// 若文件的長(zhǎng)度不是8的倍數(shù),說(shuō)明文件出了問(wèn)題,不能解密。
decryptfile.Close();
fileb4.Close();
return false;
}
DWORD dwRead;
BYTE bEncryptFileHead[8];
UINT uDecryptFileLen;
dwRead = fileb4.Read(bEncryptFileHead, 8); /* 讀取文件頭部的8個(gè)字節(jié) */
if ( 8 == dwRead )
{
// 判斷頭部是否正確。
if ( bEncryptFileHead[0] != 'I' )
{
decryptfile.Close();
fileb4.Close();
return false;
}
if ( bEncryptFileHead[1] != 'S' )
{
decryptfile.Close();
fileb4.Close();
return false;
}
if ( bEncryptFileHead[2] != 'D' )
{
decryptfile.Close();
fileb4.Close();
return false;
}
if ( bEncryptFileHead[3] != 'N' )
{
decryptfile.Close();
fileb4.Close();
return false;
}
//判斷文件的長(zhǎng)度是否改變。
memcpy(&uDecryptFileLen, bEncryptFileHead + 4, 4 );
if ( uDecryptFileLen + 8 > uFileLen || uDecryptFileLen + 16 < uFileLen )
{
decryptfile.Close();
fileb4.Close();
return false;
}
}
else
{
//文件的頭部小于8個(gè)字節(jié),直接退出。
decryptfile.Close();
fileb4.Close();
return false;
}
fileb4.Seek( uiOffset, CFile::current); /* 定位到文件的當(dāng)前位置。
BYTE bBuffer[8];
UINT uPlainData[2];
UINT uCryptoData[2];
UINT uDecryptFileLenHasWrite = 0;
do
{
memset(bBuffer, 0, 8); /* 分配內(nèi)存 */
dwRead = fileb4.Read(bBuffer, 8);
if ( dwRead > 0 && dwRead <= 8 )
{
memcpy(&uCryptoData[0], bBuffer , 4);
memcpy(&uCryptoData[1], bBuffer + 4, 4);
decrypt(uCryptoData,uPlainData); /* 解密雙字 */
if ( uDecryptFileLen - uDecryptFileLenHasWrite >= 8 )
{
decryptfile.Write(&uPlainData[0], 4); /* 寫(xiě)入文件。 */
decryptfile.Write(&uPlainData[1], 4);
uDecryptFileLenHasWrite += 8;
}
else if ( uDecryptFileLen - uDecryptFileLenHasWrite >= 4 )
{
decryptfile.Write(&uPlainData[0], 4);
uDecryptFileLenHasWrite += 4;
decryptfile.Write(&uPlainData[1], uDecryptFileLen - uDecryptFileLenHasWrite);
uDecryptFileLenHasWrite = uDecryptFileLen;
}
else
{
decryptfile.Write(&uPlainData[0], uDecryptFileLen - uDecryptFileLenHasWrite);
uDecryptFileLenHasWrite = uDecryptFileLen;
}
}
//else if ( 8 > dwRead && 1 <= dwRead )
//{
//
//}
}
while (dwRead > 0); /* 循環(huán)條件 */
// Close both files
decryptfile.Close();
fileb4.Close();
return true;
}
//主函數(shù)
void main()
{
if (sizeof(UINT)!=4)
{
printf("RC5 error: UINT has %d bytes./n",sizeof(UINT));
}
UINT j;
char *keystr = "0123456789ABCDEF";
for (j=0; j < KEYSIZE; j++)
{
SetKey((unsigned char)keystr[j]);
}
/* Setup, encrypt, and decrypt */
setup();
////////////////////////////////////////////////////////////////////////
//
EncryptFile("Test.dat");
UINT uMemLen;
DecryptFile("Test.dat", 0, NULL, uMemLen);
return 0;
}