Secret Box、Sealed Box 还是 Box:选择你的加密方法

本文介绍了使用 Zig 语言实现 NaCl Box 加密方法的三种方式:Box、Sealed Box 和 Secret Box。Box 方法使用公钥加密和私钥签名;Secret Box 方法使用共享密钥加密;Sealed Box 方法使用公钥加密,实现匿名发送。文章提供了相应的 Zig 代码示例,并展示了消息加密和解密的过程。

秘密盒子、密封盒子还是普通盒子: 你选择哪种加密方法

NaCl Box 方法提供了许多我们可能需要的加密方法,用于在 Bob 和 Alice 之间加密数据。这包括 Bob 和 Alice 拥有公钥的情况,以及他们可能共享一个密钥的情况。在这种情况下,我们将用 Zig 实现 Box、Sealed Box 和 Secret Box。

假设 Bob 想要送给 Alice 一份礼物,并想把礼物密封在一个盒子里。如果 Alice 把她的挂锁寄给 Bob,他就可以用她的挂锁把盒子锁上。他还可以签一张卡片,放到另一个盒子里,让 Alice 打开。然后,她会用挂锁的钥匙打开礼物,并检查是否是 Bob 送的。这就是 Box 方法

但是,假设 Bob 和 Alice 有一个他们用来锁盒子的密钥。有了这个,Bob 就可以用这个密钥把盒子锁上,Alice 也会用同一个密钥把它打开。这就是 Secret Box 方法

最后,可能是情人节,所以 Bob 想要匿名发送盒子,所以会用 Alice 的挂锁把它锁上,但不在盒子上留下他的签名。这就是 Sealed Box 方法

对于 Secret Box,我们用一个密钥密封盒子,并用一个密钥打开它:

nacl.SecretBox.seal(ciphertext, message, salt, secret_key);
nacl.SecretBox.open(plaintext, ciphertext, salt, secret_key);

使用 Box,我们用 Alice 的公钥加密,并用 Bob 的私钥签名。然后我们用 Alice 的私钥打开盒子,并检查 Bob 是否用他的公钥发送了盒子:


nacl.Box.seal(ciphertext, message, salt, AliceKeyPair.public_key, BobKeyPair.secret_key);
nacl.Box.open(plaintext, ciphertext, salt, BobKeyPair.public_key, AliceKeyPair.secret_key);

使用 Sealed Box,Bob 发送一个匿名盒子,我们使用 Alice 的公钥进行加密,并使用 Alice 的私钥进行解密:

nacl.SealedBox.seal(ciphertext, message, AliceKeyPair.public_key);
nacl.SealedBox.open(plaintext, ciphertext, AliceKeyPair);

NaCl Box

Box 加密方法非常完美,它利用了公钥密码学和对称密钥的力量。有了它,Bob 使用 Alice 的公钥向她发送一条加密消息,然后她使用她的私钥解密它。然后,Bob 可以用他的私钥对消息进行签名,Alice 可以使用他的公钥证明是 Bob 发送的。基本上,它使用了混合加密,其中对称密钥加密消息。密文将额外附加一个 16 字节的标签用于签名。它使用 X25519 作为密钥交换方法来传递加密密钥,Ed25519 用于数字签名,XSala20/Poly1305 用于对称密钥加密(并使用 192 位 nonce):

以下代码使用 Zig Version 0.15.1 编译 [ here]。 使用 Zig,我们可以使用 Box 加密 [ here]:

