You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于XTEA算法的ESP8266挑战响应认证方案实现问询

这个挑战响应认证的思路非常扎实,刚好能解决你ESP8266之间的授权控制问题。我结合Arduino环境给你拆解成可落地的步骤和代码示例,帮你快速实现:

挑战响应认证方案落地指南(ESP8266 Arduino)

整体流程细化

咱们把你说的思路拆成更明确的逻辑闭环:

  • 开发板B向A发送初始控制请求(比如GET /request-challenge)
  • 开发板A生成一个4字节的随机挑战值,临时缓存起来,然后把这个挑战返回给B
  • 开发板B用预共享的XTEA密钥加密这个挑战值,得到加密后的token
  • 开发板B向A发送验证请求(比如GET /toggle-relay?token=<加密后的十六进制字符串>)
  • 开发板A用同样的密钥加密缓存的挑战值,和收到的token对比:一致就触发继电器切换,不一致则拒绝请求

关键实现细节

1. XTEA算法实现(两个设备都要用到)

XTEA是轻量级块加密算法,非常适合资源有限的ESP8266。下面是适配Arduino的简化实现:

#include <stdint.h>
#include <cstring>

// 预共享密钥,两个设备必须完全一致!建议用16个任意字节
const uint32_t XTEA_KEY[4] = {0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x87654321};

// XTEA加密函数(4字节块)
void xtea_encrypt(uint32_t *v, const uint32_t *k) {
    uint32_t v0 = v[0], v1 = v[1], sum = 0, delta = 0x9E3779B9;
    for (int i = 0; i < 32; i++) {
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]);
        sum += delta;
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum >> 11) & 3]);
    }
    v[0] = v0; v[1] = v1;
}

// 将字节数组转成十六进制字符串,方便HTTP传输
String bytesToHex(const uint8_t *data, size_t length) {
    String hexStr;
    for (size_t i = 0; i < length; i++) {
        char hex[3];
        sprintf(hex, "%02X", data[i]);
        hexStr += hex;
    }
    return hexStr;
}

// 将十六进制字符串转成字节数组
bool hexToBytes(const String &hexStr, uint8_t *data, size_t maxLength) {
    if (hexStr.length() % 2 != 0) return false;
    size_t length = hexStr.length() / 2;
    if (length > maxLength) return false;
    for (size_t i = 0; i < length; i++) {
        char hexByte[3] = {hexStr[2*i], hexStr[2*i+1], '\0'};
        data[i] = strtol(hexByte, NULL, 16);
    }
    return true;
}

2. 开发板A(HTTP服务器)代码核心片段

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

ESP8266WebServer server(80);
uint32_t currentChallenge = 0; // 存储当前挑战值(4字节)
unsigned long challengeTimestamp = 0;
const unsigned long CHALLENGE_TIMEOUT = 5000; // 挑战5秒内有效,防止重放攻击

// 继电器控制引脚
const int RELAY_PIN = D1;

void setup() {
    pinMode(RELAY_PIN, OUTPUT);
    digitalWrite(RELAY_PIN, LOW); // 初始状态关闭

    // 初始化WiFi(替换成你的SSID和密码)
    WiFi.begin("你的WiFi名称", "你的WiFi密码");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
    }

    // 初始化随机种子
    randomSeed(analogRead(A0));

    // 注册路由
    server.on("/request-challenge", handleRequestChallenge);
    server.on("/toggle-relay", handleToggleRelay);
    server.begin();
}

void loop() {
    server.handleClient();
}

// 处理挑战请求
void handleRequestChallenge() {
    // 生成随机挑战值
    currentChallenge = random(0, UINT32_MAX);
    challengeTimestamp = millis();

    // 将挑战值转成十六进制字符串返回
    server.send(200, "text/plain", bytesToHex((uint8_t*)&currentChallenge, 4));
}

