如何使用Triple DES实现CFB8模式以使用CryptoJS进行解密

时间:2014-12-22 07:43:34

标签: javascript java cryptojs cfb-mode

我必须解密在java服务器中加密的数据。 在使用triple des的java服务器加密文本中(模式:CFB8,填充:NoPadding) 对于解密,我尝试加密如java服务器 下面是发布的java源代码。

private final static String keyString = "123456789012345678901234";
private final static String ivString = "abcdefgh";


public static String encrypt(String data) throws Exception {


    KeySpec keySpec = new DESedeKeySpec(keyString.getBytes());
    SecretKey key = SecretKeyFactory.getInstance("DESede").generateSecret(keySpec);
    IvParameterSpec iv = new IvParameterSpec(ivString.getBytes());
    Cipher ecipher = Cipher.getInstance("DESede/CFB8/NoPadding");
    ecipher.init(Cipher.ENCRYPT_MODE, key, iv);

    byte[] valeur = data.getBytes("UTF-8");
    byte[] enc = ecipher.doFinal(valeur);

    return new String(Base64.encode(enc, Base64.DEFAULT), "UTF-8");
}

以下代码是我的代码。

var key="123456789012345678901234";
var iv = "abcdefgh";   
var iv1 = CryptoJS.enc.Utf8.parse(iv);   
var key1 = CryptoJS.enc.Utf8.parse(key);   
var encrypted = CryptoJS.TripleDES.encrypt("asdfg", key1, { 
    iv:iv1,
    mode:CryptoJS.mode.CFB,
    padding:CryptoJS.pad.NoPadding
});

但我无法得到两者相同的结果。

当我在Java代码中将“CFB8”更改为“CFB”时,我得到了相同的结果。

如何在CryptoJS中实现CFB8?

1 个答案:

答案 0 :(得分:0)

CFB是带移位寄存器的操作模式。加密和解密发生在与移位寄存器大小相同且小于块大小的段中。

问题是CryptoJS的CFB实现仅支持段大小与使用的块密码的块大小完全相同。这意味着当您使用AES时,它将是128位的段大小。

我已经实现了CFB的可变段大小版本,它支持2的幂的所有段大小,包括1位到块大小:

/**
 * Cipher Feedback block mode with segment size parameter according to
 * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf. 
 * The segment size can be anything from 1 bit up to the block size of the 
 * underlying block cipher.
 * 
 * Current limitation: only segment sizes that divide the block size evenly 
 * are supported.
 */
