• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
mankun
博客园    首页    新随笔    联系   管理    订阅  订阅

DES算法原理

DES算法原理

DES加密

DES算法采用的是64位分组长度和56位的密钥长度,其算法原理是基于Feistel进行的改进。

加密流程

  1. 明文进行初始IP置换。
  2. 明文分成左右各32位子明文。
  3. 左右子明文经过16轮加密(F函数、16轮子密钥)。
  4. 左右子密文交换位置合成64位加密后的密文。
  5. 密文经过IP逆置换得到最后的密文。

16轮子密钥

F函数

加密演示示例

  • 明文 密钥
明文:01100011 01101111 01101101 01110000 01110101 01110100 01100101 01110010
密钥:00010011 00110100 01010111 01111001 10011011 10111100 11011111 11110001
  • 明文IP置换
IP置换后的明文:11111111 10111000 01110110 01010111 00000000 11111111 00000110 10000011
  • 密钥处理
PC1置换后的密钥:11110000 11001100 10101010 11110101 01010110 01100111 10001111
密钥对半拆分为初始C0 D0
C0:1111000 0110011 0010101 0101111
D0:0101010 1011001 1001111 0001111

C0 D0 循环左移生成 C1 D1
C1:1110000 1100110 0101010 1011111
D1:1010101 0110011 0011110 0011110
C1 D1 经过PC2置换生成K1
K1:00011011 00000010 11101111 11111100 01110000 01110010

同上处理,C1 D1再次左移生成C2 D2,然后经过PC2置换生成K2
C2:1100001 1001100 1010101 0111111
D2:0101010 1100110 0111100 0111101
K2:01111001 10101110 11011001 11011011 11001001 11100101

C3:
D3:
K3:

C4:
D4:
K4:

C5:
D5:
K5:

C6:
D6:
K6:

C7:
D7:
K7:

C8:
D8:
K8:

C9:
D9:
K9:

C10:
D10:
K10:

C11:
D11:
K11:

C12:
D12:
K12:

C13:
D13:
K13:

C14:
D14:
K14:

C15:
D15:
K15:

C16:1111000 0110011 0010101 0101111
D16:0101010 1011001 1001111 0001111
K16:11001011 00111101 10001011 00001110 00010111 11110101
  • F函数处理
IP置换后的明文拆分成初始的L0 R0
L0:11111111 10111000 01110110 01010111
R0:00000000 11111111 00000110 10000011

R0经过E盒扩展得到E1
E1:10000000 00010111 11111110 10000000 11010100 00000110

E1和K1经过异或得到EK1
EK1:10011011 00010101 00010001 01111100 10100100 01110100

EK1经过S盒压缩得到S1
S1:10001011 11000100 01100010 11101010

S1经过P盒置换得到P1
P1:01001000 10111111 01010101 10000001

P1和L0经过异或得到R1
R1:
78/*

DES解密

解密流程和加密流程基本一致,仅在16轮加解密过程中使用密钥的顺序有区别:

  • 加密:使用K1 ---> K16子密钥。
  • 解密:使用K16 ---> K1子密钥。

代码实现-C语言

#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/*
 * 使用指南:
 * 1. 编译命令示例:
 *  gcc des_code.c -o des_code -w -lm
 * 2. 运行程序:
 *  ./des_code
 */

// 初始IP置换表
static const int ip_table[64] = {
    58, 50, 42, 34, 26, 18, 10, 2,
    60, 52, 44, 36, 28, 20, 12, 4,
    62, 54, 46, 38, 30, 22, 14, 6,
    64, 56, 48, 40, 32, 24, 16, 8,
    57, 49, 41, 33, 25, 17, 9, 1,
    59, 51, 43, 35, 27, 19, 11, 3,
    61, 53, 45, 37, 29, 21, 13, 5,
    63, 55, 47, 39, 31, 23, 15, 7 };

// IP逆置换表
static const int ipre_table[64] = {
    40, 8, 48, 16, 56, 24, 64, 32,
    39, 7, 47, 15, 55, 23, 63, 31,
    38, 6, 46, 14, 54, 22, 62, 30,
    37, 5, 45, 13, 53, 21, 61, 29,
    36, 4, 44, 12, 52, 20, 60, 28,
    35, 3, 43, 11, 51, 19, 59, 27,
    34, 2, 42, 10, 50, 18, 58, 26,
    33, 1, 41, 9, 49, 17, 57, 25 };

