ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2022-05-24 18:03:21  阅读:171  来源: 互联网

标签:Java int Transposition 矩阵 char length Cipher keyLinkArray chars


这是几年前写的旧文,此前发布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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有