jodd.util.crypt.Threefish.Threefish的坑

接手的框架,做开发,maven项目

        <dependency>
            <groupId>org.jodd</groupId>
            <artifactId>jodd-all</artifactId>
            <version>4.3.2</version>
        </dependency>

工具类

@Component
public class SessionUtil {

    private static final Threefish ENCRYPTOR = new Threefish(
            Threefish.BLOCK_SIZE_BITS_256);/**
     * Reads auth cookie and returns stored string array from cookie data. Returns
     * <code>null</code> if cookie does not exist. Throws an exception if cookie
     * data is invalid or corrupted.
     */
    public static String[] readAuthCookie(HttpServletRequest request) {
        Cookie cookie = ServletUtil.getCookie(request, REMEMBERME_COOKIE_NAME);
        if (cookie == null) {
            return null;
        }
        String[] values = StringUtil.splitc(cookie.getValue(), COOKIE_DELIMETER);
        for (int i = 0; i < values.length; i++) {
            byte[] decoded = Base64.decode(values[i]);
            values[i] = ENCRYPTOR.decryptString(decoded);
        }
        return values;
    }
}

中间有一些方法,不全粘贴了。

 

使用 jodd.util.crypt.Threefish.Threefish 当初成员变量ENCRYPTOR,使用decryptString方法,

查看源码,发现Threefish 是一个线程不安全的类,若直接使用上面的方法,单个请求过来时没有问题的,不过只要存在并发,可能就会出问题(出现问题的概率还是比较大的)

 

个人没有进行太大的更改,只是进行了简单的同步处理(注意,我SessionUtil类中用到ENCRYPTOR成员变量的地方就两处,我两处使用ENCRYPTOR时都进行了加锁处理.。如果jodd.util.crypt.Threefish.Threefish写成单例(或当初单例),多个类共同使用,这种处理方式是不行的,仍然线程不安全)

处理方式其实是没什么必要写的,就是怕有人,或者以后的我再遇到这个坑,就记录一下

package com.talkweb.bjtobacco.interceptor;

import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.auth0.jwt.interfaces.Claim;
import com.talkweb.bjtobacco.config.Config;
import com.talkweb.bjtobacco.pojo.SystemUser;
import com.talkweb.bjtobacco.service.SystemUserService;
import com.talkweb.pangu.base.common.jwt.JwtToken;

import jodd.servlet.ServletUtil;
import jodd.util.Base64;
import jodd.util.StringUtil;
import jodd.util.crypt.Threefish;

/**
 * Session相关工具类
 * 
 * @author zouxixi
 * @date 2019-04-05
 */
@Component
public class SessionUtil {

    private static final ReentrantLock lock = new ReentrantLock();/**
     * cookie数据加密处理
     */
    private static final Threefish ENCRYPTOR = new Threefish(
            Threefish.BLOCK_SIZE_BITS_256);/**
     * Reads auth cookie and returns stored string array from cookie data. Returns
     * <code>null</code> if cookie does not exist. Throws an exception if cookie
     * data is invalid or corrupted.
     */
    public static String[] readAuthCookie(HttpServletRequest request) {
        Cookie cookie = ServletUtil.getCookie(request, REMEMBERME_COOKIE_NAME);
        if (cookie == null) {
            return null;
        }
        String[] values = StringUtil.splitc(cookie.getValue(), COOKIE_DELIMETER);
        for (int i = 0; i < values.length; i++) {
            byte[] decoded = Base64.decode(values[i]);
            lock.lock();
            try {
                values[i] = ENCRYPTOR.decryptString(decoded);
            }catch(Exception e) {
                e.getStackTrace();
            }finally {
                lock.unlock();
            }

        }
        return values;
    }

}

 

 

源码

// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.util.crypt;

import jodd.util.Bits;
import jodd.util.StringPool;
import jodd.util.StringUtil;

/**
 * Threefish cipher.
 * http://www.schneier.com/threefish.html
 */
public class Threefish extends BlockCipher {

    // === GENERAL CONSTANTS ===

    private static final long EXTENDED_KEY_SCHEDULE_CONST = 6148914691236517205L;

    public static final int BLOCK_SIZE_BITS_256 = 256;
    public static final int BLOCK_SIZE_BITS_512 = 512;
    public static final int BLOCK_SIZE_BITS_1024 = 1024;

    private static final int ROUNDS_72 = 72;
    private static final int ROUNDS_80 = 80;

    private static final int WORDS_4 = 4;
    private static final int WORDS_8 = 8;
    private static final int WORDS_16 = 16;

    private static final int TWEAK_VALUES = 3;
    private static final int SUBKEY_INTERVAL = 4;

