Go言語でアプリケーションを開発しているときに、どうしても既存のCライブラリを使いたくなる場合があります。
Go言語では簡単にC言語を呼び出せる仕組みが提供されています。
詳しくは cgo などを参照してください。
Go言語側からC言語側(ライブラリ)への参照だけでなく、C言語側からGo言語側を参照することもできますが、今回はGo言語側からOpenSSLを呼び出してみます。
やること
- Go言語から OpenSSLのライブラリ内にある SHA256 系の関数を使って ABCDEFG という文字列のダイジェスト値を得る。
補足
- 結果はバイナリなので、Base64した値を表示して確認します(面倒なのでBase64はGoでやります)。
- ちなみに結果は 6akqLtDVNzKsE7AxonsHGBQjHIYzyfQYRMy6iE1IKxY= です。
- コマンドラインでやるとしたら、以下の感じです。
例
$ echo -n 'ABCDEFG' |openssl sha -sha256 -binary |base64
6akqLtDVNzKsE7AxonsHGBQjHIYzyfQYRMy6iE1IKxY=
おためし
とりあえずソース
main.go
package main
// #cgo CFLAGS: -I/usr/local/Cellar/openssl/1.0.2j/include
// #cgo LDFLAGS: -L/usr/local/Cellar/openssl/1.0.2j/lib -lssl -lcrypto
// #include <openssl/sha.h>
import "C"
import "fmt"
import "encoding/base64"
func main() {
ibuf := []byte { 'A', 'B', 'C', 'D', 'E', 'F', 'G' }
obuf := make([]byte, C.SHA256_DIGEST_LENGTH)
ctx := &C.struct_SHA256state_st{}
C.SHA256_Init(ctx)
_ = C.SHA256_Update(ctx, C.CBytes(ibuf), C.size_t(len(ibuf)))
_ = C.SHA256_Final((*C.uchar)(&obuf[0]), ctx)
b64 := base64.StdEncoding.EncodeToString(obuf)
fmt.Println(b64)
}
実行
例
$ go run main.go
6akqLtDVNzKsE7AxonsHGBQjHIYzyfQYRMy6iE1IKxY=
ポイント
import "C" と cgo への命令
例
// #cgo CFLAGS: -I/usr/local/Cellar/openssl/1.0.2j/include
// #cgo LDFLAGS: -L/usr/local/Cellar/openssl/1.0.2j/lib -lssl -lcrypto
// #include <openssl/sha.h>
import "C"
- import "C" の上に怪しげなコメントがありますが、これが cgo というツールに渡されます。ですので、ただのコメントではなく、意味があります。今回はOpenSSLのヘッダファイルとライブラリの場所をそれぞれ CFLAGS, LDFLAGS で指定しています。これらの値は cgo がコンパイラに指定する時に使われます。
-
#include <openssl/sha.h>
からはC言語のソースになります。ちなみに #include だけでなく、普通にCのコードが書けます。ですので構造体や関数の定義がその場でできます。今回は定義されているものを使うだけでしたので、sha.h
の include しかしていません。
呼び出し
例
ctx := &C.struct_SHA256state_st{}
C.SHA256_Init(ctx)
_ = C.SHA256_Update(ctx, C.CBytes(ibuf), C.size_t(len(ibuf)))
_ = C.SHA256_Final((*C.uchar)(&obuf[0]), ctx)
- ヘッダファイル(今回で言うと sha.h )で定義されている構造体や関数は
C.なになに
で使えます。今回の場合、ctx := &C.struct_SHA256state_st{}
で、SHA256state_st
を使用しています。C.struct_構造体名
となっていますが、構造体や共用体、列挙型にはプリフィクスがつくことになっており、(そのままですが)それぞれ以下の通りです。- struct ... C.struct_
- union ... C.union_
- enum ... C.enum_
-
C.SHA256_Init、 C.SHA256_Update 、 C.SHA256_Final はライブラリ内の関数呼び出しをしています。ただし、CとGoで型が合わない場合が多いので、Cパッケージが変換用にいくつか関数を用意してくれています。以下は例です。
- func C.CString(string) *C.char
- func C.CBytes([]byte) unsafe.Pointer
- func C.GoString(*C.char) string
- func C.GoStringN(*C.char, C.int) string
- func C.GoBytes(unsafe.Pointer, C.int) []byte
まとめ
Go言語からのCライブラリを利用するには以下を行います。
- import "C" の上にコメントで必要なライブラリの指定コンパイラオプションを指定する。
- 同じくimport "C"の上にコメントで、必要なCのコード(#include等)を書く。
- 型の変換をなんとかして呼び出す。
- 呼び出しは C.関数名 。
- 構造体は C.struct_ 、他。