Skip to content

OpenSSLの使い方追加 #161

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion dub.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ subPackage "linux"
subPackage "thirdparty/libdparse"
subPackage "thirdparty/json"
subPackage "thirdparty/vibe-d"
subPackage "thirdparty/openssl"

dependency "dateparser" version="~>3.0.4"

buildOptions "coverage"
buildOptions "coverage"
12 changes: 12 additions & 0 deletions thirdparty/openssl/dub.sdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name "openssl_usage"
description "A minimal D application."
authors "SHOO"
copyright "Copyright © 2021, SHOO"
license "public domain"
# WindowsでOpenSSLを使うにはコンパイル済みのバイナリ(DLL+lib)が必要。
# 自前で用意するのは手間なので、openssl-staticで用意されているものを使用する。
# openssl-staticは内部でDeimosのopensslに依存しているので、
# 改めてDeimosのopensslをdubのプロジェクト設定に追加する必要はない。
dependency "openssl-static" version="~>1.0.2+3.0.8"
libs "Advapi32" platform="windows"
libs "User32" platform="windows"
108 changes: 108 additions & 0 deletions thirdparty/openssl/source/openssl_usage/example.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/++
OpenSSLの使用例

OpenSSLはベースがCの暗号化ライブラリです。インターフェースもそのままC言語のAPIを利用します。 $(BR)
[Deimos](https://github.com/D-Programming-Deimos)という公式のC言語のバインディングプロジェクトがあり、OpenSSLもその対象になっています。

OpenSSLのライセンスはバージョン3.0.0未満の場合、OpenSSL LicenseとSSLeay Licenseの両方のライセンス下で公開されています。3.0.0以降の場合はApache License Version 2.0のライセンスです。$(BR)
ここで紹介するDeimos版の対応バージョンは現在3.0.0未満ですので、OpenSSL LicenseとSSLeay Licenseということになります。

## ドキュメント
- 公式サイト: https://www.openssl.org
- APIドキュメント: https://www.openssl.org/docs/man1.1.1/man7/
- リポジトリ(Deimos): https://github.com/D-Programming-Deimos/openssl
- dubパッケージ(Deimos): https://code.dlang.org/packages/openssl

Source: $(LINK_TO_SRC thirdparty/openssl/source/openssl_usage/_example.d)
+/
module openssl_usage.example;


/++
AES-128-CBCによる共通鍵暗号化/復号の例です。

See_Also:
- https://www.openssl.org/docs/man1.1.1/man3/EVP_CIPHER_CTX_new.html
- https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57Pt3r1.pdf P13
- https://www.ipa.go.jp/security/ipg/documents/ipa-cryptrec-gl-3001-3.0.1.pdf P26, P37
+/
unittest
{
import std.exception: enforce, assumeUnique;
import deimos.openssl.evp;

// 暗号化
immutable(ubyte)[] encryptAES128(in ubyte[] src, in ubyte[128/8] key, in ubyte[16] iv)
{
// 暗号化のコンテキスト作成・破棄
auto encctx = EVP_CIPHER_CTX_new().enforce("Cannot cretae OpenSSL cipher context.");
scope (exit)
encctx.EVP_CIPHER_CTX_free();

// 初期化
encctx.EVP_EncryptInit_ex(EVP_aes_128_cbc(), null, key.ptr, iv.ptr)
.enforce("Cannot initialize OpenSSL cipher context.");

// 暗号化されたデータの格納先として、十分な量のバッファを用意。
// 暗号化のロジックによって異なる。
// AES128だったら元のデータよりブロックサイズ分の16バイト大きければ十分格納できる。
ubyte[] encrypted = new ubyte[src.length + 16];

// 暗号化
// ここでは一回で暗号化を行っているが、分割することもできる。
int encryptedLen;
int padLen;
encctx.EVP_EncryptUpdate(encrypted.ptr, &encryptedLen, src.ptr, cast(int)src.length)
.enforce("Cannot encrypt update OpenSSL cipher context.");
// 暗号化完了
encctx.EVP_EncryptFinal_ex(encrypted.ptr + encryptedLen, &padLen)
.enforce("Cannot finalize OpenSSL cipher context.");

return encrypted[0 .. encryptedLen + padLen].assumeUnique();
}

// 復号
immutable(ubyte)[] decryptAES128(in ubyte[] src, in ubyte[128/8] key, in ubyte[16] iv)
{
// 暗号化のコンテキスト作成・破棄
auto decctx = EVP_CIPHER_CTX_new().enforce("Cannot cretae OpenSSL cipher context.");
scope (exit)
decctx.EVP_CIPHER_CTX_free();

// 初期化・終了処理
decctx.EVP_DecryptInit_ex(EVP_aes_128_cbc(), null, key.ptr, iv.ptr)
.enforce("Cannot initialize OpenSSL cipher context.");

// 復号されたデータの格納先として、十分な量のバッファを用意。
// 暗号化のロジックによって異なる。
// AES128だったら元のデータよりブロックサイズ分の16バイト大きければ十分格納できる。
ubyte[] decrypted = new ubyte[src.length + 16];

// 復号
// ここでは一回で復号を行っているが、分割することもできる。
int decryptedLen;
int padLen;
decctx.EVP_DecryptUpdate(decrypted.ptr, &decryptedLen, src.ptr, cast(int)src.length)
.enforce("Cannot encrypt update OpenSSL cipher context.");
// 復号完了
decctx.EVP_DecryptFinal_ex(decrypted.ptr + decryptedLen, &padLen)
.enforce("Cannot finalize OpenSSL cipher context.");

return decrypted[0 .. decryptedLen + padLen].assumeUnique();
}

import std.conv: hexString;
import std.string: representation;
// ここでは以下のデータを暗号化して、復号します。
static immutable ubyte[] sourceData = "あいうえお"c.representation;
// 鍵とIVには以下を使用。
// 鍵は128bit(16バイト), IVはブロックサイズの16バイト
static immutable ubyte[128/8] key = cast(ubyte[128/8])hexString!"9F86D081884C7D659A2FEAA0C55AD015";
static immutable ubyte[16] iv = cast(ubyte[16])hexString!"A3BF4F1B2B0B822CD15D6C15B0F00A08";
// 暗号化
auto encryptedData = encryptAES128(sourceData, key, iv);
// 復号
auto decryptedData = decryptAES128(encryptedData, key, iv);
// 確認
assert(decryptedData == sourceData);
}
Loading