    // === VALUES FOR THE WORD PERMUTATION ===

    /**
     * Word permutation constants for PI(i) for Nw = 4.
     */
    private static final int[] PI4 = {0, 3, 2, 1};
    /**
     * Word permutation constants for PI(i) for Nw = 8.
     */
    private static final int[] PI8 = {2, 1, 4, 7, 6, 5, 0, 3};
    /**
     * Word permutation constants for PI(i) for Nw = 16.
     */
    private static final int[] PI16 = {0, 9, 2, 13, 6, 11, 4, 15, 10, 7, 12, 3, 14, 5, 8, 1};

    // === VALUES FOR THE REVERSE WORD PERMUTATION ===

    /**
     * Reverse word permutation constants for PI(i) for Nw = 4.
     */
    private static final int[] RPI4 = {0, 3, 2, 1}; // note: RPI4 == PI4
    /**
     * Reverse word permutation constants for PI(i) for Nw = 8.
     */
    private static final int[] RPI8 = {6, 1, 0, 7, 2, 5, 4, 3};
    /**
     * Reverse word permutation constants for PI(i) for Nw = 16.
     */
    private static final int[] RPI16 = {0, 15, 2, 11, 6, 13, 4, 9, 14, 1, 8, 5, 10, 3, 12, 7};

    // === ROTATION CONSTANTS FOR THE MIX FUNCTION ===

    private static final int DEPTH_OF_D_IN_R = 8;
    /**
     * Rotational constants Rd,j for Nw = 4.
     */
    private static final int[][] R4 = {
            {5, 56},
            {36, 28},
            {13, 46},
            {58, 44},
            {26, 20},
            {53, 35},
            {11, 42},
            {59, 50}
    };

    /**
     * Rotational constants Rd,j for Nw = 8.
     */
    private static final int[][] R8 = {
            {38, 30, 50, 53},
            {48, 20, 43, 31},
            {34, 14, 15, 27},
            {26, 12, 58, 7},
            {33, 49, 8, 42},
            {39, 27, 41, 14},
            {29, 26, 11, 9},
            {33, 51, 39, 35}
    };

    /**
     * Rotation constants Rd,j for Nw = 16.
     */
    private static final int[][] R16 = {
            {55, 43, 37, 40, 16, 22, 38, 12},
            {25, 25, 46, 13, 14, 13, 52, 57},
            {33, 8, 18, 57, 21, 12, 32, 54},
            {34, 43, 25, 60, 44, 9, 59, 34},
            {28, 7, 47, 48, 51, 9, 35, 41},
            {17, 6, 18, 25, 43, 42, 40, 15},
            {58, 7, 32, 45, 19, 18, 2, 56},
            {47, 49, 27, 58, 37, 48, 53, 56},
    };

    // === FIELDS CREATED DURING INSTANTIATION FOR PERFORMANCE REASONS ===

    private final long[] t = new long[TWEAK_VALUES]; // initial tweak words including t2
    private final long[] x = new long[2];
    private final long[] y = new long[2];

    // === FINAL FIELDS DETERMINED BY BLOCKSIZE ===

    private final int blockSize; // block size (in bits)
    private final int nr; // number of rounds depending on block size

    // === FIELDS DETERMINED BY KEY SIZE DURING INIT() ===

    private long[] k; // initial key words including knw
    private int nw; // number of key words excluding knw
    private int[] pi; // word permutation pi (depends on number of words <=> block size)
    private int[] rpi; // reverse word permutation rpi (depends on number of words <=> blocksize)
    private int[][] r; // rotational constants (depends on number of words <=> block size)

    // === FIELDS DETERMINED BY KEY SIZE DURING INIT() FOR PERFORMANCE REASONS ===

    // NOTE next fields use lazy instantiation
    // NOTE performance/memory: can we even use the same array? let's not before testing
    private long[] vd;
    private long[] ed;
    private long[] fd;
    private long[] ksd;

    /**
     * Threefish implementation using the specified blocksize in bits.
     *
     * @param blockSize either 256, 512 or 1024 (bits)
     */
    public Threefish(final int blockSize) {
        super(blockSize);
        this.blockSize = blockSize;

        switch (blockSize) {
            case BLOCK_SIZE_BITS_256:
            case BLOCK_SIZE_BITS_512:
                nr = ROUNDS_72;
                break;
            case BLOCK_SIZE_BITS_1024:
                nr = ROUNDS_80;
                break;
            default:
                throw new IllegalArgumentException("Illegal blocksize, use 256, 512 or 1024 bit values as blocksize");
        }
    }

