凯撒密码

PHP 实现凯撒密码:Web 加密完整教程

通过完整代码示例学习如何在 PHP 中实现凯撒密码。涵盖基础函数、面向对象类设计、UTF-8 处理、Web 表单集成以及用于加密的 REST API 端点。

发布于 2025年8月11日
10 分钟阅读
密码学指南

PHP 是 Web 上部署最广泛的服务端语言之一,从个人博客到企业应用无所不在。在 PHP 中实现凯撒密码是一项实践性极强的练习,融合了字符串操作、面向对象设计和 Web 开发技能。由于 PHP 与 Web 应用紧密相连,本教程不仅限于独立脚本,还将展示如何构建执行凯撒密码加密的 Web 表单和 REST API 端点。

本指南涵盖五种实现方案:基础函数、具备完整功能集的面向对象类、支持 UTF-8 文本的多字节安全版本、交互式 HTML 表单,以及 JSON REST API 端点。所有代码均可在 PHP 8.1 或更高版本上运行。

在线体验:在动手编写自己的 PHP 实现之前,先用我们免费的凯撒密码编码器即时体验凯撒密码加密。

基础 PHP 函数

最简单的方式是使用一个独立函数,逐字符遍历输入字符串,对字母进行位移,同时保留非字母字符不变。

<?php

function caesarEncrypt(string $text, int $shift): string
{
    // 将位移规范化到 0-25 范围
    $shift = (($shift % 26) + 26) % 26;
    $result = '';

    for ($i = 0; $i < strlen($text); $i++) {
        $char = $text[$i];

        if (ctype_upper($char)) {
            $result .= chr((ord($char) - ord('A') + $shift) % 26 + ord('A'));
        } elseif (ctype_lower($char)) {
            $result .= chr((ord($char) - ord('a') + $shift) % 26 + ord('a'));
        } else {
            $result .= $char;
        }
    }

    return $result;
}

function caesarDecrypt(string $text, int $shift): string
{
    return caesarEncrypt($text, 26 - (($shift % 26 + 26) % 26));
}

// 示例用法
$message = "Hello, World! The quick brown fox jumps over the lazy dog.";
$shift = 7;

$encrypted = caesarEncrypt($message, $shift);
$decrypted = caesarDecrypt($encrypted, $shift);

echo "Original:  $message\n";
echo "Encrypted: $encrypted\n";
echo "Decrypted: $decrypted\n";

输出:

Original:  Hello, World! The quick brown fox jumps over the lazy dog.
Encrypted: Olssv, Dvysk! Aol xbpjr iyvdu mve qbtwz vcly aol shgf kvn.
Decrypted: Hello, World! The quick brown fox jumps over the lazy dog.

该实现的几个关键细节:

  • ord()chr():PHP 的 ord() 函数返回字符的 ASCII 值,chr() 将 ASCII 值转换回字符。这是 PHP 中与 C 或 Java 整数类型转换等价的操作。
  • ctype_upper()ctype_lower():这两个函数检查字符大小写的可靠性优于手动 ASCII 范围比较,不过对于 ASCII 输入,两种方式均能正确工作。
  • 字符串拼接:PHP 字符串是可变的,但 .= 运算符在内部会创建新字符串。对于非常长的文本,这种方式效率尚可,因为 PHP 的字符串引擎会自动管理内存。

面向对象的 CaesarCipher 类

精心设计的面向对象类可以封装位移值,并为加密、解密和暴力破解分析提供简洁的接口。该类还支持方法链式调用,并实现了 PHP 8.1+ 的特性,如只读属性和枚举。

<?php

class CaesarCipher
{
    public readonly int $shift;

    public function __construct(int $shift)
    {
        $this->shift = (($shift % 26) + 26) % 26;
    }

    /**
     * 使用配置的位移值加密明文。
     */
    public function encrypt(string $text): string
    {
        return $this->transform($text, $this->shift);
    }

