一、实现广义的凯撒密码加密算法
//实现广义的凯撒密码
//凯撒密码的加密
String plaintext="";
String ciphertext="";
public String enk(){
System.out.println("请输入明文");
Scanner sc = new Scanner(System.in);
plaintext = sc.next();
char[] chars = plaintext.toCharArray();
for(int i=0; i<chars.length; i++){
int j = chars[i];
chars[i] = (char)(j+3);
ciphertext += chars[i];
}
return ciphertext;
}
二、实现广义的playfair密码的加密算法
package Bender;
import java.util.Scanner;
public class Test {
String plaintext="";
String ciphertext="";
/*
实现playfair密码加密
*/
public char[][] playk() {//将给出的密码字符串加入到密码表中
System.out.println("请输入编制密码表的单词");
Scanner sc = new Scanner(System.in);
plaintext = sc.next();
char[] chars1 = plaintext.toCharArray();//转换为字符数组
char[][] chars2 = new char[5][5];//空的密码表
//编制密码表
chars2[0][0] = chars1[0];//直接将第一个字符加入密码表
int num = 1;//记录密码表中的已有元素个数
for(int i=1; i<chars1.length; i++){//对明文字符数组(一维数组)进行遍历
if('i'==chars1[i]||'j'==chars1[i]) {//插入的是i||j
for(int j=0; j<=(num/5); j++) { //对密码表(二维数组)进行遍历
boolean jud = false;
for (int k = 0; k < 5; k++) {//插入密码表的操作只有两种情况
if (chars2[j][k]=='\0') {//遍历到空,说明密码表中的密码遍历完了没有找到相同的,那么就应该执行插入操作,并且跳出对密码表(二维数组)的遍历
chars2[j][k] = 'i';
jud = true;
num++;
break;
} else if ("i".equals(chars2[j][k]) || "j".equals(chars2[j][k])) {//遍历到密码表中存在相同的字符时就不必插入密码表了,应该直接跳出对密码表(二维数组)的遍历
jud = true;
break;
}
}
if (jud) {
break;
}
}
}else {//正常插入
for(int j=0; j<=(num/5); j++){
boolean jud = false;
for(int k=0; k<5; k++){//注意空字符的判断 ch=='\0';
if (chars2[j][k]=='\0') {//遍历到空,说明密码表中的密码遍历完了没有找到相同的,那么就应该执行插入操作,并且跳出对密码表(二维数组)的遍历
chars2[j][k] = chars1[i];
jud = true;
num++;
break;
} else if(chars1[i]==chars2[j][k])
{//遍历到密码表中存在相同的字符时就不必插入密码表了,应该直接跳出对密码表(二维数组)的遍历
jud = true;
break;
}
}
if(jud){
break;
}
}
}
}
return chars2;
}
//将上面没完成的密码表填满,最后返回编制好的密码表
public char[][] playk2(char[][] ch){
char[] chgoal = {'a','b','c','d','e','f','g','h','i','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
for(int i=0; i<chgoal.length; i++){//字母表遍历
for(int j=0; j<5; j++){//密码表遍历
boolean jud = false;
for(int k=0; k<5; k++){
if(chgoal[i]==ch[j][k]){//字母已存在密码表中,,直接跳出对密码表的遍历
jud = true;
break;
}else if(ch[j][k]=='\0'){//如果该字母不在密码表中
ch[j][k] = chgoal[i];
jud = true;
break;
}
}
if(jud){
break;
}
}
}
return ch;
}
public void show(char[][] chars){//展示最终编制的密码表
for(char[] ele: chars){
for(char ch: ele){
System.out.print(ch);
}
System.out.println();
}
}
//整理明文,返回字符数组
public char[] splitstr(String str) {
String strgoal = str.replaceAll(" ","").replaceAll("j","i");//去除字符串中的空格
char[] ch = new char[strgoal.length()*2];//以字符串两倍的长度创建字符数组
for(int i=0; i<strgoal.length(); i++){//将字符串中的单个字母添加到字符数组中
ch[i] = strgoal.charAt(i);
}
int index = strgoal.length();//index标识第一个空字符
for(int i=0; i<ch.length;i=i+2){//整理明文,相同则插入X,如果最后只剩一个字母则加X,注意循环条件i<ch.length而不是i<strgoal.length()
if(ch[i]!='\0'&&ch[i+1]!='\0'){//如果是正常情况:俩个位置都有值
if(ch[i]==ch[i+1]){//判断是否字母相同,如果相等的话(不相等则不用执行操作)
for(int j=index; j>=i+2; j--){//执行插入X的操纵,从后往前覆盖
ch[j] = ch[j-1];
}
index++;
ch[i+1] = 'x';
}
}else if(ch[i]!='\0'&&ch[i+1]=='\0'){//第二种情况:前面一个有值,后一个未赋值,将X赋给后一个,此时也意味着ch中的有效字母已经遍历完了,跳出循环即可
ch[i+1] = 'x';
break;
}else{//第三种情况,前后都为空,不执行操作,直接跳出循环即可
break;
}
}
//此时的字符数组就是整理好的明文
return ch;
}
//最后一步(此时的密码表和整理后的明文已经编制好了,最后一步根据密码表将整理好的明文编译成密文,然后输出)
public String encryp(char[][] cht, char[] chb){
//char[] ch = new char[chb.length];//和chb等长的字符数组,用来装密文
String str = "密文:";
for(int i=0; i<chb.length; i+=2){//对chb进行遍历,一次遍历相邻的两个
if(chb[i]!='\0'){
//用于存放下标
int x1 = 0;
int x2 = 0;
int y1 = 0;
int y2 = 0;
for(int j=0; j<5; j++){//对密码表进行遍历,确定明文字母在密码表中的坐标
for(int k=0; k<5; k++){
if(chb[i]==cht[j][k]){
x1 = j;
y1 = k;
}
if(chb[i+1]==cht[j][k]){
x2 = j;
y2 = k;
}
}
}
System.out.println(cht[x1][y1]+"(" + x1 + "," + y1 + ")" + cht[x2][y2]+"(" + x2 + "," + y2 + ")");
//获取到两个下标后进行判断,一共有三种情况
if(x1==x2){//两个字母在同一行
if(y1>y2){//第一个字母在第二个字母右边
str += ""+cht[x1][y1]+cht[x1][(y1+1)%5];
}else{//第一个字母在第二个字母在左边(不可能相等,在整理明文的时候已经处理了)
str += ""+cht[x1][y2]+cht[x1][(y2+1)%5];
}
}else if(y1==y2) {//两个字母在同一列
if(x1<x2){//第一个字母b在第二个a上面
str += ""+cht[x2][y2]+cht[(x2+1)%5][y2];
}else{
str += ""+cht[(x1+1)%5][y1]+cht[x1][y1];
}
}else{//对角,横着取值
str += ""+cht[x1][y2]+cht[x2][y1];
}
}
}
return str;
}
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.enk());
//playk()将给出的密码字符串加入到密码表中,返回二维字符数组
//playk2(char[][] ch)将上面没完成的密码表填满,最后返回编制好的密码表的二维字符数组
//show(char[][] ch)展示最终编制好的密码表
//spliterstr(String str)整理明文,返回字符数组
//encryp(char[][] cht, char[] chb)加密操作,返回密文字符串
// char[][] ch1 = t.playk2(t.playk());
// char[] ch2 = t.splitstr("i have a balloon");
// System.out.println(ch2);
//
// System.out.println(t.encryp(ch1,ch2));
}
}
总结
凯撒密码数据古典密码学的经典案例,而playfair密码则属于替代密码的一种。
它依据一个5*5的正方形组成的密码表来编写,密码表里排列有25个字母。如果一种语言字母超过25个,可以去掉使用频率最少的一个。如,法语一般去掉w或k,德语则是把i和j合起来当成一个字母看待。英语中z使用最少,可以去掉它。
加密原理:
编制密码表
第一步是编制密码表。在这个5*5的密码表中,共有5行5列字母。第一列(或第一行)是密钥,其余按照字母顺序。密钥是一个单词或词组,若有重复字母,可将后面重复的字母去掉。当然也要把使用频率最少的字母去掉。如:密钥是Live and learn,去掉后则为liveandr。如果密钥过长可占用第二列或行。
同时字母I和J会被当成一个字母。
如密钥crazy dog,可编制成
CDFMT
ROHNU
AGI PV
ZBKQW
YELSX
整理明文
第二步整理明文。将明文每两个字母组成一对。如果成对后有两个相同字母紧挨或最后一个字母是单个的,就插入一个字母X(或者Q)。
如,communist,应成为co,mx,mu,ni,st。
编写密文
最后编写密文。对明文加密规则如下:
1 :若p1 p2在同一行,对应密文c1 c2分别是紧靠p1 p2 右端的字母。其中第一列被看做是最后一列的右方。如,按照前表,ct对应dc
2 :若p1 p2在同一列,对应密文c1 c2分别是紧靠p1 p2 下方的字母。其中第一行被看做是最后一行的下方。
3 :若p1 p2不在同一行,不在同一列,则c1 c2是由p1 p2确定的矩形的其他两角的字母(至于横向替换还是纵向替换要事先约好,或自行尝试)。如,按照前表,wh对应ku或uk。
如,依照上表,明文where there is life,there is hope.
可先整理为:WH ER ET HE RE IS LI FE TH ER EI SH OP EX
然后密文为:KU YO XD OL OY PL FK DL FU YO LG LN NG LY
将密文变成大写,然后几个字母一组排列。
如5个一组就是KUYOX DOLOY PLFKD LFUYO LGLNN GLY