<mark>个人博客:点击进入</mark>
本篇博客做个人查阅使用。

RSA算法

数学基础知识

互质关系

如果两个正整数,除了1以外,没有其他公因子,我们就称这两个数是互质关系

  1. 任意两个质数构成互质关系
  2. 一个数是质数,另一个数只要不是前者的倍数,两者就构成互质关系
  3. 1和任意一个自然数是都是互质关系
  4. p是大于1的整数,则p和p-1构成互质关系
  5. p是大于1的奇数,则p和p-2构成互质关系

欧拉函数

在小于等于n的正整数之中,能与n构成互质关系的数的个数
计算这个值的方法叫做欧拉函数,以φ(n)表示

  1. 如果n=1,则 φ(1) = 1 。因为1与任何数(包括自身)都构成互质关系

  2. 如果n是质数,则 φ(n)=n-1 。因为质数与小于它的每一个数,都构成互质关系

  3. 如果n是质数的某一个次方,即 n = p^k (p为质数,k为大于等于1的整数)

    φ(pk) = pk - pk-1

  4. 如果n可以分解成两个互质的整数之积,即n = p1 × p2

    φ(n) = φ(p1 × p2) = φ(p1) × φ(p2)

欧拉定理

如果两个正整数a和n互质,则n的欧拉函数 φ(n) 可以让下面的等式成立:
aφ(n)≡1(mod n)

费马小定理:
假设正整数a与质数p互质,因为质数p的φ§等于p-1,则欧拉定理可以写成
ap-1≡1(mod p)

模反元素

如果两个正整数a和n互质,那么一定可以找到整数b,使得 ab-1 被n整除,或者说ab被n除的余数是1。
ab≡1(mod n)
这时候b就叫做a的"模反元素"

RSA算法基本概念

  1. RSA加密

    密文 = 明文EmodN

    公钥 = (E,N)

  2. RSA解密

    明文 = 密文DmodN

    私钥 = (D,N)

公钥 (E,N)
私钥 (D,N)
密钥对 (E,D,N)
加密 密文 = 明文EmodN
解密 明文 = 密文DmodN

生成密钥对过程

  1. 随机找两个质数p和q,p和q越大越安全,计算他们的乘积n=p*q

    实际算法中p和q的乘积转化为二进制为1024位或2048位,位数越长,算法越难被破解

  2. 计算n的欧拉函数 L = φ(n)

    φ(n)表示在小于等于n的正整数之中,与n构成互质关系的数的个数。

    互质关系:互质是公约数只有1的两个整数。

    φ(n) = φ(p*q) = φ§*φ(q) = (p-1)*(q-1)

    欧拉函数特殊性质:

    1. 若m,n互质,φ(m*n) = φ(m)*φ(n)
    2. 若n为质数则,φ(n) = n-1
  3. 求E

    E是随机选取的一个数,满足两个条件:1<E<L;E和L的最大公约数为1

  4. 求D

    D必须满足

    1 < D < L
    E*D mod L = 1
    

    也就是满足乘法逆元E*D≡1(mod L)
    D为E关于1模L的乘法逆元

求N N= p*q ;p,q为质数
求L L = (p-1)*(q-1)
求E 1<E<L,E与L互质
求D 1 < D < L,E*D mod L = 1

Windows下yafu安装及使用

安装yafu

下载网址:yafu下载

使用yafu

  1. 进入yafu解压目录,打开cmd命令行
  2. 输入yafu-x64(即yafu64位文件名)
    命令:factor(n)—— n 为需要分解的大数

若n位数过长,将n值保存在文本文档里,最后一定要有换行符

yafu-x64 "factor(@)" -batchfile test.txt

其中test.txt为保存需分解的大质数的文本文档

使用python来进行RSA加密解密

gmpy2库 安装 请参见 【linux笔记】

有了N求p、q可使用网址大质数分解 或者使用yafu