// E扩展运算表
static const int e_table[48] = {
    32, 1, 2, 3, 4, 5,
    4, 5, 6, 7, 8, 9,
    8, 9, 10, 11, 12, 13,
    12, 13, 14, 15, 16, 17,
    16, 17, 18, 19, 20, 21,
    20, 21, 22, 23, 24, 25,
    24, 25, 26, 27, 28, 29,
    28, 29, 30, 31, 32, 1 };

// P盒置换表
static const int p_table[32] = {
    16, 7, 20, 21, 29, 12, 28, 17,
    1, 15, 23, 26, 5, 18, 31, 10,
    2, 8, 24, 14, 32, 27, 3, 9,
    19, 13, 30, 6, 22, 11, 4, 25 };

// S盒置换表
static const int s_table[8][4][16] = {
    14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
    0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
    4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
    15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13,

    15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
    3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
    0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
    13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9,

    10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
    13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
    13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
    1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12,

    7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
    13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
    10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
    3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14,

    2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
    14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
    4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
    11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3,

    12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
    10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
    9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
    4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13,

    4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
    13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
    1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
    6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12,

    13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
    1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
    7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
    2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 };

// PC1置换表
static const int pc1_table[56] = {
    57, 49, 41, 33, 25, 17, 9,
    1, 58, 50, 42, 34, 26, 18,
    10, 2, 59, 51, 43, 35, 27,
    19, 11, 3, 60, 52, 44, 36,
    63, 55, 47, 39, 31, 23, 15,
    7, 62, 54, 46, 38, 30, 22,
    14, 6, 61, 53, 45, 37, 29,
    21, 13, 5, 28, 20, 12, 4 };

// PC2置换表
static const int pc2_table[48] = {
    14, 17, 11, 24, 1, 5,
    3, 28, 15, 6, 21, 10,
    23, 19, 12, 4, 26, 8,
    16, 7, 27, 20, 13, 2,
    41, 52, 31, 37, 47, 55,
    30, 40, 51, 45, 33, 48,
    44, 49, 39, 56, 34, 53,
    46, 42, 50, 36, 29, 32 };

// 左移位数表
static const int ls_table[16] = {
    1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };

// 单字节字符转十六进制
/*
 * src:入参 单字节字符串
 * hex:出参 十六进制字符串
 */
void single_byte_character_to_hex(char *src, char* hex)
{
    int offset = 0;
    for (int i = 0; i < strlen(src); i++) {
        sprintf(hex + offset, "%x", src[i]);
        offset += 2;
    }
}

// 2进制转10进制
int bin2dec(char *bin)
{
    int res = 0;
    int len = strlen(bin);
    char temp[2] = {'0', '1'}; 
    for (size_t i = 0; i < len; i++) {
        if (bin[len - i - 1] == temp[1]) {
            res += pow(2, i);   // 计算2的i次方并累加
        }
    }
    return res;
}

// 十进制转二进制
int dec2bin(int n)
{
    int sum = 0;
    int y, x = 1; // y表示余数,x为叠加的系数
    while (n != 0) {
        y = n % 2;
        sum += x * y;
        x *= 10;
        n /= 2;
    }
    return sum;
}

// 十六进制转二进制
void hex2bin(char *hex, char *bin)
{
    int len = strlen(hex);
    char buff[65] = { 0 };
    char binbuff[65] = { 0 };
    for (int i = 0; i < len; i++) {
        switch (hex[i]) {
            case '0':
                strcpy(buff, "0000");
                strncat(binbuff, buff, 4);
                break;
            case '1':
                strcpy(buff, "0001");
                strncat(binbuff, buff, 4);
                break;
            case '2':
                strcpy(buff, "0010");
                strncat(binbuff, buff, 4);
                break;
            case '3':
                strcpy(buff, "0011");
                strncat(binbuff, buff, 4);
                break;
            case '4':
                strcpy(buff, "0100");
                strncat(binbuff, buff, 4);
                break;
            case '5':
                strcpy(buff, "0101");
                strncat(binbuff, buff, 4);
                break;
            case '6':
                strcpy(buff, "0110");
                strncat(binbuff, buff, 4);
                break;
            case '7':
                strcpy(buff, "0111");
                strncat(binbuff, buff, 4);
                break;
            case '8':
                strcpy(buff, "1000");
                strncat(binbuff, buff, 4);
                break;
            case '9':
                strcpy(buff, "1001");
                strncat(binbuff, buff, 4);
                break;
            case 'a':
            case 'A':
                strcpy(buff, "1010");
                strncat(binbuff, buff, 4);
                break;
            case 'b':
            case 'B':
                strcpy(buff, "1011");
                strncat(binbuff, buff, 4);
                break;
            case 'c':
            case 'C':
                strcpy(buff, "1100");
                strncat(binbuff, buff, 4);
                break;
            case 'd':
            case 'D':
                strcpy(buff, "1101");
                strncat(binbuff, buff, 4);
                break;
            case 'e':
            case 'E':
                strcpy(buff, "1110");
                strncat(binbuff, buff, 4);
                break;
            case 'f':
            case 'F':
                strcpy(buff, "1111");
                strncat(binbuff, buff, 4);
                break;
            default:
                printf("err hex\n");
                break;
        }
    }
    strcpy(bin, binbuff);
}

