“紫密”的前辈Enigma是怎么工作的

在密码学史中,恩尼格玛密码机(Enigma)是一种具有影响力的加密与解密文件密码机。该密码机采用了一系列相似的转子机械加解密机制,为对称加密算法的流加密奠定了基础。

恩尼格玛密码机外观看起来是一个装满了复杂而精致元件的盒子。然而,当我们打开它时,可以将其分解为相对简单的几个部分。最核心的部分包括键盘、显示器和三个转子。键盘用于输入明文,显示器则用于显示加密后的密文。三个转子负责将明文转换为密文的过程。

在恩尼格玛密码机中,当按下键盘上的某个键时,与该字母对应的密文会在显示器上亮起。这是因为转子内部集成了6条线路(在实物中是26条),可以将键盘的信号对应到显示器不同的小灯上去。例如,如果按下字母’a’键,灯’B’就会亮起,这意味着字母’a’被加密成了字母’B’。

为了提高加密的复杂性,恩尼格玛密码机还采用了三个转子的旋转机制。每个转子都可以独立旋转,并具有26个不同的位置。当输入一个字母时,转子会旋转一定的角度,使得相同的字母在加密时对应不同的密文。这种机制增加了破译的难度,使得恩尼格玛密码机在二战期间成为了一种难以破解的加密工具。

此外,为了使消息更难以破译,恩尼格玛密码机还采用了空格和标点符号的省略机制。这意味着在加密过程中,空格和标点符号不会被包括在内,从而使得密文更难以被解读。

总体而言,恩尼格玛密码机的工作原理是通过键盘输入明文,经过三个转子的转换和加密算法的处理,最终在显示器上显示密文。这种加密方式具有很高的复杂性和难以破解的特点,因此在密码学史上具有重要的地位。

然而,尽管恩尼格玛密码机在二战期间被广泛使用并被认为是安全的加密工具,但在二战末期,英国的密码学家通过不懈的努力成功地破解了恩尼格玛密码机。这一事件成为了密码学史上的一个重要里程碑,也为我们提供了一个宝贵的教训:随着技术的发展和进步,没有任何一种加密方式是绝对安全的。

下面用C语言来实现这个加密过程,代码比较简单,仅供参考。可自己完善。

先来一段明文:Send troops to Poland today(今日出兵波兰),中间空格要去掉。

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

#define MAX_CHARS 26

// 交换两个字符
void swap(char *a, char *b) {
    char temp = *a;
    *a = *b;
    *b = temp;
}

// 随机打乱字符数组
void shuffle(char *arr, int n) {
    for (int i = n - 1; i > 0; i--) {
        int j = rand() % (i + 1);
        swap(&arr[i], &arr[j]);
    }
}

int main() {
    // 生成字母表
    char a[MAX_CHARS];
    for (int i = 0; i < MAX_CHARS; i++) {
        a[i] = 'a' + i;
    }

    // 复制并打乱字母表
    char b[MAX_CHARS];
    memcpy(b, a, MAX_CHARS);
    shuffle(b, MAX_CHARS);

    // 建立对应密码表 (这里使用数组代替字典,下标作为索引)
    char dict[MAX_CHARS][2];
    for (int i = 0; i < MAX_CHARS; i++) {
        dict[i][0] = a[i];
        dict[i][1] = b[i];
    }

    // 待加密字符串
    char word[] = "sendtroopstopolandtoday";
    char encrypted[MAX_CHARS];

    printf("加密前: %s\n", word);

    // 加密
    int j;
    for (int i = 0; i < strlen(word); i++) {
        for (j = 0; j < MAX_CHARS; j++) {
            if (word[i] == dict[j][0]) {
                encrypted[i] = dict[j][1];
                break;
            }
        }
    }
    encrypted[strlen(word)] = '\0';

    printf("加密后: %s\n", encrypted);

    return 0;
}

结果是:

加密前: sendtroopstopolandtoday
加密后: dgncsuffjdsfjfylncsfclr