    /**
     * Threefish implementation using the specified blocksize in bits, specifying the number of rounds directly instead
     * of using the default number of rounds depending on the blockSize. Mainly used for (performance) testing
     * purposes.
     *
     * @param blockSize either 256, 512 or 1024 (bits)
     * @param rounds the number of rounds 1..2^31, must be multiple of 4
     */
    public Threefish(final int blockSize, final int rounds) {
        super(blockSize);
        this.blockSize = blockSize;

        switch (blockSize) {
            case BLOCK_SIZE_BITS_256:
            case BLOCK_SIZE_BITS_512:
            case BLOCK_SIZE_BITS_1024:
                break;
            default:
                throw new IllegalArgumentException("Illegal blocksize, use 256, 512 or 1024 bit values as blocksize");
        }

        if (rounds <= 0 || rounds % 4 != 0) {
            throw new IllegalArgumentException("Number of rounds should be at least 1 and should be a multiple of 4");
        }

        nr = rounds;
    }


    /**
     * Initialize the cipher using the key and the tweak value.
     *
     * @param key the Threefish key to use
     * @param tweak the tweak values to use
     */
    public void init(final long[] key, final long[] tweak) {
        final int newNw = key.length;

        // only create new arrays if the value of N{w} changes (different key size)
        if (nw != newNw) {
            nw = newNw;

            switch (nw) {
                case WORDS_4:
                    pi = PI4;
                    rpi = RPI4;
                    r = R4;
                    break;
                case WORDS_8:
                    pi = PI8;
                    rpi = RPI8;
                    r = R8;
                    break;
                case WORDS_16:
                    pi = PI16;
                    rpi = RPI16;
                    r = R16;
                    break;
                default:
                    throw new RuntimeException("Invalid threefish key");
            }

            this.k = new long[nw + 1];

            // instantiation of these fields here for performance reasons
            vd = new long[nw]; // v is the intermediate value v{d} at round d
            ed = new long[nw]; // ed is the value of e{d} at round d
            fd = new long[nw]; // fd is the value of f{d} at round d
            ksd = new long[nw]; // ksd is the value of k{s} at round d
        }

        System.arraycopy(key, 0, this.k, 0, key.length);

        long knw = EXTENDED_KEY_SCHEDULE_CONST;
        for (int i = 0; i < nw; i++) {
            knw ^= this.k[i];
        }
        this.k[nw] = knw;

        // set tweak values
        t[0] = tweak[0];
        t[1] = tweak[1];
        t[2] = t[0] ^ t[1];
    }

    /**
     * Implementation of the E(K, T, P) function.
     * The K and T values should be set previously using the init() method. This version is the 64 bit implementation
     * of Threefish.
     *
     * @param p the initial plain text
     * @param c the final value defined as value v{d} where d = N{r}
     */
    public void blockEncrypt(final long[] p, final long[] c) {
        // initial value = plain
        System.arraycopy(p, 0, vd, 0, nw);

        for (int d = 0; d < nr; d++) { // do the rounds
            // calculate e{d,i}
            if (d % SUBKEY_INTERVAL == 0) {
                final int s = d / SUBKEY_INTERVAL;

                keySchedule(s);

                for (int i = 0; i < nw; i++) {
                    ed[i] = vd[i] + ksd[i];
                }
            } else {
                System.arraycopy(vd, 0, ed, 0, nw);
            }

            for (int j = 0; j < nw / 2; j++) {
                x[0] = ed[j * 2];
                x[1] = ed[j * 2 + 1];

                mix(j, d);

                fd[j * 2] = y[0];
                fd[j * 2 + 1] = y[1];
            }

            for (int i = 0; i < nw; i++) {
                vd[i] = fd[pi[i]];
            }

        }

        // do the last keyschedule
        keySchedule(nr / SUBKEY_INTERVAL);

        for (int i = 0; i < nw; i++) {
            c[i] = vd[i] + ksd[i];
        }
    }

    /**
     * Implementation of the MIX function.
     *
     * @param j the index in the rotation constants
     * @param d the round
     */
    private void mix(final int j, final int d) {
        y[0] = x[0] + x[1];
        final long rotl = r[d % DEPTH_OF_D_IN_R][j];
        // java left rotation for a long
        y[1] = (x[1] << rotl) | (x[1] >>> (Long.SIZE - rotl));
        y[1] ^= y[0];
    }


