TEA算法java实现和32个无符号位的问题

时间:2015-09-28 16:36:01

标签: java algorithm security encryption unsigned

我正在尝试在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));




    }


}

1 个答案:

答案 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的整数签名这一事实几乎没有什么区别。