// 二进制转十六进制
void bin2hex(char *bin, char *hex)
{
    int len = strlen(bin) / 4;
    char buf[5] = { 0 };
    for (size_t i = 0; i < len; i++) {
        for (size_t j = 0; j < 4; j++) {
            buf[j] = bin[i * 4 + j];
        }
        int dec = bin2dec(buf);
        switch (dec) {
            case 0:
                hex[i] = '0';
                break;
            case 1:
                hex[i] = '1';
                break;
            case 2:
                hex[i] = '2';
                break;
            case 3:
                hex[i] = '3';
                break;
            case 4:
                hex[i] = '4';
                break;
            case 5:
                hex[i] = '5';
                break;
            case 6:
                hex[i] = '6';
                break;
            case 7:
                hex[i] = '7';
                break;
            case 8:
                hex[i] = '8';
                break;
            case 9:
                hex[i] = '9';
                break;
            case 10:
                hex[i] = 'A';
                break;
            case 11:
                hex[i] = 'B';
                break;
            case 12:
                hex[i] = 'C';
                break;
            case 13:
                hex[i] = 'D';
                break;
            case 14:
                hex[i] = 'E';
                break;
            case 15:
                hex[i] = 'F';
                break;
            default:
                break;
        }
    }
}

// 初始IP置换
void init_switch(char *m)
{
    char temp[65] = { 0 };
    strcpy(temp, m);
    for (int i = 0; i < 64; i++) {
        m[i] = temp[ip_table[i] - 1];
    }
}

// IP逆置换
void re_ipswitch(char *m)
{
    char temp[65] = { 0 };
    strcpy(temp, m);
    for (int i = 0; i < 64; i++) {
        m[i] = temp[ipre_table[i] - 1];
    }
}

// E扩展运算
void extend(char *r, char *e)
{
    for (int i = 0; i < 48; i++) {
        e[i] = r[e_table[i] - 1];
    }
}

// S盒置换
void s_switch(char *str, char *end)
{
    char s[7] = { 0 };
    char hang[3] = { 0 };
    int h = 0;
    char lie[5] = { 0 };
    int l = 0;
    int afters[8] = { 0 };
    for (size_t i = 0; i < 8; i++) {
        for (size_t j = 0; j < 6; j++) {
            s[j] = str[6 * i + j];
        }
        hang[0] = s[0];
        hang[1] = s[5];
        for (size_t k = 0; k < 4; k++) {
            lie[k] = s[1 + k];
        }

        // 二进制转十进制
        h = bin2dec(hang);
        l = bin2dec(lie);

        // 完成S盒转换
        afters[i] = s_table[i][h][l];
    }
    char buf[5] = { 0 };
    for (size_t i = 0; i < 8; i++) {
        // 将S盒变换后的数字转成二进制,并补位成四位
        snprintf(buf, sizeof(buf), "%04d", dec2bin(afters[i]));
        int lenofbuf = strlen(buf);
        switch (lenofbuf) {
            case 4:
                break;
                // 进行补位
            case 3:
                buf[3] = buf[2];
                buf[2] = buf[1];
                buf[1] = buf[0];
                buf[0] = '0';
                break;
            case 2:
                buf[2] = buf[0];
                buf[3] = buf[1];
                buf[0] = '0';
                buf[1] = '0';
                break;
            case 1:
                buf[3] = buf[0];
                buf[0] = '0';
                buf[1] = '0';
                buf[2] = '0';
                break;
            default:
                break;
        }
        for (size_t j = 0; j < 4; j++) {
            end[4 * i + j] = buf[j];
        }
    }
}

