编程语言
首页 > 编程语言> > Java-列移位(Columnar Transposition Cipher)算法实现版本一

Java-列移位(Columnar Transposition Cipher)算法实现版本一

作者:互联网

这是几年前写的旧文,此前发布Wordpress小站上,现在又重新整理。算是温故知新,后续会继续整理。如有错误望及时指出,在此感谢。

场景描述

从数据安全角度而言,有些数据不希望在传输或序列化过程中明文化。在对安全要求不高的场合,可以考虑列移位算法来实现。

算法说明

http://www.practicalcryptography.com/ciphers/columnar-transposition-cipher/

算法的实现过程有很多种,我选了比较简单的一种。

加密算法描述

  1. 准备一个密钥,长度不限,密文字符可以重复;
  2. 根据密钥长度生成二维矩阵,根据密钥的长度算出二维矩阵的行数和列数;
  3. 根据密钥字符ASCII顺序按列读取字符得到加密字符串;
  4. 假设密钥为german,则二维矩阵的列数为6,列的ASCII码顺序为[2,1,5,3,0,4];
  5. 明文是密钥长度的整数倍,不足整数倍部分用特定字符填充(可选,本文中未这样实现);

加密算法举例

明文:
defend*the*east*wall*of*the*castle
密文:
d*altsetslhtfht*elee*o*en*wfcdea*a
密钥 g e r m a n
密钥顺序 0 1 2 3 4 5
字符顺序 2 1 5 3 0 4
d e f e n d
***** t h e ***** e
a s t ***** w a
l l ***** o f *****
t h e ***** c a
s t l e

在生成密文时,按照字符顺序按列读取

n*wfc

etslht

以此类推来生成密文

解密算法描述

  1. 根据密钥长度确定二维矩阵列数;
  2. 根据密钥字符顺序得到解密填充列数据时候的顺序;
  3. 假设密钥为german,则二维矩阵列数为6,矩阵列的读取顺序为[4, 1, 0, 3, 5, 2];
  4. 按照矩阵列读取顺序,将密文依大小按顺序写入二维矩阵中;

解密算法举例

密文:
d*altsetslhtfht*elee*o*en*wfcdea*a
明文:
defend*the*east*wall*of*the*castle

生成明文时,按照密钥顺序写入

n*wfc,写入密钥顺序第四列;

etslht,写入密钥顺序第一列;

以此类推来生成明文

源码实现

依赖MAVEN

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.22</version>
</dependency>

import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.*;

/**
 *
 * @Classname ColumnarTranspositionCipherTest1
 * @Description 加密算法:列移位-Columnar Transposition Cipher
 */
public class ColumnarTranspositionCipherTest1 {

//    public static final String keyStr = "mamihong";
    public static final String keyStr = "german";

    public static void main(String[] args) {
        String inputString = "defend*the*east*wall*of*the*castle";
        System.out.println("inputString:" + inputString);

        System.out.println("---------加密----------");
        String encipher = encipher(inputString);
        System.out.println("encipher:" + encipher);

        System.out.println("--------解密-----------");
        System.out.println("decrypt:" + decrypt(encipher));
    }


    /**
     * 加密
     *
     * @param inputString
     * @return
     */
    public static String encipher(String inputString) {

        final char[] inputChars = inputString.toCharArray();
        final int inputLength = inputChars.length;

        //密文数组
        final KeyLink[] keyLinkArray = getKeyLinkArrays(keyStr);

        //矩阵的列宽
        final int fieldSize = keyLinkArray.length;

        //矩阵的行数
        final int rowSize = (inputLength / fieldSize) + (inputLength % fieldSize == 0 ? 0 : 1);

        //计算矩阵的长度
        //矩阵的宽度
        System.out.println("矩阵的行数:" + rowSize + ",矩阵的列数:" + fieldSize);

        //创建二维矩阵
        final char[][] charArrays = new char[rowSize][fieldSize];

        //用原文填充二维矩阵
        for (int i = 0; i < inputLength; i++) {
            int c_i = i / fieldSize;
            int c_j = i % fieldSize;
            charArrays[c_i][c_j] = inputChars[i];
        }

        StringBuffer sBuilder = new StringBuffer();
        for (int i = 0; i < keyLinkArray.length; i++) {
            KeyLink keyLink = keyLinkArray[i];

            //读出链表next的字符
            char[] s;
            if (Objects.nonNull(keyLink)) {
                int keyOriginIndex = keyLink.originIndex;

                s = readVerStr(charArrays, keyOriginIndex);
                if (Objects.nonNull(s)) {
                    sBuilder.append(s);
                }
                //System.out.println("keyChar:" + keyLink + ",keyLink:" + keyLink + ",i:" + i + ",从二维矩阵的列:" + keyOriginIndex + ",读完了.此时:" + sBuilder);
            }
        }

        return sBuilder.toString();
    }

