走进密码学 - 古典密码学(二) 2020-10-24 21:06 Java安全密码学之古典密码学 > 原文:原数据,加密前的数据,例如手机号,姓名,密码,身份证号等。 > > 密文:加密后的数据,一串常人看不懂的字符串,例如ASDFHSDHREISG等。 > > 加密:为了保护原文,不让别人随意知道原文的意思,通过一定规律的变换,将原文变为密文。 ### 古典密码学 #### 1、移位法 ##### 1.1、单表移位法 首先我们需要一个26个单词的字母表  **单表移位法**就是将原文所有字母都在字母表上向前(或后)按照一个固定数目偏移后得到密文。例如abc将原文每个字母均向后移动3个单位加密后可以得到密文def。此方法又称为**凯撒加密**,因为据传该加密方法是罗马共和时期,凯撒与将军通信的方式。 ##### 1.2、多表移位法 将原文按照固定的数目偏移得到密文,这种方式未免太过简单,很容易被人识破。所以我们可以将移动方式变得更复杂一些,例如,将原文中每个单词的偏移量设置为不同的。比如当原文为abc,移动规律设置为312,则表示第一个字母移动3位,第二个字母移动1位,第三个字母移动2位,加密后密文为dce。 这样移动使用一个表来加密解密未免太过困难,不方便。所以我们可以设计一种方式,**将原先的单表当作原文所在的表**,然后将26个英文单词的每个单词都分别向下扩展,分别移动1位、2位、3位...。例如A向后移动1位得到B,向后移动两位得到C...一直到移动25位,这样我们就得到了这样的表:  现在我们如果有原文A,那么无论它需要移动几个单位,我们只需要将它向下移动哪个单位即可得到密文了。 同样的原文中的B也可以向下移动扩展,移动1个单位是C,移动两个单位是D...一直到移动第24个单位,得到Z,移动第25个单位,又回到了A,所以我们可以得到这样的表:  现在,当原文中出现B时,我们也可以使用和A同样的方法来向下移动得到密文啦。比如原文中的B需要移动5个单位,我们只需要选择横坐标的B,然后将它向下移动5个单位,即可得到密文为G了。 类似的,其他原文字母也分别向下扩展,最终我们得到这样一张表:  由于所有的纵坐标字母都是有横坐标第一排的原文移动某个单位得到的,所以横坐标可以看作是原文,所有纵坐标可以看作密文。 现在,来看下我们的加解密方式变得多么轻松。如果有原文BOY,移动方式为512,那么我们选择横坐标的B向下移动5个单位,得到密文G,横坐标的O向下移动1个单位得到密文P,横坐标的Y向下移动2个单位,得到密文A。所以原文BOY通过512的移动方式得到了密文GPA。同理,当拿到密文GPA和移动方式512时,我们也可以使用此表来解密:找到纵坐标的G,然后向上移动5个单位,得到了原文B,找到纵坐标的P,向上移动1个单位,得到原文O,找到纵坐标的A向上移动两个单位,得到原文Y。所以得出解密后的原文为BOY。 移动方式控制了原文会变成怎样的密文,所以我们可以将移动方式(或移动规律)称为密钥。既然现在纵坐标有了字母,那么我们就不单单可以使用数字来表示密钥了,还可以使用字母来表示。 比如原文A,密钥为S,那么加密过程即为横坐标的A,在第S行的字母为S。原文为D,密钥为M,机密过程即为横坐标的D,在第M行的字母,为V。解密方式类似。 像这种使用多张表组合起来进行加解密的方式,称为多表移位法。多表移位时,每个原文的单词移动的规律不同(相同的话就是单表移位了),这就让加密解密更加复杂,也让密文更安全。不难发现,单表移位法是一个表横向移动,多表后,则是根据密钥纵向移动。 将这个规律运用起来,把原文和密钥都变长一点试试,若原文是BCDGZ,密钥是TRYUO,那么密文呢?第一行的B移动到第T行是U,第一行的C移动到第R行是T,同理:D --> B, G-->A, Z-->N,所以经过此密钥加密后为UTBAN。此方法历史上被称为**维吉尼亚密码(或维热纳尔密码)**。(维吉尼亚密码的密钥选择通常为选择某一关键词然后重复关键词,直到长度达到原文的长度,则将此字符串作为密钥。这里的关键词可以是任意单词) 对比上面几种加密方式,不难看出,单表移位其实就是将一串字符整体向前(或后)平移几个单位;这显然不够安全,很容易发现规律,所以延伸出每个单词移动不同的位数,出现了多表移位;多表移位时,指定每个位置上的原文需要移动的步数,让原文每次尽量移动某个杂乱的步数,这让密文更加无序;后面延申出的字母作为密钥的方式,则是将原文移动到密钥代表的行,这样用单词代替数字,能避免密钥太过直白,泄露后的危机,同时也让加解密有了门槛。 --- > java工具类: 凯撒加密,维吉尼亚密码 ```java package EncrptDemo; /** * @author: HanXu * on 2020/10/23 * Class description: 古典密码学 * 仅支持26个大写字母加解密 */ public class GuDianDemo { public static void main(String[] args) { //凯撒加密 String data = "GFUDRTSDXMLPOKZQA"; int secreKey = 6; String cipher = CaesarCipher(data, secreKey); System.out.println(cipher); String decrypts = CaesarDecrypts(cipher, secreKey); System.out.println(decrypts); if (data.equals(decrypts)) { System.out.println("SUCCESS"); } else { System.err.println("FAILE"); } //维尼吉亚密码加密 /*String data = "BCDGZBCDGZBCD"; String secretKey = "TRYUO";*/ /*String data = "IAMHANXUIWANTTOGOTOHOME"; String secretKey = "CTJ";*/ /*String data = "SDTRDHGFJHGDFHFGHGHJKGHJDFG"; String secretKey = "DSRTASDFGHDGH"; String encry = VigenereEncry(data, secretKey); System.out.println(encry); String decrypt = VigenereDecrypt(encry, secretKey); System.out.println(decrypt); if (data.equals(decrypt)) { System.out.println("SUCCESS"); } else { System.err.println("FAILE"); }*/ } /** * 凯撒密码加密 * @param data 原文 * @param secretKey 密钥 * @return */ public static String CaesarCipher(String data, int secretKey) { secretKey = secretKey % 26; char[] dataChars = data.toCharArray(); for (int i = 0; i < dataChars.length; i++) { int asciiCode = (int) dataChars[i] + secretKey; dataChars[i] = (char) (asciiCode <= 90 ? asciiCode : (64 + asciiCode % 90)); } return new String(dataChars); } /** * 凯撒密码解密 * @param cipherText 密文 * @param secretKey 密钥 * @return */ public static String CaesarDecrypts(String cipherText, int secretKey) { secretKey = secretKey % 26; char[] cipherChars = cipherText.toCharArray(); for (int i = 0; i < cipherChars.length; i++) { int asciiCode = (int) cipherChars[i] - secretKey; cipherChars[i] = (char) (asciiCode >= 65 ? asciiCode : (90 - (64 - asciiCode))); } return new String(cipherChars); } /** * 维尼吉亚密码加密 * @param data 原文 * @param secretKey 密钥 * @return */ public static String VigenereEncry(String data, String secretKey) { int dataLength = data.length(); int secretKeyLength = secretKey.length(); // 保证密钥长度与原文长度一致 if (dataLength > secretKeyLength) { char[] secretKeyChars = secretKey.toCharArray(); int j = 0; for (int i = secretKeyLength; i < dataLength; i++) { secretKey += secretKeyChars[j]; j = (j + 1) % secretKeyLength; } } StringBuilder cipherText = new StringBuilder(); char[] dataChars = data.toCharArray(); char[] secretKeyChars = secretKey.toCharArray(); for (int i = 0; i < dataLength; i++) { // 原文 + 长度 后 若 > 90 则 从 A开始 加上 ( > 91的 长度) // 长度 = 密文 - 65 int asciiCode = dataChars[i] + (secretKeyChars[i] - 65); asciiCode = (asciiCode <= 90) ? asciiCode : (65 + (asciiCode - 91)); cipherText.append((char) asciiCode); } return cipherText.toString(); } /** * 维尼吉亚密码解密 * @param cipherText 密文 * @param secretKey 密钥 * @return */ public static String VigenereDecrypt(String cipherText, String secretKey) { int cipherTextLength = cipherText.length(); int secretKeyLength = secretKey.length(); // 保证密钥长度与密文长度一致 if (cipherTextLength > secretKeyLength) { char[] secretKeyChars = secretKey.toCharArray(); int j = 0; for (int i = secretKeyLength; i < cipherTextLength; i++) { secretKey += secretKeyChars[j]; j = (j + 1) % secretKeyLength; } } StringBuilder data = new StringBuilder(); char[] cipherTextChars = cipherText.toCharArray(); char[] secretKeyChars = secretKey.toCharArray(); for (int i = 0; i < cipherTextLength; i++) { // 密文 - 长度 后 若 < 65 则 从 Z开始 减去 ( < 64 的 长度) // 长度 为 密钥 - 65 int asciiCode = cipherTextChars[i] - (secretKeyChars[i] - 65); asciiCode = (asciiCode >= 65) ? asciiCode : (90 - (64 - asciiCode)); data.append((char) asciiCode); } return data.toString(); } } ``` #### 2、替换法 ##### 2.1、单表替换 制定映射规则表a b c d e - q w e r t,则原文abc,替换后密文为qwe ##### 2.2、多表替换 a b c d - q w e r,a b c d - o p t y,a b c d - i j k l,规定替换顺序为3 1 2,那么原文a b c,替换后密文为i w t。 --- 本文还没写完,以后会更新的... --END--
发表评论