import gmpy2
N = 103461035900816914121390101299049044413950405173712170434161686539878160984549
E = 65537
p = 282164587459512124844245113950593348271
q = 366669102002966856876605669837014229419
L = (p-1)*(q-1)
D = int(gmpy2.invert(E, L)) 
''' gmpy2.invert()返回值类型为<class 'mpz'> invert(x,y) 计算 x 关于1模 y 的乘法逆元 '''
enstr = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
destr = gmpy2.powmod(enstr, D, N)
''' gmpy2.powmod()返回值类型为<class 'mpz'> gmpy2.powmod(x,y,z),计算x^y mod z 的值并返回 '''
flag0 = hex(destr)[2:]     # flag0类型为str,存的是十六进制表示的字符串
flag1 = bytes.fromhex(flag0)  # 将其从十六进制转换为byte类型(此时已解释成字符)
flag = str(flag1, 'utf-8')  # 将byte类型转换为str类型
print(flag)

SM4算法

参考链接:欣仔带你零基础入门SM4加密算法

SM4算法简介

SM4是一个分组密码,明文和密文的长度是128位,秘钥的长度是128位。

将128位分成以32位为单位

SM4分步讲解

S盒处理

先进行S盒处理,s盒为特定数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZTF1NKzb-1581058927553)(re加密算法.assets/1.png)]

由于S盒只能处理两位十六进制数,也就是8位,所以处理32位时候采用4个s盒并列来进行加密

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VvqyNywp-1581058927554)(re加密算法.assets/2.png)]

L变换

将S盒输出的32位(例如下图B)作为L变换的输入,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GBqfX8r7-1581058927554)(re加密算法.assets/4.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LNjokfxb-1581058927555)(re加密算法.assets/3.png)]

也就是L(B)返回B ^ (B<<<2) ^ (B<<<10) ^ (B<<<18) ^ (B<<<24)

轮函数

X0 X1 X2 X3 为128位的明文分成的四个32位

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NXbZEVyL-1581058927555)(re加密算法.assets/6.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nE1U4PMC-1581058927556)(re加密算法.assets/5.png)]

SM4加密算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1AAwEHIT-1581058927557)(re加密算法.assets/7.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OVR8NWfR-1581058927558)(re加密算法.assets/8.png)]

轮秘钥rk生成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zAKi7Z4o-1581058927558)(re加密算法.assets/9.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rX8zwiys-1581058927559)(re加密算法.assets/10.png)]

CTF中SM4算法的特征

  1. S盒固定

    unsigned char TAO[16][16] =
    {
        {0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05},
        {0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99},
        {0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62},
        {0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6},
        {0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8},
        {0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35},
        {0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87},
        {0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e},
        {0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1},
        {0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3},
        {0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f},
        {0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51},
        {0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8},
        {0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0},
        {0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84},
        {0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48}
    };
    
  2. 系统参数 FK 固定

    unsigned long FK[4] = {0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc};
    
  3. 固定参数 CK 固定

    unsigned long CK[32] =
    {
        0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
        0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
        0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
        0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
        0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
        0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
        0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
        0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
    };
    

使用python库对SM4算法进行解密

pysm4库 安装 请参见 【linux笔记】

标准128位加密

>>> from pysm4 import encrypt, decrypt
# 明文
>>> clear_num = 0x0123456789abcdeffedcba9876543210
# 密钥
>>> mk = 0x0123456789abcdeffedcba9876543210
# 加密
>>> cipher_num = encrypt(clear_num, mk)
>>> hex(cipher_num)[2:].replace('L', '')
'681edf34d206965e86b3e94f536e4246'
# 解密
>>> clear_num == decrypt(cipher_num, mk)
True

特殊情况参见pysm4安装文档

此链接仅作保存|没有验证过

RC4算法

RC4算法简单讲述

  1. 生成S盒

开始时,S中元素的值被置为按升序从0到255,即S[0]=0,S[1]=1,…,S[255]=255。同时建立一个临时矢量T(长度与S相同)。如果密钥K的长度为256字节,则将K赋给T(K的长度为可能小于S的长度)。否则,若密钥长度为keylen字节,则将K的值赋给T的前keylen个元素,并循环重复用K的值赋给T剩下的元素,直到T的所有元素都被赋值。

  1. 打乱S盒

然后用T产生S的初始置换.从S[0]到S [255],对每个S[i],根据由T[i]确定的方案,将S[i]置换为S中的另一字节。