// P盒置换
void p_switch(char *start, char *end)
{
    for (size_t i = 0; i < 32; i++) {
        end[i] = start[p_table[i] - 1];
    }
}

// 异或运算
// 参数分别为异或的两个数,返回的结果和异或的位数
void xor(char *a, char *b, char *res, int len)
{
    if (a == NULL) {    // 初始向量为空时,直接返回b
        strcpy(res, b);
    }

    char ret[64] = { 0 };
    for (size_t i = 0; i < len; i++) {
        if (a[i] == b[i]) {
            ret[i] = '0';
        } else {
            ret[i] = '1';
        }
    }
    strcpy(res, ret);
}

// PC1置换生成初始C0 D0
void init_key(char *key, char *c0, char *d0)
{
    char cd[57] = { 0 };
    char temp[65] = { 0 };
    strcpy(temp, key);

    // PC1置换
    for (size_t i = 0; i < 56; i++) {
        cd[i] = temp[pc1_table[i] - 1];
    }
    // printf("After PC1 switch: %s\n", cd);

    // 将CD分解为C和D
    for (size_t i = 0; i < 28; i++) {
        c0[i] = cd[i];
        d0[i] = cd[28 + i];
    }
}

// 循环左移位操作
void ls_switch(char *str, int round)
{
    int times = ls_table[round - 1];
    char temp[28] = { 0 };
    strcpy(temp, str);
    for (size_t i = 0; i < 28; i++) {
        str[i] = temp[(i + times) % 28];
    }
}

// 合并左右字符串
void str_merge(char *left, char *right, char *out, int half_len)
{
    for (size_t i = 0; i < half_len; i++) {
        out[i] = left[i];
        out[half_len + i] = right[i];
    }
}

// 子密钥生成函数
void key_gen(char *key, char *c, char *d, int round, char *after_key)
{
    // 进行循环左移位
    ls_switch(c, round);
    ls_switch(d, round);

    // 将移位后的c、d合并到cd
    char cd[57] = { 0 };
    str_merge(c, d, cd, 28);

    // PC2置换生成子密钥
    for (size_t i = 0; i < 48; i++) {
        after_key[i] = cd[pc2_table[i] - 1];
    }
}

// DES加密
void des_encrypt(char *src, char *key, char *out)
{
    char temp[65] = { 0 };
    strcpy(temp, src);

    // 进行IP置换
    init_switch(temp);
    char left[33] = { 0 };
    char right[33] = { 0 };
    // printf("After IP switch: %s\n", temp);

    // 将明文分为左右两部分
    for (size_t i = 0; i < 32; i++) {
        left[i] = temp[i];
        right[i] = temp[32 + i];
    }
    // printf("Left0: %s\n", left);
    // printf("Right0: %s\n", right);

	// PC1置换生成初始C0 D0
    char c[29] = { 0 };
    char d[29] = { 0 };
    init_key(key, c, d);
    // printf("C0: %s\n", c);
    // printf("D0: %s\n", d);
    // printf("------------------------\n");

    // 进行十六轮加密
    for (size_t round = 1; round < 17; round++) {
        // E扩展运算
        char er[49] = { 0 };
        extend(right, er);
        // printf("E%d: %s\n", round, er);

        // 生成16轮密钥
        char key_tmp[49] = { 0 };
        key_gen(key, c, d, round, key_tmp);
        // printf("Round %zu Key: %s\n", round, key_tmp);
        // printf("C%d: %s\n", round,  c);
        // printf("D%d: %s\n", round,  d);

        // 密钥与E(R)异或得到结果res
        char res[49] = { 0 };
        xor(key_tmp, er, res, 48);
        // printf("EK%d: %s\n", round, res);

        // 进行S盒置换
        char afters[64] = { 0 };
        s_switch(res, afters);
        // printf("S%d: %s\n", round, afters);

        // 再进行P置换
        char afterp[33] = { 0 };
        p_switch(afters, afterp);
        // printf("P%d: %s\n", round, afterp);

        // 两边再异或
        char r0[33] = { 0 };
        xor(left, afterp, r0, 32);
        strcpy(left, right);
        strcpy(right, r0);
        // printf("Left%d: %s\n", round, left);
        // printf("Right%d: %s\n", round, right);
        // printf("------------------------\n");
    }

    // 左右交换后合并
    str_merge(right, left, temp, 32);

    // 进行逆IP置换
    re_ipswitch(temp);
    // printf("After IP-1 switch: %s\n", temp);
    strcpy(out, temp);
}