    /**
     * 解密
     *
     * @return
     */
    public static String decrypt(String inputString) {

        final int inputLength = inputString.length();
        //生成密钥的链表结构
        KeyLink[] keyLinkArrays = getKeyLinkArrays(keyStr);

        //矩阵的列宽
        final int rSize = keyLinkArrays.length;

        //矩阵的行数
        final int fSize = (inputLength / rSize) + (inputLength % rSize == 0 ? 0 : 1);

        //计算矩阵的长度
        //矩阵的宽度
        System.out.println("矩阵的宽度:" + fSize + ",矩阵的深度:" + rSize);

        //创建二维矩阵
        final char[][] charArrays = new char[fSize][rSize];

        //将密文按照链表结构中的定义写入二维矩阵中
        int startIndex = 0;
        for (int i = 0; i < keyLinkArrays.length; i++) {
            KeyLink keyLink = keyLinkArrays[i];

            if (keyLink != null) {
                int keyOriginIndex = keyLink.originIndex;

                //确定当前行i对应的数据宽度
                int currSum = rSize * (fSize - 1) + (keyOriginIndex + 1);

                //表示实际数据列的宽度
                int realMatrixWidth = fSize;
                if (currSum > inputLength) {
                    realMatrixWidth = fSize - 1;
                }
                int stopIndex = startIndex + realMatrixWidth;

                String substring = inputString.substring(startIndex, stopIndex);
                //System.out.println("keyLink:" + keyLink + ",currSum:" + currSum + ",realMatrixWidth:" + realMatrixWidth + ",substring:" + substring);

                writeVerStr(charArrays, keyOriginIndex, substring.toCharArray());

                startIndex += substring.length();
            }
        }

        return ver2String(charArrays);
    }

    /**
     * 打印二维矩阵中某一列的内容
     *
     * @param chars
     * @return
     */
    public static String ver2String(char[][] chars) {
        if (chars.length > 0) {

            int lengthRow = chars.length;
            int lengthVertical = chars[0].length;

            StringBuffer sBuilder = new StringBuffer();
            for (int i = 0; i < lengthRow; i++) {
                for (int j = 0; j < lengthVertical; j++) {
                    if (!Character.isISOControl(chars[i][j])) {
                        char c = chars[i][j];
                        sBuilder.append(c);
                    }
                }
                System.out.println("ver2String i:" + i + ",body:" + new String(chars[i]));
            }

            return sBuilder.toString();
        } else {
            return null;
        }
    }