    /**
     * 使用配置的位移值解密密文。
     */
    public function decrypt(string $text): string
    {
        return $this->transform($text, 26 - $this->shift);
    }

    /**
     * 尝试所有 26 种可能的位移并返回结果。
     *
     * @return array<int, string> 位移值到解密文本的关联数组
     */
    public static function bruteForce(string $ciphertext): array
    {
        $results = [];
        for ($shift = 0; $shift < 26; $shift++) {
            $cipher = new self($shift);
            $results[$shift] = $cipher->decrypt($ciphertext);
        }
        return $results;
    }

    /**
     * 根据英文字母频率对文本评分,以帮助识别
     * 暴力破解结果中最可能正确的解密。
     */
    public static function scoreEnglish(string $text): float
    {
        $frequencies = [
            'a' => 8.167, 'b' => 1.492, 'c' => 2.782, 'd' => 4.253,
            'e' => 12.702, 'f' => 2.228, 'g' => 2.015, 'h' => 6.094,
            'i' => 6.966, 'j' => 0.153, 'k' => 0.772, 'l' => 4.025,
            'm' => 2.406, 'n' => 6.749, 'o' => 7.507, 'p' => 1.929,
            'q' => 0.095, 'r' => 5.987, 's' => 6.327, 't' => 9.056,
            'u' => 2.758, 'v' => 0.978, 'w' => 2.360, 'x' => 0.150,
            'y' => 1.974, 'z' => 0.074,
        ];

        $score = 0.0;
        $letterCount = 0;
        $lower = strtolower($text);

        for ($i = 0; $i < strlen($lower); $i++) {
            $ch = $lower[$i];
            if (isset($frequencies[$ch])) {
                $score += $frequencies[$ch];
                $letterCount++;
            }
        }

        return $letterCount > 0 ? $score / $letterCount : 0.0;
    }

    /**
     * 自动检测密文最可能的位移值。
     *
     * @return array{shift: int, text: string, score: float}
     */
    public static function autoDecrypt(string $ciphertext): array
    {
        $results = self::bruteForce($ciphertext);
        $bestShift = 0;
        $bestScore = 0.0;

        foreach ($results as $shift => $text) {
            $score = self::scoreEnglish($text);
            if ($score > $bestScore) {
                $bestScore = $score;
                $bestShift = $shift;
            }
        }

        return [
            'shift' => $bestShift,
            'text' => $results[$bestShift],
            'score' => round($bestScore, 3),
        ];
    }

    private function transform(string $text, int $shift): string
    {
        $result = '';
        $len = strlen($text);

        for ($i = 0; $i < $len; $i++) {
            $ch = $text[$i];
            $ord = ord($ch);

            if ($ord >= 65 && $ord <= 90) {
                $result .= chr(($ord - 65 + $shift) % 26 + 65);
            } elseif ($ord >= 97 && $ord <= 122) {
                $result .= chr(($ord - 97 + $shift) % 26 + 97);
            } else {
                $result .= $ch;
            }
        }

        return $result;
    }

    public function __toString(): string
    {
        return "CaesarCipher(shift={$this->shift})";
    }
}

// 使用示例
$cipher = new CaesarCipher(13);
echo $cipher . "\n";
echo "Encrypted: " . $cipher->encrypt("Attack at dawn!") . "\n";
echo "Decrypted: " . $cipher->decrypt("Nggnpx ng qnja!") . "\n";

// 自动检测加密
echo "\n--- Auto Decrypt ---\n";
$result = CaesarCipher::autoDecrypt("Wklv lv d vhfuhw phvvdjh!");
echo "Detected shift: {$result['shift']}\n";
echo "Decrypted text: {$result['text']}\n";
echo "Confidence score: {$result['score']}\n";

autoDecrypt() 方法尤为实用。它执行暴力破解攻击,然后利用英文字母频率分析自动识别最可能的位移值。评分函数将解密文本中每个字母的频率与已知的英文字母频率进行比较,得分最高的解密结果即为最可能正确的答案。

处理 UTF-8 与多字节字符