CryptoJS.mode.CFBb = (function () {
    var C = CryptoJS;
        CFBb = C.lib.BlockCipherMode.extend(),
        WordArray = C.lib.WordArray;

    /**
     * Shifts the array by n bits to the left. Zero bits are added as the 
     * least significant bits. This operation modifies the current array.
     * 
     * @param {WordArray} wordArray WordArray to work on
     * @param {int} n Bits to shift by
     * 
     * @returns the WordArray that was passed in
     */
    var bitshift = function(wordArray, n){
        var carry = 0,
            words = wordArray.words,
            wres,
            skipped = 0,
            carryMask;
        if (n > 0) {
            while(n > 31) {
                // delete first element:
                words.splice(0, 1);

                // add `0` word to the back
                words.push(0);

                n -= 32;
                skipped++;
            }
            if (n == 0) {
                // 1. nothing to shift if the shift amount is on a word boundary
                // 2. This has to be done, because the following algorithm computes 
                // wrong values only for n==0
                return carry;
            }
            for(var i = words.length - skipped - 1; i >= 0; i--) {
                wres = words[i];
                words[i] <<= n;
                words[i] |= carry;
                carry = wres >>> (32 - n);
            }
        } else if (n < 0) {
            while(n < -31) {
                // insert `0` word to the front:
                words.splice(0, 0, 0);

                // remove last element:
                words.length--;

                n += 32;
                skipped++;
            }
            if (n == 0) {
                // nothing to shift if the shift amount is on a word boundary
                return carry;
            }
            n = -n;
            carryMask = (1 << n) - 1;
            for(var i = skipped; i < words.length; i++) {
                wres = words[i] & carryMask;
                words[i] >>>= n;
                words[i] |= carry;
                carry = wres << (32 - n);
            }
        }
        return carry;
    };

    /**
     * Negates all bits in the WordArray. This manipulates the given array.
     * 
     * @param {WordArray} wordArray WordArray to work on
     * 
     * @returns the WordArray that was passed in
     */
    var neg = function(wordArray){
        var words = wordArray.words;
        for(var i = 0; i < words.length; i++) {
            words[i] = ~words[i];
        }
        return wordArray;
    };

    CFBb.Encryptor = CFBb.extend({
        processBlock: function(words, offset){
            processBlock.call(this, words, offset, true);
        }
    });

    CFBb.Decryptor = CFBb.extend({
        processBlock: function(words, offset){
            processBlock.call(this, words, offset, false);
        }
    });

    function processBlock(words, offset, encryptor) {
        // Shortcuts
        var self = this;
        var cipher = self._cipher;
        var blockSize = cipher.blockSize * 32; // in bits
        var prev = self._prevBlock;
        var segmentSize = cipher.cfg.segmentSize; // in bits
        var i, j;
        var currentPosition;

        // Create a bit mask that has a comtinuous slice of bits set that is as big as the segment
        var fullSegmentMask = [];
        for(i = 31; i < segmentSize; i += 32) {
            fullSegmentMask.push(0xffffffff);
        }
        // `s` most signiicant bits are set:
        fullSegmentMask.push(((1 << segmentSize) - 1) << (32 - segmentSize));
        for(i = fullSegmentMask.length; i < words.length; i++) {
            fullSegmentMask.push(0);
        }

        fullSegmentMask = WordArray.create(fullSegmentMask);

        // some helper variables
        var slidingSegmentMask = fullSegmentMask.clone(),
            slidingSegmentMaskShifted = slidingSegmentMask.clone(),
            slidingNegativeSegmentMask,
            prevCT;

        // shift the mask according to the current offset
        bitshift(slidingSegmentMaskShifted, -offset * 32);

        for(i = 0; i < blockSize/segmentSize; i++) {
            if (!prev) {
                prev = self._iv.slice(0); // clone

                // Remove IV for subsequent blocks
                self._iv = undefined;
            } else {
                // Prepare the iteration by concatenating the unencrypted part of the previous block and the previous ciphertext

                prev = WordArray.create(prev);
                bitshift(prev, segmentSize);
                prev = prev.words;
                previousCiphertextSegment = self._ct;

                // fill previous ciphertext up to the block size
                while(previousCiphertextSegment.length < blockSize / 32) {
                    previousCiphertextSegment.push(0);
                }
                previousCiphertextSegment = WordArray.create(previousCiphertextSegment);

                // move to the back
                bitshift(previousCiphertextSegment, -blockSize + segmentSize);

                // put together
                for (var j = 0; j < prev.length; j++) {
                    prev[j] |= previousCiphertextSegment.words[j];
                }
            }

            currentPosition = offset * 32 + i * segmentSize;

            // move segment in question to the front of the array
            var plaintextSlice = WordArray.create(words.slice(0));
            bitshift(plaintextSlice, currentPosition);

            if (!encryptor) {
                self._ct = plaintextSlice.words.slice(0, Math.ceil(segmentSize / 32));
            }

            var segKey = prev.slice(0); // clone
            cipher.encryptBlock(segKey, 0);

            // Encrypt segment
            for (j = 0; j < Math.ceil(segmentSize / 32); j++) {
                plaintextSlice.words[j] ^= segKey[j];
            }

            // Filter only the current segment
            for (j = 0; j < plaintextSlice.words.length; j++) {
                plaintextSlice.words[j] &= fullSegmentMask.words[j];
            }

            if (encryptor) {
                self._ct = plaintextSlice.words.slice(0, Math.ceil(segmentSize / 32));
            }

            // remove the segment from the plaintext array
            slidingNegativeSegmentMask = neg(slidingSegmentMaskShifted.clone());
            for (j = 0; j < words.length; j++) {
                words[j] &= slidingNegativeSegmentMask.words[j];
            }

            // move filtered ciphertext segment to back to the correct place
            bitshift(plaintextSlice, -currentPosition);

            // add filtered ciphertext segment to the plaintext/ciphertext array
            for (j = 0; j < words.length; j++) {
                words[j] |= plaintextSlice.words[j];
            }

            // shift the segment mask further along
            bitshift(slidingSegmentMask, -segmentSize);
            bitshift(slidingSegmentMaskShifted, -segmentSize);
        }
        self._prevBlock = prev;
    }

    return CFBb;
}());

您应该使用适当的填充。 CryptoJS默认使用PKCS#7填充。 CFB8的最佳填充根本就没有填充(你已经使用过了)。

示例:

var iv = CryptoJS.lib.WordArray.random(128/8);
var encrypted = CryptoJS.TripleDES.encrypt("message", key, {
    iv: iv, 
    mode: CryptoJS.mode.CFBb, 
    padding: CryptoJS.pad.NoPadding,
    segmentSize: 8
});
var recoveredPlaintext = CryptoJS.TripleDES.decrypt(encrypted, key, {
    iv: iv,
    mode: CryptoJS.mode.CFBb,
    padding: CryptoJS.pad.NoPadding,
    segmentSize: 8
});
console.log(recoveredPlaintext.toString(CryptoJS.enc.Utf8));

由于这是使用随机IV,因此查看Java和JavaScript实现是否兼容的唯一方法是在一个加密并在另一个方向解密。

请记住,由于IV是随机的,您需要将其与密文一起发送。由于它不需要保密,因此您可以轻松地将其添加到密文并在解密之前将其切片。

此代码是我在GitHub上的存储库中的自定义副本:artjomb/cryptojs-extension