// 处理继电器切换请求
void handleToggleRelay() {
    // 检查挑战是否超时
    if (millis() - challengeTimestamp > CHALLENGE_TIMEOUT) {
        server.send(403, "text/plain", "Challenge expired");
        return;
    }

    // 获取传入的token
    if (!server.hasArg("token")) {
        server.send(400, "text/plain", "Missing token");
        return;
    }
    String tokenStr = server.arg("token");

    // 将token转成字节数组
    uint8_t tokenBytes[4];
    if (!hexToBytes(tokenStr, tokenBytes, 4)) {
        server.send(400, "text/plain", "Invalid token format");
        return;
    }

    // 加密当前挑战值,生成预期的token
    uint32_t challengeToEncrypt = currentChallenge;
    xtea_encrypt((uint32_t*)&challengeToEncrypt, XTEA_KEY);

    // 对比token
    if (memcmp(&challengeToEncrypt, tokenBytes, 4) == 0) {
        // 验证通过,切换继电器
        digitalWrite(RELAY_PIN, !digitalRead(RELAY_PIN));
        server.send(200, "text/plain", "Relay toggled successfully");
    } else {
        server.send(403, "text/plain", "Invalid token");
    }

    // 挑战值使用后失效,防止重复使用
    currentChallenge = 0;
}

3. 开发板B(HTTP客户端)代码核心片段

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

// 开发板A的IP地址(替换成你的实际IP)
const char* SERVER_IP = "192.168.1.100";

void setup() {
    Serial.begin(115200);

    // 初始化WiFi(替换成你的SSID和密码)
    WiFi.begin("你的WiFi名称", "你的WiFi密码");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("WiFi connected");
}

void loop() {
    // 模拟触发控制请求(你可以改成按键触发等)
    toggleRelay();
    delay(10000); // 每10秒触发一次
}

void toggleRelay() {
    HTTPClient http;

    // 第一步:获取挑战值
    String challengeUrl = "http://" + String(SERVER_IP) + "/request-challenge";
    if (http.begin(challengeUrl)) {
        int httpCode = http.GET();
        if (httpCode == HTTP_CODE_OK) {
            String challengeStr = http.getString();
            http.end();

            // 将挑战字符串转成字节数组
            uint8_t challengeBytes[4];
            if (hexToBytes(challengeStr, challengeBytes, 4)) {
                uint32_t challenge = *(uint32_t*)challengeBytes;

                // 第二步:用XTEA加密挑战值生成token
                xtea_encrypt((uint32_t*)&challenge, XTEA_KEY);
                String tokenStr = bytesToHex((uint8_t*)&challenge, 4);

                // 第三步:发送验证请求,切换继电器
                String toggleUrl = "http://" + String(SERVER_IP) + "/toggle-relay?token=" + tokenStr;
                if (http.begin(toggleUrl)) {
                    httpCode = http.GET();
                    if (httpCode == HTTP_CODE_OK) {
                        Serial.println("Relay toggled successfully");
                    } else {
                        Serial.print("Toggle failed: ");
                        Serial.println(httpCode);
                    }
                    http.end();
                } else {
                    Serial.println("Failed to connect to toggle endpoint");
                }
            } else {
                Serial.println("Invalid challenge format");
            }
        } else {
            Serial.print("Failed to get challenge: ");
            Serial.println(httpCode);
        }
    } else {
        Serial.println("Failed to connect to challenge endpoint");
    }
}

额外注意事项

  • 密钥保密:XTEA_KEY必须在两个设备上完全一致,且绝对不能泄露给第三方,否则认证就失效了。
  • 重放攻击防护:设置的CHALLENGE_TIMEOUT很重要,确保每个挑战只能在短时间内使用,避免攻击者截获token后重复使用。
  • 随机数可靠性:ESP8266的random()依赖模拟引脚的噪声初始化,如果你需要更高的随机性,可以考虑用硬件随机数(部分ESP8266型号支持)。
  • 通信安全:如果是在公网环境使用,建议把HTTP换成HTTPS(ESP8266可以用ESP8266WiFiSecure库),防止挑战和token被明文截获。

内容的提问来源于stack exchange,提问作者cdoub

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:59:12