    /**
     * 竖读
     * 从二维矩阵中竖着读出第i列的所有内容
     *
     * @param chars
     * @param verIndex
     * @return
     */
    public static char[] readVerStr(char[][] chars, int verIndex) {
        if (chars.length > 0) {

            int lengthRow = chars.length;
            int lengthVertical = chars[0].length;

            if (verIndex < lengthVertical) {
                StringBuffer sBuilder = new StringBuffer();

                for (int i = 0; i < lengthRow; i++) {
                    char c = chars[i][verIndex];
                    if (!Character.isISOControl(c)) {
                        sBuilder.append(c);
                    }
                }

                return sBuilder.toString().toCharArray();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * 将char数组,写入二维char矩阵中,竖写
     *
     * @param chars
     * @param verIndex
     * @param cs
     */
    public static void writeVerStr(char[][] chars, int verIndex, char[] cs) {

        int lengthRow = chars.length;
        int lengthVertical = chars[0].length;

        for (int i = 0; i < lengthRow; i++) {
            if (cs.length > i && !Character.isISOControl(cs[i])) {
                char c = cs[i];
                chars[i][verIndex] = c;
            }
        }
    }

    /**
     * 根据密钥字符生成密钥对象数组
     *
     * @param token
     * @return
     */
    public static KeyLink[] getKeyLinkArrays(final String token) {

        final char[] keyChars = token.toCharArray();
        final int keyLength = keyChars.length;

        char[] sortedKeyChars = Arrays.copyOf(keyChars, keyChars.length);

        System.out.println("密钥排序前:" + Arrays.toString(sortedKeyChars));
        Arrays.sort(sortedKeyChars);
        System.out.println("密钥排序后:" + Arrays.toString(sortedKeyChars));

        KeyLink[] keyLinkArray = new KeyLink[keyLength];

        for (int i = 0; i < keyChars.length; i++) {
            //将char按照
            char keyChar = keyChars[i];

            KeyLink keyLink = new KeyLink(keyChar, i, -1);
            keyLinkArray[i] = keyLink;
        }

        for (int i = 0; i < sortedKeyChars.length; i++) {
            //根据排序的密文arrys返回的下标,查找KeyLink
            KeyLink[] foundKeyLink = getKeyLinkByCharName(keyLinkArray, sortedKeyChars[i]);
            //System.out.println("从[i]:" + i + ",sortedKeyChars[i]:" + sortedKeyChars[i] + ",找到:" + Arrays.toString(foundKeyLink));

            if (foundKeyLink != null) {
                for (int j = 0; j < foundKeyLink.length; j++) {
                    if (foundKeyLink[j] != null) {
                        if (foundKeyLink[j].sortedIndex >= 0) {
                            //如果已使用,从下一个密文的索引开始再查
                            continue;
                        } else {
                            //未使用
                            foundKeyLink[j].sortedIndex = i;
                            break;
                        }
                    }
                }
            }
        }

        for (int i = 0; i < keyLinkArray.length; i++) {
            System.out.println("getKeyLinkArrays i:" + i + ",keyLinkArray:" + keyLinkArray[i]);
        }

        return keyLinkArray;
    }

    /**
     * 根据字符查询相关的密钥对象
     *
     * @param keyLinkArray
     * @param charName
     * @return
     */
    public static KeyLink[] getKeyLinkByCharName(final KeyLink[] keyLinkArray, final char charName) {

        int ct = 0;
        for (int j = 0; j < keyLinkArray.length; j++) {
            //判断数据组是不是包括了这个字符,且是不是配置了sortedIndex
            if (keyLinkArray[j] != null && keyLinkArray[j].charName == charName) {
                ct++;
            }
        }

        int start = 0;
        KeyLink[] tmp = new KeyLink[ct];
        for (int j = 0; j < keyLinkArray.length || start < ct; j++) {
            //判断数据组是不是包括了这个字符,且是不是配置了sortedIndex
            if (keyLinkArray[j] != null && keyLinkArray[j].charName == charName) {
                tmp[start] = keyLinkArray[j];
                start++;
            }
        }
        if (ct == 0) {
            return null;
        } else {
            return tmp;
        }
    }

}

@AllArgsConstructor
@Data
class KeyLink {
    char charName;
    int originIndex;

    int sortedIndex;
    //KeyLink next;

    @Override
    public String toString() {
        return "KeyLink{" +
                "charName=" + charName +
                ", originIndex=" + originIndex +
                ", sortedIndex=" + sortedIndex +
                '}';
    }
}

总结

这个代码没有考虑矩阵数据对齐问题,需要在加解密时,需要额外进行数据对齐操作,可读性有待提升。

这种加密方式并不适合安全要求很高的场合。

标签:Java,int,Transposition,矩阵,char,length,Cipher,keyLinkArray,chars
来源: https://www.cnblogs.com/panshan-lurenjia/p/16306494.html