// DES解密
void des_decrypt(char *src, char *key, char *out)
{
    char temp[65] = { 0 };
    strcpy(temp, src);
    char keys[16][65] = { 0 };

    // 进行IP置换
    init_switch(temp);

    // 将密文分为左右两部分
    char left[33] = { 0 };
    char right[33] = { 0 };
    for (size_t i = 0; i < 32; i++) {
        left[i] = temp[i];
        right[i] = temp[32 + i];
    }

	// PC1置换生成初始轮C0 D0
    char c[29] = { 0 };
    char d[29] = { 0 };
    init_key(key, c, d);
    // printf("C0: %s\n", c);
    // printf("D0: %s\n", d);
    // printf("------------------------\n");

    // 生成十六轮密钥
    for (size_t i = 0; i < 16; i++) {
       key_gen(key, c, d, i + 1, keys[i]);
       keys[i][48] = '\0';
    }

    // 解密
    for (size_t round = 1; round < 17; round++) {
        // E扩展运算
        char er[49] = { 0 };
        extend(right, er);
        // printf("exr:\t%s\n", er);

        // 密钥与E(R)异或得到结果res
        char res[49] = { 0 };
        xor(keys[16 - round], er, res, 48);
        // printf("kxer:\t%s\n", res);

        // 进行S盒置换
        char afters[64] = { 0 };
        s_switch(res, afters);
        // printf("sw:\t%s\n", afters);

        // 再进行P盒置换
        char afterp[33] = { 0 };
        p_switch(afters, afterp);
        // printf("ps:\t%s\n", afterp);

        // 两边再异或
        char r0[33] = { 0 };
        xor(left, afterp, r0, 32);
        strcpy(left, right);
        strcpy(right, r0);
        // printf("l%d:\t%s\n", round, left);
        // printf("r%d:\t%s\n", round, right);
        // printf("------------------------\n");
    }
    // 左右交换后合并
    str_merge(right, left, temp, 32);

    // 进行逆IP置换
    re_ipswitch(temp);
    strcpy(out, temp);
}

int main()
{
    char *src = "12345678";
    char *key = "12345678";
    char out_bin[64] = { 0 };
    char out_hex[17] = { 0 };
    char src_bin[65] = { 0 };
    char key_bin[65] = { 0 };
    // char src_bin[65] = "0110001101101111011011010111000001110101011101000110010101110010";
    // char key_bin[65] = "0001001100110100010101110111100110011011101111001101111111110001";

    // 明文转二进制
    char src_hex[17] = { 0 };
    single_byte_character_to_hex(src, src_hex);
    hex2bin(src_hex, src_bin);
    printf("Plaintext  bin: %s\n", src_bin);
    printf("Plaintext  hex: %s\n", src_hex);

    // 密钥转二进制
    char key_hex[15] = { 0 };
    single_byte_character_to_hex(key, key_hex);
    hex2bin(key_hex, key_bin);
    printf("Key        bin: %s\n", key_bin);
    printf("Key        hex: %s\n", key_hex);

    // 执行DES加密
    des_encrypt(src_bin, key_bin, out_bin);

    // 二进制转十六进制输出密文
    bin2hex(out_bin, out_hex);
    printf("Ciphertext bin: %s\n", out_bin);
    printf("Ciphertext hex: %s\n", out_hex);

    // 执行DES解密
    char decrypted_bin[65] = { 0 };
    char decrypted_hex[17] = { 0 };
    des_decrypt(out_bin, key_bin, decrypted_bin);

    // 二进制转十六进制输出明文
    bin2hex(decrypted_bin, decrypted_hex);
    printf("Decrypted  bin: %s\n", decrypted_bin);
    printf("Decrypted  hex: %s\n", decrypted_hex);
    return 0;
}
posted @ 2025-11-29 20:58  小小船帆  阅读(7)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3