(密码学实验)两种基本古典密码设计与实现—C语言—随机全排列应用

目录

一、实验目的

二、实验内容

三、实验过程

1.概要设计

(1)随机全排列生成程序

(2)程序逻辑图

2.详细设计

(1)密钥字法

(2)洗牌法

(3)公式法

(4)自设计随机16全排列

(5)生成密码本(unsigned char *KeyGen)

(6)源代码

3.测试结果

(1)替换密码

(2)移位密码

4.遇到问题

四、实验体会

五、思考题


密码学实验一、二

随机全排列生成程序极其应用开发

两种基本古典密码设计与实现

一、实验目的

该实验为验证性实验。

通过本实验,使学生对于两种基本的古典密码编码方法(“代替”与“移位”)产生深刻的感性认识,体验清楚二者之间的本质差异,为理解和掌握现代密码的相应知识打下良好基础。

二、实验内容

1. 设计一个周期3的多表代替密码并予以实现,要求:第1个表由密钥字法产生(密钥字自拟),第2个表由洗牌法产生(注意,字母a~z与数字0~25对应,洗牌法即相当于实验一的方法1(n=25)),第三个表由公式法产生(数学公式自拟,注意它须是Z26上的一个一一变换)。

2.设计一个周期5的16-置换移位密码并予以实现,要求:5个16-置换至少有一个是由实验一(n=15)提供的两个方法以外、自行设计的其它方法产生。

三、实验过程

1.概要设计

说明本程序中用到的所有数据结构的定义、主程序的流程以及程序模块之间的层次(调用)关系。可用流程图等图形化工具描述,并辅以对各模块功能的必要说明。

(1)随机全排列生成程序

方法1:读取一个随机文件中n+1字节数据存入d,对0~n的自然排列,从i=n开始,计算j=(d[i-1]+d[i])%i ,并交换P[i]与P[j]的值。

程序设计: 

unsigned char *full_array1(int n)
{
    int i,j;
    char filename[20];
    FILE *fp;
    static unsigned char d[256],P[256],temp;
    start:
        printf("\n 请输入随机数据采样文件名:\n");
        scanf("%s",filename);
        if( (fp=fopen(filename,"rb"))==NULL )
        {
            printf("没有找到文件:%s\n",filename);
            goto start;
        }
    fread(d,n+1,1,fp);
    fclose(fp);
    for(i=0;i<=n;i++)
    {
        P[i] = i ; //形成自然排列
    }
    for(i=n;i>0;i--)
    {
        j=(d[i-1]+d[i])%i ;
        temp = P[i] ;
        P[i] = P[j];
        P[j] = temp; //交换
    }
    return(P);
}

​​​​​​​方法2:随机函数生成一串,依次mod n,并入排列,并将重复的删去,直到生成0~n的随机全排列。

程序设计:

unsigned char *full_array2(int n)
{
    int m, i, j, k, l=0, flag;
    static unsigned char P[256];
    start:
        printf("\n请输入不小于%d的所用随机数个数:\n", n+1);
        scanf("%d",&m);
        if(m<=n)
        {
          printf("\n输入数%d比%d小,须重新输入!\n", m, n+1);
                goto start;
        }
        srand((unsigned)time(NULL));
        for(i=0;i<m;i++)
        {
            P[l++]=(unsigned char)(rand()%(n+1));
            for(j=0;j<l-1;j++)
            {
                if(P[j]==P[l-1])
                {
                    l--;
                    break;
                }
            }
        }
        k=l;
        for(i=0;i<=n;i++)
        {
            flag=0;
            for(j=0;j<k;j++)
            {
                if(P[j]==i)
                {
                    flag=l;
                    break;
                }
            }
            if(!flag)
            {
                P[l]=i;
                l++;
            }
        }
        return (P);
}

(2)程序逻辑图

主函数(题目一):

(题目二相似)

KeyGen_s函数(生成密码本):

2.详细设计

(1)密钥字法

原理:首先选择一个便于记忆的字母串作为密钥字,然后删去密钥字中重复字母,接下来用余下字母顺序添加至密钥之后,形成全新26字母排序。

举例:以密钥为hello为例,下图为该密钥生成密码本。

a

b

c

d

e

f

g

h

i

j

k

l

m

n

o

p

q

r

s

t

u

v

w

x

y

z

