一.描述
字符集为大小写字母与数字,-*,一共64个字符,-*加密后不变,各字符集加密完后应该还是属于该字符集。
二.思路
首先将字符编码,最大的字符集个数为26,5个比特即可表示,用8个比特表示一个字符,前3个比特为控制位,表示其为哪一种类的字符,后五个比特表示该字符在字符集内的具体数值。用CTC模式进行加密,每次从加密后的counter值中取五位出来与每个字符编码的后5个比特异或进行加密,加密完输出时,根据控制位进行相应的处理使其格式保持。解密就是再加密一次,详情见代码注释。aes的源代码请自行下载。
三.程序源代码
//
// main.cpp
// csv_ecyt
//
// Created by 王俊 on 2017/10/29.
// Copyright © 2017年 王俊. All rights reserved.
//
#include <stdio.h>
#include<string>
#include<iostream>
#include<map>
#include <stdlib.h>
#include <time.h>
using namespace std;
#include"aes.h"
string cm;
u8 ct[16], sct[16], cipherKey[16] = { 0x2c ,0x7e, 0x16, 0x17, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xe7, 0x15, 0x87, 0x09, 0xcf, 0x4f, 0x21 };
u32 int_cm[4]={0,0,0,0},rk[44], bigint[4] = { 0,0,0,0}, sm[4]={0,0,0,0,},cms[4],temp_sm[4];
map<int, int> c_char_int, int_c_char,l_char_int,int_l_char,char_int,int_char;//用于保存字符集与字符之间双向映射的两个map
int find_kind(char a)
//返回值 1是大写字母,2是小写字母,3是数字,4是 - ,5是*,-1出错
{
if (a >= 'A'&&a <= 'Z')
return 1;
if (a >= 'a'&&a <= 'z')
return 2;
if (a >= '0'&&a <= '9')
return 3;
if (a == '-')
return 4;
if (a == '*')
return 5;
return -1;
}
void change_to_bigint()
//字符串转化为可加密的0,1编码
// 其思想是用8个bit保存一个字符,前三个比特用于保存种类号,后5个比特用于保存字符集序号,
//组序号与字符在字符串出现的序号相同,组值等于该字符的字符集与int的映射值
{
unsigned int temp1,kind,sft_cnt=0,count=0;
for (int i = 0;i < cm.length();i++)
{
count = i * 8 / 32;
sft_cnt = i * 8 - (i * 8 / 32) * 32;
kind = find_kind(cm[i]); //获取所属种类
if(kind==-1)
{
cout<<"error:不在字符集内!"<<endl;
exit(1);
}
if (kind == 1)
temp1 = c_char_int[(int)cm[i]];
else if (kind == 2)
temp1 = l_char_int[(int)cm[i]];
else if (kind == 3)
temp1 = char_int[(int)cm[i]];
else
temp1 = 0;
kind <<= 5; //种类号左移至最高三位
kind ^= temp1; //生成带控制信息的编码
kind <<= sft_cnt; //移动到相应的位置
int_cm[count] ^= kind;
}
}
void init_table() //初始化映射表
{
int start =65;
for (int i = 0;i < 26;i++,start++)
{
c_char_int.insert(make_pair(start, i));
int_c_char.insert(make_pair(i, start));
}
start = 97;
for (int i = 0;i < 26;i++,start++)
{
l_char_int.insert(make_pair(start, i));
int_l_char.insert(make_pair(i, start));
}
start = 48;
for (int i = 0;i < 10;i++,start++)
{
char_int.insert(make_pair(start, i));
int_char.insert(make_pair(i, start));
}
}
void init_counter() //初始化counter
{
for (int i = 0;i < 16;i++)
ct[i] = rand()%256;
}
void Encrypt(u8 a[],u32 b[]) //加解密函数,第一个参数为aes加密后的counter,第二个用于保存加密后的密文或者解密后的明文
//每次从counter中取5位出来与明文相异或,控制位保持不变
{
unsigned int temp=0,temp1=0, temp2=0;
unsigned int xr[4]={0,0,0,0};
//xor用于保存将u8 counter换成4个int值,便于后面的异或作
unsigned int sft_cnt_cm,sft_cnt_sct, count_cm = 0,count_sct=0;
for (int i = 0;i < 16;i++) //将u8 counter换成4个int值,便于后面的异或操作
{
temp=(int)a[i];
xr[i / 4] = xr[i / 4] ^ (temp << ((i % 4) * 8));
}
for (int i = 0;i < 16;i++)
{
count_cm = i * 8 / 32;
count_sct = i * 5/ 32;
sft_cnt_cm = i * 8 - (i * 8 / 32) * 32; //用于定位int所保存的4个值哪一个值
sft_cnt_sct=i*5- (i *5 / 32) * 32; //用于定位本次所需取出的5位组的位置
if (i*5== 30) //跨int操作特殊处理
{
temp1 = 0x03;
temp1 &= (xr[0] >>30);
temp2 = 0x07;
temp2 &= xr[1];
temp2<<=2;
temp2 ^= temp1;
temp2 <<= sft_cnt_cm;
b[count_cm]^=temp2;
continue;
}
if (i*5 == 60)
{
temp1 = 0x0f;
temp1 &= (xr[1] >> 28);
temp2 = 0x01;
temp2 &= xr[2];
temp2<<=4;
temp2 ^= temp1;
temp2 <<= sft_cnt_cm;
b[count_cm]^=temp2;
temp1 = 0x1f;
continue;
}
temp1 = 0x1f; //每次取5位
temp1 <<= sft_cnt_sct; //移动到相应的位置
temp2 = temp1&xr[count_sct]; //与xor相与获取6位组的密文
temp2>>=sft_cnt_sct;
temp2 <<= sft_cnt_cm;//移动到相应的位置
b[count_cm]^= temp2; //更新数据
}
}
void Show(u32 a[]) //将加解密后的编码以字符串的方式显示出来
//每次取出8位出来,根据控制信息与数值输出字符
{
unsigned int temp1,temp2,temp3;
int sft_cnt=0,count=0;
for(int i=0;;i++)
{
count=i*8/32;
sft_cnt=i*8-((i*8)/32)*32;
temp1=0xff;
temp2=0x1f;
temp3=0xe0;
temp1<<=sft_cnt;
temp2<<=sft_cnt;
temp3<<=sft_cnt;
temp1&=a[count];
temp2&=temp1;
temp3&=temp1;
temp2>>=sft_cnt;
temp3>>=(5+sft_cnt);
if(temp3==1)
cout<<(char)int_c_char[temp2%26];
else if(temp3==2)
cout<<(char)int_l_char[temp2%26];
else if(temp3==3)
cout<<temp2%10;
else if(temp3==4)
cout<<"-";
else if(temp3==5)
cout<<"*";
else
break;
}
cout<<endl;
}
void Decrypt(u8 a[],u32 b[])
{
Encrypt(a,b); //解密就是再加密一次
}
int main()
{
srand((unsigned int)time(NULL));
int Nr; //用于保存轮加密的轮数
cin >> cm; //输入的明文
init_table(); //初始化字符集的映射表
change_to_bigint(); //将输入的明文字符串映射为0,1编码
init_counter(); //用counter模式来实现aes加密,初始化随机数
Nr = rijndaelKeySetupEnc(rk, cipherKey, 128); //密钥扩展
rijndaelEncrypt(rk, Nr, ct, sct); //加密counter
for(int i=0;i<4;i++)
sm[i]=int_cm[i];
Encrypt(sct,sm); //与明文异或生成密文并显示(保持格式加密)
Show(sm);
Decrypt(sct,sm); //解密
Show(sm);
getchar();
getchar();
return 0;
}