PHP 的标准字符串函数(如 strlen() 和方括号索引)操作的是字节,而非字符。当处理可能包含多字节 UTF-8 字符(重音字母、emoji、CJK 字符)的文本时,需要使用 PHP 的 mbstring 扩展。以下版本通过仅处理 ASCII 字母、安全透传所有其他字符,正确处理了 UTF-8 输入。

<?php

function caesarEncryptUtf8(string $text, int $shift): string
{
    $shift = (($shift % 26) + 26) % 26;
    $result = '';
    $length = mb_strlen($text, 'UTF-8');

    for ($i = 0; $i < $length; $i++) {
        $char = mb_substr($text, $i, 1, 'UTF-8');
        $ord = ord($char);

        // 仅转换单字节 ASCII 字母
        if (strlen($char) === 1) {
            if ($ord >= 65 && $ord <= 90) {
                $result .= chr(($ord - 65 + $shift) % 26 + 65);
                continue;
            } elseif ($ord >= 97 && $ord <= 122) {
                $result .= chr(($ord - 97 + $shift) % 26 + 97);
                continue;
            }
        }

        // 非 ASCII 和非字母字符原样透传
        $result .= $char;
    }

    return $result;
}

// 包含国际化文本的示例
echo caesarEncryptUtf8("Hello, World!", 3) . "\n";
// Output: Khoor, Zruog!

echo caesarEncryptUtf8("Café résumé — enjoy!", 5) . "\n";
// Output: Hfké wéxzré — jsotd!

echo caesarEncryptUtf8("Tokyo 東京 is amazing!", 10) . "\n";
// Output: Dyuiy 東京 sc kwkjsxq!

关键在于通过检查 strlen($char) === 1 来判断字符是否为单字节 ASCII 字符。多字节 UTF-8 字符(包括带重音的字母,如带锐音符的 e)的字节长度大于 1,因此会原样透传。这是经典凯撒密码的正确行为——凯撒密码只对 26 个英文字母进行操作。

Web 表单集成

PHP 的一大优势在于与 HTML 表单的天然融合。以下示例创建了一个完整的自包含网页,用户可通过浏览器界面对文本进行加密和解密。