h

e

l

o

a

b

c

d

f

g

i

j

k

m

n

p

q

r

s

t

u

v

w

x

y

z

以密钥为abcabc为例,下图为该密钥生成密码本。

a

b

c

d

e

f

g

h

i

j

k

l

m

n

o

p

q

r

s

t

u

v

w

x

y

z

a

b

c

d

e

f

g

h

i

j

k

l

m

n

o

p

q

r

s

t

u

v

w

x

y

z

程序设计:

printf("\n请输入密钥字:\n");
gets(KeyWords);
strcat(KeyWords,"abcdefghijklmnopqrstuvwxyz");
k=0;
l=strlen(KeyWords);
ChoiceWords[k]=tolower(KeyWords[0]);
for(i=1;i<l;i++)
{
if(letter_to_digit(KeyWords[i])==-1)
{
continue;
}
ChoiceWords[++k]=tolower(KeyWords[i]);
for(j=0;j<k;j++)
{
if(ChoiceWords[j]==ChoiceWords[k])
{
k--;
break;
}
}
}
for(i=0;i<26;i++)
{
KeyTab[i]=(unsigned char)letter_to_digit(ChoiceWords[i]);
}

(2)洗牌法

原理:就是将写有0~(n-1)的n张纸牌打乱次序后重新排列。打乱次序的方式利用(1)随机全排列生成程序。

程序设计:

p=full_array1(25);                         //生成随机全排列
  for(i=0;i<26;i++)KeyTab[26+i]=p[i];   //密码本27~52(抽牌法)

(3)公式法

原理:利用公式生成密码表。公式为(11*i+3)mod 26

程序设计:

for(i=0;i<26;i++)
KeyTab[52+i]=(unsigned char)((11*i+3)%26);//密码本53~78        

(4)自设计随机16全排列

原理:

unsigned char *full_array16_pickout(){
//自己写的创建随机16全排列,抽牌法  

    int i,j,l;  
    static unsigned char p[256];  
    while(l<16){  
        p[l++]=(unsigned char)(rand()%16);  
        for(j=0;j<l-1;j++){  
            if(p[j]==p[l-1]){  
                l--;  
                break;  
            }  
        }  
    }  
    static unsigned char pout[256];  
    l=15,i=0;  
    while(l>=0){  
        j=rand()%16;  
        pout[i++]=p[j];  
        p[j]=p[l--];  
    }  
    return pout;  
}

(5)生成密码本(unsigned char *KeyGen)

     原理:该函数生成密码本本质上是一个78长的数组。前26位存储密钥字法生成的字母序列;中间26位存储抽牌法生成的字母序列;最后26位储存公式法生成序列。

以下为周期3替代密码表。

a

b

c

d

e

f

g

h

i

j

k

l

m

n

o

p

q

r

s

t

u

v

w

x

y

z

密钥字

抽牌

公式

生成五组随机数,构成5-16置换密码表。

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

(6)源代码

在编写代码过程中,部分函数参考实验指导书,主函数