先别高兴太早,这只是单张表,很容易被破解。

为了提升破解者的难度,下面用两张表来混合加密。

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

#define MAX_CHARS 26

// 交换两个字符
void swap(char *a, char *b) {
    char temp = *a;
    *a = *b;
    *b = temp;
}

// 随机打乱字符数组
void shuffle(char *arr, int n) {
    for (int i = n - 1; i > 0; i--) {
        int j = rand() % (i + 1);
        swap(&arr[i], &arr[j]);
    }
}

int main() {
    // 初始化随机数种子
    srand(time(NULL));

    // 生成字母表
    char a[MAX_CHARS];
    for (int i = 0; i < MAX_CHARS; i++) {
        a[i] = 'a' + i;
    }

    // 复制并打乱字母表两次
    char b[MAX_CHARS], c[MAX_CHARS];
    memcpy(b, a, MAX_CHARS);
    memcpy(c, a, MAX_CHARS);
    shuffle(b, MAX_CHARS);
    shuffle(c, MAX_CHARS);

    // 建立对应密码表 (使用二维数组表示)
    char dict1[MAX_CHARS][2], dict2[MAX_CHARS][2];
    for (int i = 0; i < MAX_CHARS; i++) {
        dict1[i][0] = a[i];
        dict1[i][1] = b[i];
        dict2[i][0] = a[i];
        dict2[i][1] = c[i];
    }

    // 待加密字符串
    char word[] = "sendtroopstopolandtoday";
    char encrypted[100];  // 确保足够大

    printf("加密前: %s\n", word);

    // 加密
    int j;
    for (int i = 0; i < strlen(word); i++) {
        for (j = 0; j < MAX_CHARS; j++) {
            if (word[i] == dict1[j][0]) {
                encrypted[i] = i % 2 == 0 ? dict1[j][1] : dict2[j][1];
                break;
            }
        }
    }
    encrypted[strlen(word)] = '\0';

    printf("加密后: %s\n", encrypted);

    return 0;
}

结果是:

加密前: sendtroopstopolandtoday
加密后: yazkownmfuomfmiqzkomhqx

双表混合法,即第一个字符是使用第一个映射表加密,第二个字符使用第二张映射表来进行加密,这可以使加密的安全度提高很多,也比较接近Enigma机的工作原理。

下面来讲明文的还原方式。

根据前面获得的两张映射表,将它们全部反转。

第一张表:

{‘a’: ‘s’, ‘b’: ‘t’, ‘c’: ‘r’, ‘d’: ‘h’, ‘e’: ‘b’, ‘f’: ‘w’, ‘g’: ‘q’, ‘h’: ‘j’, ‘i’: ‘p’, ‘j’: ‘e’, ‘k’: ‘v’, ‘l’: ‘i’, ‘m’: ‘g’, ‘n’: ‘z’, ‘o’: ‘n’, ‘p’: ‘f’, ‘q’: ‘m’, ‘r’: ‘u’, ‘s’: ‘y’, ‘t’: ‘o’, ‘u’: ‘d’, ‘v’: ‘c’, ‘w’: ‘l’, ‘x’: ‘a’, ‘y’: ‘x’, ‘z’: ‘k’}

第二张表:

{‘a’: ‘q’, ‘b’: ‘l’, ‘c’: ‘g’, ‘d’: ‘k’, ‘e’: ‘a’, ‘f’: ‘v’, ‘g’: ‘j’, ‘h’: ‘y’, ‘i’: ‘p’, ‘j’: ‘z’, ‘k’: ‘x’, ‘l’: ‘n’, ‘m’: ‘c’, ‘n’: ‘d’, ‘o’: ‘m’, ‘p’: ‘t’, ‘q’: ‘s’, ‘r’: ‘w’, ‘s’: ‘u’, ‘t’: ‘h’, ‘u’: ‘o’, ‘v’: ‘f’, ‘w’: ‘b’, ‘x’: ‘e’, ‘y’: ‘r’, ‘z’: ‘i’}