秘钥调度算法(KSA)

void rc4_init(unsigned char *s, unsigned char *key, unsigned long Len) //初始化函数
{
    int i =0, j = 0;
    char k[256] = {0};
    unsigned char tmp = 0;
    for (i=0;i<256;i++) {
        s[i] = i;
        k[i] = key[i%Len];
    }
    for (i=0; i<256; i++) {
        j=(j+s[i]+k[i])%256;
        tmp = s[i];
        s[i] = s[j]; //交换s[i]和s[j]
        s[j] = tmp;
    }
 }
  1. 矢量S一旦完成初始化,输入密钥就不再被使用。密钥流的生成是从S[0]到S[255],对每个s[i],根据当前S的值,将S[i]与S中的另一字节置换。当S[255]完成置换后,操作继续重复,最后进行异或运算

伪随机生成算法(PRGA)

void rc4_crypt(unsigned char *s, unsigned char *Data, unsigned long Len) //加解密
{
    int i = 0, j = 0, t = 0;
    unsigned long k = 0;
    unsigned char tmp;
    for(k=0;k<Len;k++) {
        i=(i+1)%256;
        j=(j+s[i])%256;
        tmp = s[i];
        s[i] = s[j]; //交换s[x]和s[y]
        s[j] = tmp;
        t=(s[i]+s[j])%256;
        Data[k] ^= s[t];
     }
} 

RC4算法解密脚本(c语言)

void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len_k) //初始化函数
{
    int i = 0, j = 0;
    char k[256] = { 0 };
    unsigned char tmp = 0;
    for (i = 0; i < 256; i++) {
        s[i] = i;
        k[i] = key[i % Len_k];
    }
    for (i = 0; i < 256; i++) {
        j = (j + s[i] + k[i]) % 256;
        tmp = s[i];
        s[i] = s[j]; //交换s[i]和s[j]
        s[j] = tmp;
    }
}

/*
RC4加解密函数
unsigned char* Data     加解密的数据
unsigned long Len_D     加解密数据的长度
unsigned char* key      密钥
unsigned long Len_k     密钥长度
*/
void rc4_crypt(unsigned char* Data, unsigned long Len_D, unsigned char* key, unsigned long Len_k) //加解密
{
    unsigned char s[256];
    rc4_init(s, key, Len_k);
    int i = 0, j = 0, t = 0;
    unsigned long k = 0;
    unsigned char tmp;
    for (k = 0; k < Len_D; k++) {
        i = (i + 1) % 256;
        j = (j + s[i]) % 256;
        tmp = s[i];
        s[i] = s[j]; //交换s[x]和s[y]
        s[j] = tmp;
        t = (s[i] + s[j]) % 256;
        Data[k] ^= s[t];
    }
}

MD5加密

MD5基本描述

MD5的输入输出如下

  • 输入:任意长的消息,512比特长的分组。
  • 输出:160比特的消息摘要。

有时候获得到的md5是16位的,其实那16位是32位md5的长度,是从32位md5值来的。是将32位md5去掉前八位,去掉后八位得到的。

MD5特征(MD5函数的初始化IV)

一般来说,可以通过函数的初始化来判断是不是MD5函数。一般来说,如果一个函数有如下四个初始化的变量,可以猜测该函数为MD5函数,因为这是MD5函数的初始化IV。

var int h0 := 0x67452301
var int h1 := 0xEFCDAB89
var int h2 := 0x98BADCFE
var int h3 := 0x10325476

查询MD5的网站

  • http://www.cmd5.com/
  • http://www.ttmd5.com/
  • http://pmd5.com/

AES算法

待整理

tea系列