题目一:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <unistd.h>
unsigned char *KeyGen_s();
unsigned char *full_array1(int n);
int letter_to_digit(char c);
char digit_to_letter(int n);
void Encrypt_s(unsigned char *key);
int main()
{
    unsigned char *key;
    key=KeyGen_s();
 printf("\n以下为生成密码本:\n");
    for(int i=0;i<3;i++){
for(int j=0;j<26;j++){
putchar(digit_to_letter(key[i*26+j]));
printf("  ");
}
printf("\n");
 }
    Encrypt_s(key);
    return 0;
}
int letter_to_digit(char c)  //字母——数字
{
    int i;
   char alphabet[26];
for(int i=0;i<26;i++)alphabet[i]='a'+i;
    for(i=0;i<26;i++)
    {
        if(tolower(c)==alphabet[i])
        {
            return (i);
        }
    }
    return(-1);
}
char digit_to_letter(int n)//数字-——字母
{
    char alphabet[26];
for(int i=0;i<26;i++)alphabet[i]='a'+i;
    if(n<0 || n>25)
    {
        return (0);
    }
    return (alphabet[n]);
}
unsigned char *KeyGen_s()      //用于生成密码本
{
    char KeyWords[106];
    char ChoiceWords[26];
    unsigned char *p;
   static unsigned char KeyTab[26*3];//创建密码本
    int i, j, k, l;
    printf("\n请输入密钥字:\n");
    gets(KeyWords);
 strcat(KeyWords,"abcdefghijklmnopqrstuvwxyz");
    k=0;
    l=strlen(KeyWords);
    ChoiceWords[k]=tolower(KeyWords[0]);
    for(i=1;i<l;i++)
//密钥字法后续删除重复字母

    {
        if(letter_to_digit(KeyWords[i])==-1)
        {
            continue;
        }
        ChoiceWords[++k]=tolower(KeyWords[i]);
        for(j=0;j<k;j++)
        {
            if(ChoiceWords[j]==ChoiceWords[k])
            {
                k--;
                break;
            }
        }
    }
    for(i=0;i<26;i++)
    {
        KeyTab[i]=(unsigned char)letter_to_digit(ChoiceWords[i]);
//密码本前26 (密钥字法)

    }
    p=full_array1(25);  //生成随机全排列
    for(i=0;i<26;i++)
    {
    KeyTab[26+i]=p[i];     //密码本27~52(抽牌法)
    }
    for(i=0;i<26;i++)
    {
     KeyTab[52+i]=(unsigned char)((11*i+3)%26);
//密码本53~78    公式为11*i+3(mod 26)

    }
    return(KeyTab);
}
void Encrypt_s(unsigned char *key)    
// 读取文件,输出密文

{
    int i;
    FILE *fp;
    char filename[20], c;
    start:
        printf("\n请输入待加密文本文件名:\n");
        scanf("%s", filename);
        if((fp=fopen(filename,"rt"))==NULL)
        {
            printf("没有找到文件:%s\n",filename);
            goto start;
        }
        printf("\n密文如下:\n");
        i=0;
        while((c=fgetc(fp))!=EOF)
        {
            if(letter_to_digit(c)==-1)
            {
                putchar(c);
                continue;
            }
             isupper(c)?putchar(toupper(digit_to_letter(key[(int)((i%3)*26+letter_to_digit(c))]))):\
putchar(digit_to_letter(key[(int)(i%3)*26+letter_to_digit(c)]));
            i++;
            if(i>=26*3)i=0;
        }
        fclose(fp);
}
unsigned char *full_array1(int n)
{
    int i,j;
    char filename[20];
    FILE *fp;
    static unsigned char d[256],P[256],temp;
    start:
        printf("\n 请输入随机数据采样文件名:\n");
        scanf("%s",filename);
        if( (fp=fopen(filename,"rb"))==NULL )
        {
           printf("没有找到文件:%s\n",filename);
            goto start;
        }
    fread(d,n+1,1,fp);
    fclose(fp);
    for(i=0;i<=n;i++)
    {
        P[i] = i ; //形成自然排列
    }
    for(i=n;i>0;i--)
    {
        j=(d[i-1]+d[i])%i ;
        temp = P[i] ;
        P[i] = P[j];
        P[j] = temp; //交换
    }
    return(P);
}