    /**
     * Implementation of the D(K, T, C) function.
     * The K and T values should be set previously using the init() method. This version is the 64 bit implementation
     * of Threefish.
     *
     * @param c the cipher text
     * @param p the plain text
     */
    public void blockDecrypt(final long[] c, final long[] p) {
        // initial value = plain
        System.arraycopy(c, 0, vd, 0, nw);

        for (int d = nr; d > 0; d--) { // do the rounds
            // calculate e{d,i}
            if (d % SUBKEY_INTERVAL == 0) {
                final int s = d / SUBKEY_INTERVAL;
                keySchedule(s); // calculate same keys

                for (int i = 0; i < nw; i++) {
                    fd[i] = vd[i] - ksd[i];
                }

            } else {
                System.arraycopy(vd, 0, fd, 0, nw);
            }

            for (int i = 0; i < nw; i++) {
                ed[i] = fd[rpi[i]];
            }

            for (int j = 0; j < nw / 2; j++) {
                y[0] = ed[j * 2];
                y[1] = ed[j * 2 + 1];

                demix(j, d - 1);

                vd[j * 2] = x[0];
                vd[j * 2 + 1] = x[1];
            }
        }

        // do the first keyschedule
        keySchedule(0);

        for (int i = 0; i < nw; i++) {
            p[i] = vd[i] - ksd[i];
        }
    }

    /**
     * Implementation of the un-MIX function.
     */
    private void demix(final int j, final int d) {
        y[1] ^= y[0];
        final long rotr = r[d % DEPTH_OF_D_IN_R][j]; // NOTE performance: darn, creation on stack!
        // right shift
        x[1] = (y[1] << (Long.SIZE - rotr)) | (y[1] >>> rotr);
        x[0] = y[0] - x[1];
    }


    /**
     * Creates the subkeys.
     *
     * @param s the value of the round devided by 4
     */
    private void keySchedule(final int s) {
        for (int i = 0; i < nw; i++) {
            // just put in the main key first
            ksd[i] = k[(s + i) % (nw + 1)];

            // don't add anything for i = 0,...,Nw - 4
            if (i == nw - 3) { // second to last
                ksd[i] += t[s % TWEAK_VALUES];
            } else if (i == nw - 2) { // first to last
                ksd[i] += t[(s + 1) % TWEAK_VALUES];
            } else if (i == nw - 1) { // last
                ksd[i] += s;
            }
        }
    }

    // ---------------------------------------------------------------- user friendly methods

    /**
     * Initializes cipher in a simple way.
     */
    public void init(final String keyMessage, final long tweak1, final long tweak2) {
        long[] tweak = new long[] {tweak1, tweak2};
        byte[] key = new byte[blockSize / Byte.SIZE];
        byte[] keyData = StringUtil.getBytes(keyMessage);
        System.arraycopy(keyData, 0, key, 0, key.length < keyData.length ? key.length : keyData.length);
        init(bytesToLongs(key), tweak);
    }

    /**
     * Encrypts a block.
     */
    @Override
    public byte[] encryptBlock(final byte[] content, final int offset) {
        
        long[] contentBlock = bytesToLongs(content, offset, blockSizeInBytes);
        
        long[] encryptedBlock = new long[blockSize / Long.SIZE];

        blockEncrypt(contentBlock, encryptedBlock);

        return longsToBytes(encryptedBlock);
    }

    @Override
    public byte[] decryptBlock(final byte[] encryptedContent, final int offset) {
        long[] encryptedBlock = bytesToLongs(encryptedContent, offset, blockSizeInBytes);

        long[] decryptedBlock= new long[encryptedBlock.length];

        blockDecrypt(encryptedBlock, decryptedBlock);

        return longsToBytes(decryptedBlock);
    }

    /**
     * Encrypts a string.
     */
    public byte[] encryptString(final String plain) {
        return encrypt(StringUtil.getBytes(plain));
    }

    /**
     * Decrypts a string.
     */
    public String decryptString(final byte[] encrypted) {
        return StringUtil.newString(decrypt(encrypted), StringPool.UTF_8);
    }

    // ---------------------------------------------------------------- util
    
    protected static long[] bytesToLongs(final byte[] ba) {
        return bytesToLongs(ba, 0, ba.length);
    }
    /**
     * Converts segment of byte array into long array.
     */
    protected static long[] bytesToLongs(final byte[] ba, final int offset, final int size) {
        long[] result = new long[size >> 3];
        int i8 = offset;
        for (int i = 0; i < result.length; i++) {
            result[i] = Bits.getLong(ba, i8);
            i8 += 8;
        }
        return result;
    }

    protected static byte[] longsToBytes(final long[] la) {
        byte[] result = new byte[la.length << 3];
        int i8 = 0;
        for (long l : la) {
            Bits.putLong(result, i8, l);
            i8 += 8;
        }
        return result;
    }


}

 

posted on 2020-08-18 16:56  xingshouzhan  阅读(336)  评论(0编辑  收藏  举报

导航