tea系列加解密脚本(c语言)

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#define tea_DELTA 0x9e3779b9
/* tea加密函数 32轮加密,请根据需要更改 uint32_t* origin 为要加密的数据是两个32位无符号整数 uint32_t* key 为加密解密密钥,为4个32位无符号整数,即密钥长度为128位 */
void tea_encode(uint32_t* origin, uint32_t* key) {
    uint32_t v0 = origin[0], v1 = origin[1], sum = 0, i;           /* set up */
    uint32_t delta = 0x9e3779b9;                     /* a key schedule constant */
    uint32_t k0 = key[0], k1 = key[1], k2 = key[2], k3 = key[3];   /* cache key */
    for (i = 0; i < 32; i++) {                       /* basic cycle start */
        sum += delta;
        v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
        v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
    }                                              /* end cycle */
    origin[0] = v0; origin[1] = v1;
}
/* tea解密函数 32轮解密,请根据需要更改 uint32_t* origin 为要加密的数据是两个32位无符号整数 uint32_t* key 为加密解密密钥,为4个32位无符号整数,即密钥长度为128位 */
void tea_decode(uint32_t* origin, uint32_t* key) {
    uint32_t v0 = origin[0], v1 = origin[1], i;  /* set up */
    uint32_t delta = 0x9e3779b9,sum = delta << 5; //32轮运算,所以是2的5次方;16轮运算,所以是2的4次方;8轮运算,所以是2的3次方/* a key schedule constant */
    uint32_t k0 = key[0], k1 = key[1], k2 = key[2], k3 = key[3];   /* cache key */
    for (i = 0; i < 32; i++) {                         /* basic cycle start */
        v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
        v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
        sum -= delta;
}                                              /* end cycle */
    origin[0] = v0; origin[1] = v1;
}