题目二:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
unsigned char *full_array2(int n);
unsigned char *KeyGen_p();
void Encrypt_p(unsigned char *key);
int main()
{
    unsigned char *key;
    key=KeyGen_p();
for(int i=0;i<5;i++){
for(int j=0;j<16;j++){
printf("  %d",key[i*16+j]))
}
printf("\n");
 }
    Encrypt_p(key);
    return 0;
}
unsigned char *KeyGen_p()
{
    unsigned char *p;
    static unsigned char KeyTab[16*5];
    int i, j;
    for(i=0;i<5;i++)
    {
        p=full_array2(15);
        for(j=0;j<16;j++)
        {
            KeyTab[16*i+j]=p[j];
        }
    }
    return(KeyTab);
}
void Encrypt_p(unsigned char *key)
{
    int i, j;
    FILE *fp;
    char filename[26], c;
    char d[16];
    start:
        printf("\n请输入待加密文本文件名:\n");
        scanf("%s", filename);
        if((fp=fopen(filename,"rt"))==NULL)
        {
            printf("没有找到文件:%s\n",filename);
            goto start;
        }
        printf("\n密文如下:\n");
        i=0;
        while((c=fgetc(fp))!=EOF)
        {
            d[i%16]=c;
            i++;
            if(i%16!=0)
            {
                continue;
            }
            for(j=0;j<16;j++)
            {
                putchar(d[key[(int)(((i-1)/16)*16+j)]]);
            }
            if(i>=16*5)
            {
                i=0;
            }
        }
        if(i%16!=0)
        {
            for(j=i%16;j<16;j++)
            {
                d[j]='*';
            }
            for(j=0;j<16;j++)
            {
                putchar(d[key[(int)((i/16)*16+j)]]);
            }
        }
        putchar('\n');
}
unsigned char *full_array2(int n)
{
    int m, i, j, k, l=0, flag;
    static unsigned char P[256];
    start:
 printf("\n请输入不小于%d的所用随机数个数:\n", n+1);
        scanf("%d",&m);
        if(m<=n)
        {
            printf("\n输入数%d比%d小,须重新输入!\n", m, n+1);
                goto start;
        }
        srand((unsigned)time(NULL));
        for(i=0;i<m;i++)
        {
            P[l++]=(unsigned char)(rand()%(n+1));
            for(j=0;j<l-1;j++)
            {
                if(P[j]==P[l-1])
                {
                    l--;
                    break;
                }
            }
        }
        k=l;
        for(i=0;i<=n;i++)
        {
            flag=0;
            for(j=0;j<k;j++)
            {
                if(P[j]==i)
                {
                    flag=l;
                    break;
                }
            }
            if(!flag)
            {
                P[l]=i;
                l++;
            }
        }
        return (P);
}

3.测试结果

密文:

(1)替换密码

运行结果:

检验:密码本如下

a

b

c

d

e

f

g

h

i

j

k

l

m

n

o

p

q

r

s

t

u

v

w

x

y

z

j

y

k

a

b

c

d

e

f

g

h

i

l

m

n

o

p

q

r

s

t

u

v

w

x

z

c

w

n

v

g

t

x

a

e

f

s

y

b

o

p

j

d

l

i

u

k

z

h

q

m

r

d

o

z

k

v

g

r

c

n

y

j

u

f

q

b

m

x

i

t

e

p

a

l

w

h

s

检验前六位:

明文

Q

i

s

a

s

y

密文

P

e

t

j

i

h

检验结果正确。

(2)移位密码

运行结果:

检验:密码本如下

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

0

6

7

8

3

10

1

11

4

9

13

12

14

2

5

15

3

2

0

8

14

9

11

5

10

1

6

15

7

12

4

13

6

14

8

7

2

12

0

1

5

13

3

4

15

9

10

11

0

4

5

8

11

14

15

6

9

3

1

10

2

12

7

13

7

12

1

4

13

9

0

14

3

8

6

10

2

5

11

15

检验前16位:

Q

i

s

a

  

s

y

m

m

e

t

r

i

c

Q

  

s

y

s

m

   

e

m

r

t

i

i

a

c

检验结果正确。

4.遇到问题

在实验过程中部分代码参考实验指导书,因此在实验过程中,发现实验指导书上存在的一些问题

(1)程序报错

程序在运行此行代码时报错,原因与长度有关。

解决方法:改进代码,如下。

char alphabet[i];

for(int i=0;i<26;i++)alphabet[i]='a'+i;

(2)实验指导书关于题目一代码错误

按照次代码,会将明文前26个用密钥字法加密;第二个26用抽牌法加密……。与要求周期3不符。

解决方法:修改代码,将i除26修改为i模3;

正在上传…重新上传取消

四、实验体会

本次是密码学的第一次实验,主要是两种基本古典密码设计与实现,在操作之中逐渐熟悉了他们的使用方法并了解了两种密码的差异。

    本次实验的函数对我相对困难,因此部分函数借鉴了实验指导书,在对实验指导书的学习过程中,我也发现了实验指导书上面存在一些问题,并通过自己的理解修改了函数,并得到了老师的肯定。

能发现问题,源自于我对题目的深刻理解,这次实验之前的预习必不可少,投入的时间终究会有回报。

五、思考题

思考题:“代替表”与“置换”的不动点、逆等是否一致?

不一致。

代替密码的实质是从明文空间通过一定的映射关系到密文空间,已经不再是原来的集合了。移位密码的实质是在明文空间内部,通过移动明文位置,使明文乱序,从而达到加密的目的。两者的不动点和逆只有在统一集合变换时才可能一样。

  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拉进人山人海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值