反转第一张表:

{‘s’: ‘a’, ‘t’: ‘b’, ‘r’: ‘c’, ‘h’: ‘d’, ‘b’: ‘e’, ‘w’: ‘f’, ‘q’: ‘g’, ‘j’: ‘h’, ‘p’: ‘i’, ‘e’: ‘j’, ‘v’: ‘k’, ‘i’: ‘l’, ‘g’: ‘m’, ‘z’: ‘n’, ‘n’: ‘o’, ‘f’: ‘p’, ‘m’: ‘q’, ‘u’: ‘r’, ‘y’: ‘s’, ‘o’: ‘t’, ‘d’: ‘u’, ‘c’: ‘v’, ‘l’: ‘w’, ‘a’: ‘x’, ‘x’: ‘y’, ‘k’: ‘z’}

反转第二张表:

{‘q’: ‘a’, ‘l’: ‘b’, ‘g’: ‘c’, ‘k’: ‘d’, ‘a’: ‘e’, ‘v’: ‘f’, ‘j’: ‘g’, ‘y’: ‘h’, ‘p’: ‘i’, ‘z’: ‘j’, ‘x’: ‘k’, ‘n’: ‘l’, ‘c’: ‘m’, ‘d’: ‘n’, ‘m’: ‘o’, ‘t’: ‘p’, ‘s’: ‘q’, ‘w’: ‘r’, ‘u’: ‘s’, ‘h’: ‘t’, ‘o’: ‘u’, ‘f’: ‘v’, ‘b’: ‘w’, ‘e’: ‘x’, ‘r’: ‘y’, ‘i’: ‘z’}

然后把密文填进去,就可以出来明文了。

这里还是用代码来实现:

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

int main() {
    char mm[] = "yazkownmfuomfmiqzkomhqx";
    char dict3[26][2] = {
        {'s', 'a'}, {'t', 'b'}, {'r', 'c'}, {'h', 'd'}, {'b', 'e'}, {'w', 'f'}, {'q', 'g'}, {'j', 'h'}, {'p', 'i'}, {'e', 'j'},
        {'v', 'k'}, {'i', 'l'}, {'g', 'm'}, {'z', 'n'}, {'n', 'o'}, {'f', 'p'}, {'m', 'q'}, {'u', 'r'}, {'y', 's'}, {'o', 't'},
        {'d', 'u'}, {'c', 'v'}, {'l', 'w'}, {'a', 'x'}, {'x', 'y'}, {'k', 'z'}
    };
    char dict4[26][2] = {
        {'q', 'a'}, {'l', 'b'}, {'g', 'c'}, {'k', 'd'}, {'a', 'e'}, {'v', 'f'}, {'j', 'g'}, {'y', 'h'}, {'p', 'i'}, {'z', 'j'},
        {'x', 'k'}, {'n', 'l'}, {'c', 'm'}, {'d', 'n'}, {'m', 'o'}, {'t', 'p'}, {'s', 'q'}, {'w', 'r'}, {'u', 's'}, {'h', 't'},
        {'o', 'u'}, {'f', 'v'}, {'b', 'w'}, {'e', 'x'}, {'r', 'y'}, {'i', 'z'}
    };

    char word[strlen(mm) + 1];
    word[strlen(mm)] = '\0';

    for (int i = 0; i < strlen(mm); i++) {
        if (i % 2 == 0) {
            for (int j = 0; j < 26; j++) {
                if (mm[i] == dict3[j][0]) {
                    word[i] = dict3[j][1];
                    break;
                }
            }
        } else {
            for (int j = 0; j < 26; j++) {
                if (mm[i] == dict4[j][0]) {
                    word[i] = dict4[j][1];
                    break;
                }
            }
        }
    }

    printf("明文是:%s\n", word);

    return 0;
}

明文是:sendtroopstopolandtoday

实际的Enigma机工作起来要比这个复杂很多,但基本原理类似。有兴趣的朋友可以深入研究。