<?php
$result = '';
$inputText = '';
$shift = 3;
$mode = 'encrypt';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $inputText = $_POST['text'] ?? '';
    $shift = (int)($_POST['shift'] ?? 3);
    $mode = $_POST['mode'] ?? 'encrypt';

    // 对输入进行净化以防止 XSS
    $inputText = strip_tags($inputText);

    $normalizedShift = (($shift % 26) + 26) % 26;
    $effectiveShift = ($mode === 'decrypt')
        ? 26 - $normalizedShift
        : $normalizedShift;

    $result = '';
    for ($i = 0; $i < strlen($inputText); $i++) {
        $ch = $inputText[$i];
        $ord = ord($ch);

        if ($ord >= 65 && $ord <= 90) {
            $result .= chr(($ord - 65 + $effectiveShift) % 26 + 65);
        } elseif ($ord >= 97 && $ord <= 122) {
            $result .= chr(($ord - 97 + $effectiveShift) % 26 + 97);
        } else {
            $result .= $ch;
        }
    }
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Caesar Cipher Tool</title>
    <style>
        body { font-family: system-ui, sans-serif; max-width: 600px; margin: 2rem auto; padding: 0 1rem; }
        label { display: block; margin-top: 1rem; font-weight: bold; }
        textarea, input, select, button { width: 100%; padding: 0.5rem; margin-top: 0.25rem; box-sizing: border-box; }
        textarea { height: 120px; font-family: monospace; }
        button { margin-top: 1rem; background: #2563eb; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 1rem; }
        button:hover { background: #1d4ed8; }
        .result { margin-top: 1.5rem; padding: 1rem; background: #f0fdf4; border: 1px solid #86efac; border-radius: 4px; }
        .result pre { white-space: pre-wrap; word-wrap: break-word; margin: 0; }
    </style>
</head>
<body>
    <h1>Caesar Cipher</h1>

    <form method="POST">
        <label for="text">Enter your text:</label>
        <textarea id="text" name="text" required><?= htmlspecialchars($inputText) ?></textarea>

        <label for="shift">Shift value (1-25):</label>
        <input type="number" id="shift" name="shift" min="1" max="25"
               value="<?= htmlspecialchars((string)$shift) ?>">

        <label for="mode">Mode:</label>
        <select id="mode" name="mode">
            <option value="encrypt" <?= $mode === 'encrypt' ? 'selected' : '' ?>>Encrypt</option>
            <option value="decrypt" <?= $mode === 'decrypt' ? 'selected' : '' ?>>Decrypt</option>
        </select>

        <button type="submit">Process</button>
    </form>

    <?php if ($result !== '' && $_SERVER['REQUEST_METHOD'] === 'POST'): ?>
    <div class="result">
        <h2><?= $mode === 'encrypt' ? 'Encrypted' : 'Decrypted' ?> Result:</h2>
        <pre><?= htmlspecialchars($result) ?></pre>
    </div>
    <?php endif; ?>
</body>
</html>

此 Web 表单的安全性考量:

  • htmlspecialchars():所有用户输入在渲染到 HTML 之前均经过转义,以防止跨站脚本(XSS)攻击。对于任何展示用户输入的 Web 表单,这一点至关重要。
  • strip_tags():在处理之前移除输入文本中的所有 HTML 标签,作为防御 XSS 的额外保护层。
  • 输入验证:位移值被强制转换为 int,防止类型混淆。HTML 的 minmax 属性提供客户端验证,但真正执行约束的是服务端验证(取模规范化)。

将此文件保存为 caesar.php,使用 PHP 内置服务器快速测试:

php -S localhost:8000
# 然后在浏览器中打开 http://localhost:8000/caesar.php

REST API 端点

对于编程访问,JSON API 比 HTML 表单更为合适。以下实现提供了加密、解密和暴力破解端点,接受 JSON 输入并返回 JSON 响应。它适合与 Laravel 或 Slim 等框架配合使用,也可独立运行。

<?php

header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');

// 处理 CORS 预检请求
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(204);
    exit;
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['error' => 'Method not allowed. Use POST.']);
    exit;
}

$input = json_decode(file_get_contents('php://input'), true);

if ($input === null) {
    http_response_code(400);
    echo json_encode(['error' => 'Invalid JSON input.']);
    exit;
}

$action = $input['action'] ?? '';
$text = $input['text'] ?? '';
$shift = (int)($input['shift'] ?? 0);

if (empty($text)) {
    http_response_code(400);
    echo json_encode(['error' => 'Text field is required.']);
    exit;
}

// 限制输入长度以防止滥用
if (strlen($text) > 10000) {
    http_response_code(400);
    echo json_encode(['error' => 'Text exceeds maximum length of 10,000 characters.']);
    exit;
}

function transform(string $text, int $shift): string
{
    $shift = (($shift % 26) + 26) % 26;
    $result = '';
    for ($i = 0; $i < strlen($text); $i++) {
        $ord = ord($text[$i]);
        if ($ord >= 65 && $ord <= 90) {
            $result .= chr(($ord - 65 + $shift) % 26 + 65);
        } elseif ($ord >= 97 && $ord <= 122) {
            $result .= chr(($ord - 97 + $shift) % 26 + 97);
        } else {
            $result .= $text[$i];
        }
    }
    return $result;
}

$response = match ($action) {
    'encrypt' => [
        'action' => 'encrypt',
        'shift' => (($shift % 26) + 26) % 26,
        'input' => $text,
        'result' => transform($text, $shift),
    ],
    'decrypt' => [
        'action' => 'decrypt',
        'shift' => (($shift % 26) + 26) % 26,
        'input' => $text,
        'result' => transform($text, 26 - (($shift % 26 + 26) % 26)),
    ],
    'bruteforce' => [
        'action' => 'bruteforce',
        'input' => $text,
        'results' => array_map(
            fn(int $s) => [
                'shift' => $s,
                'text' => transform($text, 26 - $s),
            ],
            range(0, 25)
        ),
    ],
    default => null,
};

if ($response === null) {
    http_response_code(400);
    echo json_encode([
        'error' => "Invalid action '$action'. Use 'encrypt', 'decrypt', or 'bruteforce'.",
    ]);
    exit;
}

echo json_encode($response, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

使用 curl 调用:

# 加密
curl -X POST http://localhost:8000/api.php \
  -H "Content-Type: application/json" \
  -d '{"action":"encrypt","text":"Hello World","shift":5}'

# 响应:
# {
#     "action": "encrypt",
#     "shift": 5,
#     "input": "Hello World",
#     "result": "Mjqqt Btwqi"
# }

# 解密
curl -X POST http://localhost:8000/api.php \
  -H "Content-Type: application/json" \
  -d '{"action":"decrypt","text":"Mjqqt Btwqi","shift":5}'

# 暴力破解
curl -X POST http://localhost:8000/api.php \
  -H "Content-Type: application/json" \
  -d '{"action":"bruteforce","text":"Khoor Zruog"}'

该 API 包含若干生产级考量:

  • CORS 头:允许跨域请求,使 API 可以从不同域的 JavaScript 代码中调用。
  • 输入长度限制:拒绝超过 10,000 个字符的文本,防止滥用。
  • 请求方式验证:仅接受 POST 请求,其他方式返回 405 状态码。
  • JSON 验证:格式错误的 JSON 输入返回清晰的 400 错误。
  • match 表达式:PHP 8.0 的 match 表达式提供了一种简洁、穷举的方式来路由不同动作。

性能优化建议

对于大多数使用场景,基础函数已经足够快速。PHP 使用简单字符串操作每秒可处理数百万个字符。但如果你正在构建高流量 API 端点,可以考虑以下优化:

避免重复调用 strlen:将 strlen() 的结果存储在变量中,而不是在每次循环迭代中都调用它。虽然 PHP 内部会缓存字符串长度,但函数调用的开销依然存在。

使用 strtr 进行简单替换:PHP 的 strtr() 函数在给定转换表时,能非常高效地执行逐字符转换:

function caesarFast(string $text, int $shift): string
{
    $shift = (($shift % 26) + 26) % 26;
    $from = implode('', array_map('chr', [...range(65, 90), ...range(97, 122)]));
    $to = '';
    foreach ([65, 97] as $base) {
        for ($i = 0; $i < 26; $i++) {
            $to .= chr(($i + $shift) % 26 + $base);
        }
    }
    return strtr($text, $from, $to);
}

这种方式只构建一次替换表,然后通过单次 C 级函数调用处理整个字符串,对于非常长的字符串,比 PHP 循环快得多。

总结

PHP 的多功能性使其成为实现凯撒密码的优秀语言,覆盖范围从简单脚本到 Web 应用和 API。基础函数满足独立使用场景;面向对象类提供可复用组件并支持自动检测;UTF-8 版本正确处理国际化文本;Web 表单创建交互式浏览器体验;REST API 则支持任意客户端的编程访问。

这些实现展示了 PHP 在 Web 开发中的优势,同时传授了基础密码学概念。示例中涉及的模式——包括输入验证、XSS 防护、CORS 处理和 JSON API——适用于任何 PHP Web 应用,使本教程的价值远不止于密码学练习。

下一步:体验我们的交互式凯撒密码编码器解码器,或探索维吉尼亚密码,了解更高级的加密技术。

关于本文

本文是我们综合 凯撒密码 教程系列的一部分。继续了解古典密码学,并探索我们的交互式密码工具。

更多 凯撒密码 教程

试用 凯撒密码 工具

通过我们的交互式凯撒密码工具,将所学知识付诸实践。

试用 凯撒密码 工具