const std = @import("std");
const nacl = @import("std").crypto.nacl;
const crypto = @import("std").crypto;

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    var stdout_buffer: [4096]u8 = undefined;
    var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
    const stdout = &stdout_writer.interface;
    // 获取命令行参数
    var message: []const u8 = undefined;
    const args = try std.process.argsAlloc(std.heap.page_allocator);
    defer std.process.argsFree(std.heap.page_allocator, args);
    // 检查是否有任何参数
    if (args.len > 1) {
        message = args[1];
    }
    const BobKeyPair = nacl.Box.KeyPair.generate();
    const AliceKeyPair = nacl.Box.KeyPair.generate();

    var salt: [24]u8 = undefined;
    crypto.random.bytes(&salt);

    const ciphertext_size = message.len + @as(usize, nacl.Box.tag_length);
    const ciphertext = try allocator.alloc(u8, ciphertext_size);
    defer allocator.free(ciphertext);

    const plaintext = try allocator.alloc(u8, message.len);
    defer allocator.free(plaintext);

    // 使用 Alice 的公钥加密并使用 Bob 的私钥签名
    try nacl.Box.seal(ciphertext, message, salt, AliceKeyPair.public_key, BobKeyPair.secret_key);

    // 使用 Alice 的私钥解密并使用 Bob 的公钥验证
    try nacl.Box.open(plaintext, ciphertext, salt, BobKeyPair.public_key, AliceKeyPair.secret_key);

    try stdout.print("== Box encryption Message: {s} \n", .{message});
    try stdout.print("== Box encryption Salt: {x} \n", .{salt});
    try stdout.print("\nBob Secret Key: {x} \n", .{BobKeyPair.secret_key});
    try stdout.print("Bob Public Key: {x}\n", .{BobKeyPair.public_key});
    try stdout.print("\nAlice Secret Key: {x} \n", .{AliceKeyPair.secret_key});
    try stdout.print("Alice Public Key: {x}\n", .{AliceKeyPair.public_key});
    try stdout.print("\nCiphertext passed to Alice (using Alice's public key): {x}\n", .{ciphertext});
    try stdout.print("\nPlaintext recovered by Alice (with her private key): {s}\n", .{plaintext});
    try stdout.flush();
}

一个使用消息 “Hello Alice. I’m Bob!” 的示例运行 [ here]:

== Box encryption Message: Hello Alice. I'm Bob!
== Box encryption Salt: 63353c9f0c9263bae43d9cf545502c9bc7f7cc8a9d4a52ec

Bob Secret Key: bd6ef81bd4244bca098918f6d042f95029b0572dd0d2f3f8232bf340df43d974
Bob Public Key: ccc18db42a1f37479e9d53e38acd49d8bd24aed3e22cc60a574752ac8d91e279

Alice Secret Key: 350e678b75efb32dc808292d3483517ed47f42e04a974581cc7a2697a0b88691
Alice Public Key: cdd826741d877e5bc750855f61eb824876141b6ce7305cbbae0d7721d11ccd01

Ciphertext passed to Alice (using Alice's public key): b353ff2b846d573f424e8ae4c10dc64815db4051a48305f773a879c047af2695384cbbfbb3

Plaintext recovered by Alice (with her private key): Hello Alice. I'm Bob!

我们可以看到明文有 21 个字节,而密文有 37 个字节。这意味着签名的标签是 16 个字节(128 位)。

NaCl Secret Box

使用 Secret Box,Bob 和 Alice 共享一个密钥,该密钥用于加密数据,然后解密数据。这使用 XSala20/Poly1305 进行对称密钥加密(并使用 192 位 nonce):

以下代码使用 Zig Version 0.15.1 编译 [ here]。 借助 ZIg,我们可以使用 Box Sealed 加密 [ here]:

const std = @import("std");
const nacl = @import("std").crypto.nacl;
const crypto = @import("std").crypto;

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    var stdout_buffer: [4096]u8 = undefined;
    var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
    const stdout = &stdout_writer.interface;
    // 获取命令行参数
    var message: []const u8 = undefined;
    const args = try std.process.argsAlloc(std.heap.page_allocator);
    defer std.process.argsFree(std.heap.page_allocator, args);
    // 检查是否有任何参数
    if (args.len > 1) {
        message = args[1];
    }
    const AliceKeyPair = nacl.SealedBox.KeyPair.generate();

    var salt: [24]u8 = undefined;
    crypto.random.bytes(&salt);

    const ciphertext_size = message.len + @as(usize, nacl.SealedBox.seal_length);
    const ciphertext = try allocator.alloc(u8, ciphertext_size);
    defer allocator.free(ciphertext);

    const plaintext = try allocator.alloc(u8, message.len);
    defer allocator.free(plaintext);

    try nacl.SealedBox.seal(ciphertext, message, AliceKeyPair.public_key);
    try nacl.SealedBox.open(plaintext, ciphertext, AliceKeyPair);

    try stdout.print("== Box encryption Message: {s} \n", .{message});
    try stdout.print("== Box encryption Salt: {x} \n", .{salt});
    try stdout.print("\nAlice Secret Key: {x} \n", .{AliceKeyPair.secret_key});
    try stdout.print("Alice Public Key: {x}\n", .{AliceKeyPair.public_key});
    try stdout.print("\nCiphertext passed to Alice (using Alice's public key): {x}\n", .{ciphertext});
    try stdout.print("\nPlaintext recovered by Alice (with her private key): {s}\n", .{plaintext});
    try stdout.flush();
}

