我正在尝试在java中实现Tiny Encryption Algorithm(TEA)。由于算法将每个64位块划分为左右子块,其中每个子块根据this源为32无符号整数。
正如所料,我遇到了java不支持无符号32位整数的问题。我到处都收到格式错误。
所以我决定使用BigInteger,它给我带来了一个新问题。根据我的理解,由于TEA使用32位整数的移位和加法,因此应该将这些操作的结果保持在32位,因此密文也将是每个子块32位。但是,通过移位和添加,BigInteger没有为每个子块保留32位。实际上,我意外地得到了一个非常大的位作为密文输出。
所以我决定保留BigInteger,同时在单独的方法中实现移位,加法和减法。不幸的是,我没有得到正确的结果。如下所示,解密的密文不等于原始明文。我的问题有什么解决方案?我得到以下输出:
Original Plain Text:0x0123456789ABCDEF
CipherText:0xa0761126d09724fd
Decrypted CipherText is:0x8d5a4a234b3c6720
以下是我的代码。
import java.math.BigInteger;
public class TEA {
BigInteger [] K ; //128 bits key
private String plainText;
public static final BigInteger delta = new BigInteger("9e3779b9",16);
//constructor receives a string of plaintext and 128 bit key in hexadecimal
public TEA(String plainText, String key)
{
parseKey(key);
}
//constructor receives a hexadecimal
public TEA(String key)
{
parseKey(key);
}
//parses a 128 bit key, given in hexadecimal form, and store its value in 4 integers (total of 128 bits),
private void parseKey(String key)
{
if(key.substring(0,2).equals("0x"))
key= key.substring(2);
//validating input
if(key.length() != 32)
{
System.out.println("Invalid key size!");
return;
}
//dividing the key into 4 strings
String[] kStr = new String[4];
int index=-1;
for(int i=0; i<key.length(); i++)
{
if(i%8 == 0)
{
index++;
kStr[index]="";
}
kStr[index] = kStr[index] + key.charAt(i);
}
//converting the 4 hex strings into 4 integers
K= new BigInteger[4];
for(int i=0; i<4; i++)
K[i] = new BigInteger(kStr[i], 16);
}
//receives a plaintext block of 64 bits in hexadecimal to be encrypted
//returns the cipher block
String encryptBlock(String plainTextBlock)
{
if(plainTextBlock.substring(0,2).equals("0x"))
plainTextBlock= plainTextBlock.substring(2);
//validating input
if(plainTextBlock.length()!=16)
{
System.out.println("Invalid block size!");
return null;
}
//separating the string block into left and right blocks
String LStr = plainTextBlock.substring(0, 8); //left block (32 bit)
String RStr = plainTextBlock.substring(8); //right block (32 bit)
//converting left and right blocks to integers
BigInteger L = new BigInteger(LStr, 16);
BigInteger R = new BigInteger(RStr, 16);
BigInteger sum= new BigInteger("0");
//32 rounds
for(int i=0; i<32; i++)
{
sum = sum.add(delta);
L= sum(L, (sum(shiftLeft(R,4),K[0])) .xor(sum(R,sum)) .xor(sum(shiftRight(R,5),K[1]))) ;
R= sum(R, (sum(shiftLeft(L,4),K[2])) .xor(sum(L,sum)) .xor(sum(shiftRight(L,5),K[3]))) ;
//R= R.add( (shiftLeft(R,4).add(K[2])).xor(L.add(sum)).xor(shiftRight(L,5).add(K[3])) );
}
//joining back the blocks as hex
String cipherBlock = "0x"+L.toString(16)+R.toString(16)+"";
return cipherBlock;
}
//receives a ciphertext block of 64 bits in hexadecimal to be decrypted
//returns the plaintext block
String decryptBlock(String cipherBlock)
{
if(cipherBlock.substring(0,2).equals("0x"))
cipherBlock= cipherBlock.substring(2);
//validating input
if(cipherBlock.length()!=16)
{
System.out.println("Invalid block size!");
return null;
}
//separating the string block into left and right blocks
String LStr = cipherBlock.substring(0, 8); //left block (32 bit)
String RStr = cipherBlock.substring(8); //right block (32 bit)
//converting left and right blocks to integers
BigInteger L = new BigInteger(LStr, 16);
BigInteger R = new BigInteger(RStr, 16);
BigInteger sum= shiftLeft(delta,5);
//32 rounds
for(int i=0; i<32; i++)
{
R= subtract(R, (sum(shiftLeft(L,4),K[2])) .xor(sum(L,sum)) .xor(sum(shiftRight(L,5),K[3]))) ;
L= subtract(L, (sum(shiftLeft(R,4),K[0])) .xor(sum(R,sum)) .xor(sum(shiftRight(R,5),K[1]))) ;
//R= R.subtract( (L.shiftLeft(4).add(K[2])).xor(L.add(sum)).xor(L.shiftRight(5).add(K[3])) );
//L= L.subtract( (R.shiftLeft(4).add(K[0])).xor(R.add(sum)).xor(R.shiftRight(5).add(K[1])) );
sum = sum.subtract(delta);
}
//joining back the blocks as hex
String plainTextBlock = "0x"+L.toString(16)+R.toString(16)+"";
return plainTextBlock;
}
private BigInteger shiftLeft(BigInteger x, int steps)
{
BigInteger shifted=null;
boolean negative =false;
String xStr = x.toString(2);
//removing negative sign while shifting (currently)
if(xStr.charAt(0)=='-')
{
negative= true;
xStr = xStr.substring(1);
}
int additionalSize = 32- xStr.length();
for(int i=0; i<additionalSize; i++)
xStr= "0"+xStr;
for(int i=0; i<steps; i++)
{
xStr = xStr.substring(1);
xStr = xStr+"0";
}
//one last addition of negative sign if the number is negative
if(negative==true)
xStr= "-"+xStr;
//System.out.println(xStr);
shifted = new BigInteger(xStr,2);
return shifted;
}
private BigInteger shiftRight(BigInteger x, int steps)
{
BigInteger shifted=null;
boolean negative = false;
String xStr = x.toString(2);
//removing negative sign while shifting (currently)
if(xStr.charAt(0)=='-')
{
negative= true;
xStr = xStr.substring(1);
}
int additionalSize = 32- xStr.length();
for(int i=0; i<additionalSize; i++)
xStr= "0"+xStr;
for(int i=0; i<steps; i++)
{
xStr = xStr.substring(0,xStr.length()-1);
xStr = "0"+xStr;
}
//one last addition of negative sign if the number is negative
if(negative==true)
xStr= "-"+xStr;
shifted = new BigInteger(xStr,2);
return shifted;
}
private BigInteger sum(BigInteger a, BigInteger b)
{
BigInteger sum = a.add(b);
String sumStr = sum.toString(2);
if(sumStr.length()>32)
{
int diff = sumStr.length()- 32;
sumStr = sumStr.substring(diff);
}
BigInteger newSum = new BigInteger(sumStr,2);
return newSum;
}
private BigInteger subtract(BigInteger a, BigInteger b)
{
BigInteger sub = a.subtract(b);
String subStr = sub.toString(2);
if(subStr.length()>32)
{
int diff = subStr.length()- 32;
subStr = subStr.substring(diff);
}
BigInteger newSub = new BigInteger(subStr,2);
return newSub;
}
public static void main(String[] args)
{
String plainText="0x0123456789ABCDEF";
String key= "0xA56BABCD00000000FFFFFFFFABCDEF01";
TEA tea = new TEA(key);
String cipherText = tea.encryptBlock(plainText);
System.out.println("Original Plain Text:"+plainText);
System.out.println("CipherText:"+cipherText);
System.out.println("Decrypted CipherText is:"+tea.decryptBlock(cipherText));
}
}
答案 0 :(得分:1)
我认为没有理由使用BigIntegers,所以我在Java中尝试了自己的实现,这几乎是维基百科文章中C代码的逐字副本。它对我来说是正确的,虽然我没有测试来反对它。
public class TEAToy {
private final static int DELTA = 0x9e3779b9;
private final static int DECRYPT_SUM_INIT = 0xC6EF3720;
private final static long MASK32 = (1L << 32) - 1;
public static long encrypt(long in, int [] k) {
int v1 = (int) in;
int v0 = (int) (in >>> 32);
int sum = 0;
for (int i=0; i<32; i++) {
sum += DELTA;
v0 += ((v1<<4) + k[0]) ^ (v1 + sum) ^ ((v1>>>5) + k[1]);
v1 += ((v0<<4) + k[2]) ^ (v0 + sum) ^ ((v0>>>5) + k[3]);
}
return (v0 & MASK32) << 32 | (v1 & MASK32);
}
public static long decrypt(long in, int [] k) {
int v1 = (int) in;
int v0 = (int) (in >>> 32);
int sum = DECRYPT_SUM_INIT;
for (int i=0; i<32; i++) {
v1 -= ((v0<<4) + k[2]) ^ (v0 + sum) ^ ((v0>>>5) + k[3]);
v0 -= ((v1<<4) + k[0]) ^ (v1 + sum) ^ ((v1>>>5) + k[1]);
sum -= DELTA;
}
return (v0 & MASK32) << 32 | (v1 & MASK32);
}
正如您所看到的,Java的整数签名这一事实几乎没有什么区别。