2015年1月の読書メーター
2015年1月の読書メーター
読んだ本の数:3冊
読んだページ数:998ページ
ナイス数:7ナイス
「納品」をなくせばうまくいく ソフトウェア業界の“常識"を変えるビジネスモデルの感想
何がうまくいって、何がうまくいかないのか、それはやってみないと分からない。そうなると「納品」を無くし、常に改善し続けるというのは非常に理にかなっている。本来あれば企業内に技術者を置くべき所ではあるが、本にも書かれている通り、いろいろな理由で難しい物がある。このやり方は技術者だけでなく、新しくビジネスを始めたいと思っている人にも助けになると思う。☆×4
読了日:1月24日 著者:倉貫義人
嫌われる勇気―――自己啓発の源流「アドラー」の教えの感想
☆×4
読了日:1月18日 著者:岸見一郎,古賀史健
マネー・ボール〔完全版〕 (ハヤカワ・ノンフィクション文庫)の感想
読もうと思ったきっかけは、WEB+DB PRESS Vol.84に統計分析の特集があり、そこに取り上げられていたから。/それまで常識だと思われていたものを、新しい尺度で測り直して評価するというのは、非常に有効なんだと思う。ただし、その新しい尺度を見つけ出すのは簡単では無いだろうし、またそれまでの常識から懸け離れていればいるほど、すぐには受け入れられない。だからこそチャンスなんだとも思う。 ☆×4
読了日:1月10日 著者:マイケル・ルイス
読書メーター
opensslによる暗号化/復号化
自分用のメモ。
概要
- OpenSSLを使ってファイルの暗号化と復号化を行う
- C++にて実装
- 自前のプログラムとopensslコマンドで双方でファイルの暗号化/復号化が出来るようにする
単純に自前のプログラム内で暗号化/復号化する方法はGoogleで検索すれば見つかったけど、opensslコマンドとの間でもやりとりしたかった。サンプルでは自前のプログラムで暗号化と復号化を一度にやっている。暗号化したファイルがopensslコマンドで復号化出来ることを確認。
ソースコード(OpensslSample.cpp。https://gist.github.com/asakawajunya/bb3098fc49b4507fbe31)
サンプルなので暗号化に使用するパスワードはソースにべた書き。
#include <string.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <openssl/evp.h> #include <openssl/aes.h> #include <openssl/rand.h> #include <cstdlib> #include <ctime> #include <iostream> #include <unistd.h> unsigned char password[128] = "defaultpass"; void encrypt(const char* ifile, const char* ofile) { // salt key unsigned char salt_key[] = "xxxxxxxx"; // salt string unsigned char salt[] = "Salted__xxxxxxxx"; // saltをランダムに生成 for (int i = 0; i < PKCS5_SALT_LEN; i++) { salt[i + PKCS5_SALT_LEN] = salt_key[i] = (int) ((unsigned char) std::rand()); } struct stat ifile_stat; if (0 == stat(ifile, &ifile_stat)) { if (ifile_stat.st_size > 0) { unsigned char *indata = (unsigned char *) malloc( ifile_stat.st_size); unsigned char *outdata = (unsigned char *) malloc( ifile_stat.st_size + 32); unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH]; const EVP_CIPHER* ci = EVP_aes_256_cbc(); EVP_BytesToKey(ci, EVP_md5(), salt_key, password, strlen((const char *) password), 1, key, iv); FILE* i_file = fopen(ifile, "rb"); fread(indata, sizeof(char), ifile_stat.st_size, i_file); int outdata1_len = 0; int outdata2_len = 0; EVP_CIPHER_CTX ctx; EVP_EncryptInit(&ctx, ci, key, iv); EVP_EncryptUpdate(&ctx, outdata, &outdata1_len, indata, ifile_stat.st_size); EVP_EncryptFinal(&ctx, outdata + outdata1_len, &outdata2_len); EVP_CIPHER_CTX_cleanup(&ctx); FILE* o_file = fopen(ofile, "wb"); // salt書き込み fwrite(salt, sizeof(char), strlen((const char *) salt), o_file); // データ書き込み fwrite(outdata, sizeof(char), outdata1_len + outdata2_len, o_file); fclose(i_file); fclose(o_file); free(indata); free(outdata); indata = NULL; outdata = NULL; } } } void decrypt(const char* ifile, const char* ofile) { struct stat ifile_stat; if (0 == stat(ifile, &ifile_stat)) { FILE* i_file = fopen(ifile, "rb"); unsigned char salt_key[PKCS5_SALT_LEN]; unsigned char salt[16]; // salt読み込み fread(salt, sizeof(char), 16, i_file); for (int i = 0; i < PKCS5_SALT_LEN; i++) { salt_key[i] = salt[i + PKCS5_SALT_LEN]; } int outdata1_len = 0; int outdata2_len = 0; unsigned char *indata = (unsigned char *) malloc(ifile_stat.st_size); unsigned char *outdata = (unsigned char *) malloc(ifile_stat.st_size); unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH]; // データ読み込み fread(indata, sizeof(char), ifile_stat.st_size, i_file); const EVP_CIPHER* ci = EVP_aes_256_cbc(); EVP_BytesToKey(ci, EVP_md5(), salt_key, password, strlen((const char *) password), 1, key, iv); EVP_CIPHER_CTX ctx; EVP_DecryptInit(&ctx, ci, key, iv); EVP_DecryptUpdate(&ctx, outdata, &outdata1_len, indata, ifile_stat.st_size); EVP_DecryptFinal(&ctx, outdata + outdata1_len, &outdata2_len); EVP_CIPHER_CTX_cleanup(&ctx); FILE* o_file = fopen(ofile, "wb"); // padding部分を削って書き込む。書き込みデータの一番最後のデータから削る分を特定する。 fwrite(outdata, sizeof(char), outdata1_len - (int) outdata[ifile_stat.st_size - 16 - 1], o_file); fclose(i_file); fclose(o_file); free(indata); free(outdata); indata = NULL; outdata = NULL; } } int main(int argc, char *argv[]) { if (2 != argc) { fprintf(stderr, "Usage: %s original_file", argv[0]); return EXIT_FAILURE; } // 元ファイル std::string original_file = argv[1]; // 暗号化ファイル std::string encrypt_file = std::string(original_file) + ".encrypt"; // 復号化ファイル std::string decrypt_file = std::string(original_file) + ".decrypt"; encrypt(original_file.c_str(), encrypt_file.c_str()); decrypt(encrypt_file.c_str(), decrypt_file.c_str()); return EXIT_SUCCESS; }
ビルド方法
Mac OS 10.9.5だと結構warningが出るが、ひとまずそのまま。OpenSSLではなくCommonCryptoを使わないとダメな模様。Linuxなら平気かも(未確認)
$ c++ OpensslSample.cpp -o OpensslSample -lcrypto
確認
インラインで説明を追記。
# 暗号化対象のファイルを作成 /Users/junya/develop/OpensslSample% echo "HelloWorld" > test.txt # md5確認 /Users/junya/develop/OpensslSample% md5 test.txt MD5 (test.txt) = 6df4d50a41a5d20bc4faad8a6f09aa8f # 暗号化および復号化 /Users/junya/develop/OpensslSample% ./OpensslSample test.txt # OpensslSampleを実行すると、"元ファイル名.decrypt"と"元ファイル名.encrypt"というファイルを出力する /Users/junya/develop/OpensslSample% ls -l total 72 -rwxr-xr-x 1 junya staff 15372 9 22 14:00 OpensslSample -rw-r--r-- 1 junya staff 4193 9 22 13:58 OpensslSample.cpp -rw-r--r-- 1 junya staff 11 9 22 14:00 test.txt -rw-r--r-- 1 junya staff 11 9 22 14:01 test.txt.decrypt -rw-r--r-- 1 junya staff 32 9 22 14:01 test.txt.encrypt # md5確認。元ファイルと復号化したファイルのハッシュ値が一致することを確認 /Users/junya/develop/OpensslSample% md5 test.txt* MD5 (test.txt) = 6df4d50a41a5d20bc4faad8a6f09aa8f MD5 (test.txt.decrypt) = 6df4d50a41a5d20bc4faad8a6f09aa8f MD5 (test.txt.encrypt) = df58fbb119ceea14dd9e109773fc73f9 # opensslコマンドを使って、暗号化ファイルを復号化する。パスワードにはサンプルソースに書かれている物を入力する /Users/junya/develop/OpensslSample% openssl aes-256-cbc -d -in test.txt.encrypt enter aes-256-cbc decryption password: HelloWorld
その他
- opensslコマンドでは、デフォルトではファイルの先頭部分に"Salted__xxxxxxxx"という16バイトのデータを書き込む("xxxxxxxx"部分はランダム。これによってファイルとパスワードが同一であっても、毎回異なる暗号化ファイルが出来上がる。nosaltオプションもあるが、今回は使わないようにした。
- 暗号化すると、ファイルサイズは必ず16の倍数になる。足りない場合はpadding。自前のプログラムで復号化する場合、padding部分を取り除かないと元ファイルと一致しなくなる。書き込みデータの一番最後にはどれだけpaddingされたか書かれているので、その分だけファイルに出力しないようにしている。1バイトpaddingしている場合は"01"x1、2バイトpaddingされている場合は"02"x2、元ファイルが16の倍数であっても必ずpaddingされ、その場合は"10"x16が記述される。
- 暗号化するのに必要なファイルサイズは元ファイル +32バイト("Salted__xxxxxxxx"とpadding分)あれば足りる。"Salted__xxxxxxxx"を除けば16バイト削れる、padding分も元ファイルサイズを16で割ったあまりを使えば正確になる。
Amazon SES SMTP Credentialsをpythonやrubyで作ってみる
http://docs.aws.amazon.com/ses/latest/DeveloperGuide/smtp-credentials.html の下の方に書いてあるJavaのコードをスクリプト言語で書くとどうなるかというのを試してみる。AWS SECRET ACCESS KEYをそのまま書くわけにはいかないので、そこは適当な文字で。
実行したバージョンが分かるようにしておく。バージョン変わるとAPIも変わるかも。
pythonの場合
python 2.7.5 (default, Sep 12 2013, 21:33:34) [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import base64 >>> import hashlib >>> import hmac >>> base64.b64encode(chr(2) + hmac.new('AWS_SECRET_ACCESS_KEY','SendRawEmail',hashlib.sha256).digest()) 'Aiqnxngua+8IzceF3cMNnR+Tdt0Vo0bXw6z8Q+3U2ls7'
rubyの場合
irb(main):001:0> RUBY_VERSION => "2.0.0" irb(main):002:0> require 'openssl' => true irb(main):003:0> require 'base64' => true irb(main):004:0> Base64.encode64(2.chr + OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), 'AWS_SECRET_ACCESS_KEY', 'SendRawEmail')).chop => "Aiqnxngua+8IzceF3cMNnR+Tdt0Vo0bXw6z8Q+3U2ls7"
rubyの方は実行したら後ろに改行コードが付いてしまったので、chopにて除去。
Javaで実行した結果と一致したので合っているっぽい。
vmstatで時刻を表示する
sarコマンドを実行すると、先頭に時刻を表示する。
# sar 1 Linux 3.2.0-4-amd64 (linux) 2014年02月14日 _x86_64_ (8 CPU) 20時17分02秒 CPU %user %nice %system %iowait %steal %idle 20時17分03秒 all 0.00 0.00 0.00 0.00 0.00 100.00 20時17分04秒 all 0.00 0.00 0.00 0.00 0.00 100.00
vmstatでは時刻を表示しない。
# vmstat 1 procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 0 0 0 704064 136996 88860 0 0 0 0 4 4 0 0 100 0 0 0 0 704064 136996 88860 0 0 0 0 57 31 0 0 100 0
vmstatでもsar同様に時刻を表示させたかったので調べていたら、以下のようにすれば良いことが分かった。
# vmstat 1 | awk '{print strftime("%H:%M:%S"), $0; fflush()}' 20:25:32 procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- 20:25:32 r b swpd free buff cache si so bi bo in cs us sy id wa 20:25:32 1 0 0 703740 136996 88864 0 0 0 0 4 4 0 0 100 0 20:25:33 0 0 0 703864 136996 88864 0 0 0 0 55 31 0 0 100 0
キモはfflushでバッファしないようにしていること。これをしておかないと、ファイルにリダイレクトしている時、ctrl+cで停止してもバッファされている分がファイルに出力されなくなる。開始数秒でctrl+cで停止すると、なぜかファイルサイズが0バイトで偉く悩んだ。
0から2147483647までの間の乱数を取得する
bashだと$RANDOMで乱数は取得できるけど、0〜32767までしか取得できない。それ以上の乱数を手っ取り早く取得できないかと思い探している内に以下に落ち着いた。
コード
#!/usr/bin/env bash while :; do random32=$((($RANDOM & 1)<<30 | $RANDOM<<15 | $RANDOM)) echo ${random32} done
これを、random32.shみたいな名前で保存しておく。
実際に使う場合は以下のようになる。無限ループさせているので、延々出力し続ける。
$ ./random32.sh 2062042316 1359327322 286514404 1256198400 1891772729 以下省略・・・
一つだけ取り出した場合は、"./random32.sh | head -n 1"みたいにする。
$ ./random32.sh | head -n 1 695679400
一定の値より小さい乱数を取り出したい場合は以下のようにする。
$ ./random32.sh | xargs -n 1 -I {} expr {} % 65536 42128 52300 58834 22831 13975 52221 21935 62329 以下省略・・・
xargsを使って65536で割ったあまりを出力する。$RANDOMだと上限が32767のため、65536で割っても32767以上の値が取り出せないが、これなら取り出せる。
bash用ロガースクリプト
単純にechoでメッセージ表示するだけでも良かったのだけど、環境変数でログレベルを切り替えて表示出来るように実装してみた。DEBUG、INFO、WARN、ERRORのみ指定出来、それ以外だとINFO扱いにする。
logger.sh
# LOG_LEVELの設定 declare -A _LOG_LEVEL _LOG_LEVEL[DEBUG]=10 _LOG_LEVEL[INFO]=30 _LOG_LEVEL[WARN]=40 _LOG_LEVEL[ERROR]=50 # LOG_LEVELが指定されていた場合はそのログレベルを使用する # LOG_LEVELが指定されていなかった場合はINFOを指定 if [ "${LOG_LEVEL}" != "" ]; then LOG_LEVEL=${_LOG_LEVEL[${LOG_LEVEL}]} fi if [ "${LOG_LEVEL}" = "" ]; then LOG_LEVEL=${_LOG_LEVEL[INFO]} fi function log_impl() { local target=$1;shift; local level=$1;shift; if [ ${_LOG_LEVEL[${level}]} -ge ${target} ]; then printf "$(date +'%Y/%m/%d %T') %-5s $@\n" ${level} fi } function log_debug() { log_impl ${LOG_LEVEL} DEBUG "$@" } function log_info() { log_impl ${LOG_LEVEL} INFO "$@" } function log_warn() { log_impl ${LOG_LEVEL} WARN "$@" } function log_error() { log_impl ${LOG_LEVEL} ERROR "$@" }
log_sample.sh
#!/usr/bin/env bash source logger.sh log_debug "HOGE" log_info "HOGE" log_warn "HOGE" log_error "HOGE"
実行結果
/Users/junya/develop/bash_sample% export LOG_LEVEL=DEBUG;./log_sample.sh 2013/10/09 20:51:27 DEBUG HOGE 2013/10/09 20:51:27 INFO HOGE 2013/10/09 20:51:27 WARN HOGE 2013/10/09 20:51:27 ERROR HOGE /Users/junya/develop/bash_sample% export LOG_LEVEL=DEBUG;./log_sample.sh 2013/10/09 20:54:30 DEBUG HOGE 2013/10/09 20:54:30 INFO HOGE 2013/10/09 20:54:30 WARN HOGE 2013/10/09 20:54:30 ERROR HOGE /Users/junya/develop/bash_sample% export LOG_LEVEL=INFO;./log_sample.sh 2013/10/09 20:54:37 INFO HOGE 2013/10/09 20:54:37 WARN HOGE 2013/10/09 20:54:37 ERROR HOGE /Users/junya/develop/bash_sample% export LOG_LEVEL=WARN;./log_sample.sh 2013/10/09 20:54:42 WARN HOGE 2013/10/09 20:54:42 ERROR HOGE /Users/junya/develop/bash_sample% export LOG_LEVEL=ERROR;./log_sample.sh 2013/10/09 20:54:49 ERROR HOGE /Users/junya/develop/bash_sample%
netcatによるワンライナーWebサーバ
起動方法
/bin/bash -c "while :; do echo -ne 'HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n' | nc -l -p 8080 -w 30; done"
終了方法
psコマンドでpidを確認後、killコマンドで終了させる。
kill -9 プロセスID
終了直後にncコマンドのプロセスが残っていることがあるが、しばらくすると消える。
もう少しスマートに出来ると良いのだが。
whileで無限ループさせているため、ncコマンドだけをkillしようとしてもうまくいかない。すぐに次のncが起動してしまうので。そこでbash -cのとして一枚かぶせている。
確認方法
curlやwgetなどを使ってアクセスすれば動作確認可能。ブラウザでも可能だが、自分のMac環境だとGoogle chromeは接続できなかった。Firefox、safariは平気だった。
curl http://localhost:8080
上記コマンドを投げると、nc側のコンソールには、以下のように出力されている。
GET / HTTP/1.1 User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8y zlib/1.2.5 Host: localhost:8080 Accept: */*