一个使用消息 “Hello Alice. I’m Bob!” 的示例运行 [ here]:

== Box encryption Message: Hello Alice. I'm Bob!
== Box encryption Salt: 8b4c170d4a31a3afe6eedc1dc2f57091fb50750d81503f64

Alice Secret Key: 65023dae30945d748b9806545790e9238bbea81350d914b5fe41f6efa5f805ae
Alice Public Key: e4aadb85b13d358a90ea47d720bb051b14b4196dca23ad0fac7bbe94329a7a53

Ciphertext passed to Alice (using Alice's public key): 6896321116806e46702bcbb5174b714198916ad577db1afbfddc2cac4c3c5149dd3582409dae6abde150d13e73b9e87e36301753280ee56e55cc4371e1414b6c7a2d72c18a

Plaintext recovered by Alice (with her private key): Hello Alice. I'm Bob!

我们可以看到明文有 21 个字节,而密文有 69 个字节。这意味着标签是 48 个字节。

NaCl Sealed Box

使用 Sealed Box,Bob 不会表明自己的身份,因此会创建一个匿名的加密消息。这使用 X25519 交换对称密钥,并使用 Alice 的公钥。它将使用带有 XSala20/Poly1305 的对称密钥加密来加密数据:

以下代码使用 Zig Version 0.15.1 编译 [ here]。 借助 Zig,我们可以使用 Secret Box 加密:

const std = @import("std");
const nacl = @import("std").crypto.nacl;
const crypto = @import("std").crypto;

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    var stdout_buffer: [4096]u8 = undefined;
    var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
    const stdout = &stdout_writer.interface;
    // 获取命令行参数
    var message: []const u8 = undefined;
    const args = try std.process.argsAlloc(std.heap.page_allocator);
    defer std.process.argsFree(std.heap.page_allocator, args);
    // 检查是否有任何参数
    if (args.len > 1) {
        message = args[1];
    }
    var salt: [24]u8 = undefined;
    crypto.random.bytes(&salt);

    var secret_key: [32]u8 = undefined;
    crypto.random.bytes(&secret_key);

    const ciphertext_size = message.len + @as(usize, nacl.Box.tag_length);
    const ciphertext = try allocator.alloc(u8, ciphertext_size);
    defer allocator.free(ciphertext);

    const plaintext = try allocator.alloc(u8, message.len);
    defer allocator.free(plaintext);

    nacl.SecretBox.seal(ciphertext, message, salt, secret_key);

    try nacl.SecretBox.open(plaintext, ciphertext, salt, secret_key);

    try stdout.print("== Box encryption Message: {s} \n", .{message});
    try stdout.print("== Box encryption Salt: {x} \n", .{salt});
    try stdout.print("\nSecret key: {x} \n", .{secret_key});
    try stdout.print("\nCiphertext passed to Alice (using Alice's public key): {x}\n", .{ciphertext});
    try stdout.print("\nPlaintext recovered by Alice (with her private key): {s}\n", .{plaintext});
    try stdout.flush();
}

一个使用消息 “Hello Alice. I’m Bob!” 的示例运行:

== Box encryption Message: Hello Alice. I'm Bob!
== Box encryption Salt: f0d254c022e86f332b1e04f70185d426b4beb4ebf5942675

Secret key: b064b322e96fcb8767b19fdfaab78ae8d8ee76edee32fdb1e412f1e186d20dc8

Ciphertext passed to Alice (using Alice's public key): 4d039df330ce38c3c788ecbaf43b3dae45b97d40a2568ae54886d61f8e3b5cd9ebc9c40b40

Plaintext recovered by Alice (with her private key): Hello Alice. I'm Bob!

我们可以看到明文有 21 个字节,而密文有 37 个字节。这意味着签名的标签是 16 个字节(128 位)。

结论

去搞一些 Zig 编程吧: https://asecuritysite.com/zig

  • 原文链接: medium.com/asecuritysite...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
asecuritysite
asecuritysite
江湖只有他的大名,没有他的介绍。