/* xtea加密函数 num_rounds 加密轮数 uint32_t* origin 为要加密的数据是两个32位无符号整数 uint32_t* k 为加密解密密钥,为4个32位无符号整数,即密钥长度为128位 */
void xtea_encode(unsigned int num_rounds, uint32_t origin[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0 = origin[0], v1 = origin[1], sum = 0, delta = 0x9E3779B9;
    for (i = 0; i < num_rounds; i++) {
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
        sum += delta;
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
    }
    origin[0] = v0; origin[1] = v1;
}
/* xtea解密函数 num_rounds 加密轮数 uint32_t* origin 为要加密的数据是两个32位无符号整数 uint32_t* k 为加密解密密钥,为4个32位无符号整数,即密钥长度为128位 */
void xtea_decode(unsigned int num_rounds, uint32_t origin[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0 = origin[0], v1 = origin[1], delta = 0x9E3779B9, sum = delta * num_rounds;
    for (i = 0; i < num_rounds; i++) {
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
        sum -= delta;
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
    origin[0] = v0; origin[1] = v1;
}



// xxtea加密解密
#if 0
int main()//例子
{
    uint32_t v[2] = { 1,2 };
    uint32_t const k[4] = { 2,2,3,4 };
    int n = 2; //n的绝对值表示v的长度,取正表示加密,取负表示解密
    // v为要加密的数据是两个32位无符号整数
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
    printf("加密前原始数据:%u %u\n", v[0], v[1]);
    xxtea(v, n, k);
    printf("加密后的数据:%u %u\n", v[0], v[1]);
    xxtea(v, -n, k);
    printf("解密后的数据:%u %u\n", v[0], v[1]);
    return 0;
}
#endif
#define xxtea_MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
/* origin 为要加密的数据是两个32位无符号整数(若加密字符串先转换为16进制整数) n 的绝对值表示v的长度(即有几个32位整数),取正表示加密,取负表示解密 key 为加密解密密钥,为4个32位无符号整数,即密钥长度为128位 */
void xxtea(uint32_t* origin, int n, uint32_t const key[4])
{
	uint32_t y, z, sum;
	unsigned p, rounds, e;
	if (n > 1)            /* Coding Part */
	{
		rounds = 6 + 52 / n;
		sum = 0;
		z = origin[n - 1];
		do
		{
			sum += tea_DELTA;
			e = (sum >> 2) & 3;
			for (p = 0; p < n - 1; p++)
			{
				y = origin[p + 1];
				z = origin[p] += xxtea_MX;
			}
			y = origin[0];
			z = origin[n - 1] += xxtea_MX;
		} while (--rounds);
	}
	else if (n < -1)      /* Decoding Part */
	{
		n = -n;
		rounds = 6 + 52 / n;
		sum = rounds * tea_DELTA;
		y = origin[0];
		do
		{
			e = (sum >> 2) & 3;
			for (p = n - 1; p > 0; p--)
			{
				z = origin[p - 1];
				y = origin[p] -= xxtea_MX;
			}
			z = origin[n - 1];
			y = origin[0] -= xxtea_MX;
			sum -= tea_DELTA;
		} while (--rounds);
	}
}

base64加解密

更多讲解请参见 【Base16,Base32,Base64编码详细学习】

base64加解密脚本(c语言)

/* base64加密 const unsigned char* src 需要加密的数组 unsigned char* dst 加密之后存储的数组 */
int base64_encode(const unsigned char* src, unsigned char* dst)
{

    size_t len = strlen((const char*)src);
    static unsigned char base64char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    while (len > 2) {
        *dst++ = base64char[src[0] >> 2 & 0x3f];
        *dst++ = base64char[(src[0] & 0x3) << 4 | src[1] >> 4 & 0xf];
        *dst++ = base64char[(src[1] & 0xf) << 2 | src[2] >> 6 & 0x3];
        *dst++ = base64char[src[2] & 0x3f];
        len -= 3;
        src += 3;
    }

    if (len) {
        *dst++ = base64char[src[0] >> 2 & 0x3f];
        if (len > 1) {
            *dst++ = base64char[((src[0] & 0x3) << 4) | ((src[1] >> 4) & 0xf)];
            *dst++ = base64char[(src[1] & 0xf) << 2];
        }
        else {
            *dst++ = base64char[(src[0] & 0x3) << 4];
            *dst++ = '=';
        }
        *dst++ = '=';
    }

    *dst = 0;
    return 0;
}

/* base64解密 const unsigned char* src 需要解密的字符 unsigned char* dst 解密之后存储的数组 */
int base64_decode(const unsigned char* src, unsigned char* dst)
{
    static char base64char[] = {
            -1 , -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1 , -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1 , -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
            52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
            -1 ,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
            -1 , 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
    };

    int i;
    size_t len = strlen((const char*)src);

    for (i = 0; i < len; i++) {
        if (src[i] == -1) {
            return 1; //unexpected characters
        }
        else if (src[i] == '=') {
            len = i;
        }
    }

    if (len % 4 == 1) {
        return  2;
    }

    while (len > 3) {
        *dst++ = (unsigned char)(base64char[src[0]] << 2) | (base64char[src[1]] >> 4 & 0x3);
        *dst++ = (unsigned char)(base64char[src[1]] << 4) | (base64char[src[2]] >> 2 & 0xf);
        *dst++ = (unsigned char)(base64char[src[2]] << 6) | (base64char[src[3]]);

        src += 4;
        len -= 4;
    }

    if (len) {
        if (len > 1) {
            *dst++ = (base64char[src[0]] << 2) | (base64char[src[1]] >> 4 & 0x3);
        }

        if (len > 2) {
            *dst++ = (base64char[src[1]] << 4) | (base64char[src[2]] >> 2 & 0xf);
        }

    }
    *dst = 0;
    return 0;
}

/* base64自定义字符表加密 char* src 存放待加密字符串的数组 char* dst 存放加密后字符串的数组 char* mytable 自定义字符表 */
void base64_myen(char* src, char*dst, char* mytable)
{
    base64_encode((const unsigned char*)src,(unsigned char*)dst);
    int i, j;
    char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    int len_s = strlen(dst);
    for (i = 0; i < len_s; i++)
    {
        for (j = 0; j < 65; j++)
        {
            if (dst[i] == base64_table[j])
            {
                dst[i] = mytable[j];
                break;
            }
        }
    }
}

/* base64自定义字符表解密 char* src 存放待解密字符串的数组 char* dst 存放解密后字符串的数组 char* mytable 自定义字符表 */
void base64_myde(char* src, char* dst, char* mytable)
{
    int i, j;
    char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    int len_s = strlen(src);
    for (i = 0; i < len_s; i++)
    {
        for (j = 0; j < 65; j++)
        {
            if (src[i] == mytable[j])
            {
                src[i] = base64_table[j];
                break;
            }
        }
    }
    base64_decode((const unsigned char*)src, (unsigned char*)dst);
}