Windows10 IoT CoreとRaspberry Pi 2の概要とセットアップ

はじめに

前回はQiitaとはてなブログの相互運用の試用で、C++でHello Worldのプログラムを書いてみました。今回は、タイトルに上げたWindows 10 IoT CoreとRaspberry Pi 2の概要を説明しつつ、セットアップまでを行いたいと思います。

なお、はじめに伝えておきますが長文失礼です。本当は画面上にHello Worldまで表示させたかったんですけど、セットアップまでで長文になってしまったので分けました。また、同様の内容をすでにプログラミング生放送勉強会 第35回@株式会社DMM.comラボ(恵比寿)で登壇し発表をしています。その時のスライドは下記URLです。

www.slideshare.net

アジェンダ

  • Raspberry Piとは
  • Windows10 IoT Coreとは
  • セットアップ
  • Hello World

Raspberry Piとは

ARMを搭載したシングルボードコンピュータです。

特徴

格安 本体と必要なものをそろえても1万円以下で済みます。 軽量 軽く手のひらに載るサイズです。 Raspberry Piはイギリスのラズベリーパイ財団によって教育向けコンピュータとして開発され、累計500万台(2015年2月18日時点)を売り上げています。

対応OS

Linuxと今回取り上げるWindows 10 IoT Core(Raspberry Pi 2のみ)です。

なお、Raspberry Pi対応Linuxディストリビューションは、主に下記があります。 Raspbian DebianをRaspberry Pi向けにカスタマイズ Pidora Fedoraを同じくカスタマイズ

Linuxを使ってできること

デスクトップパソコンとして利用

Linuxディストリビューションをインストールすることができるため、そのデスクトップ環境を利用することができます。

サーバーとして利用

上記と同じく、サーバーアプリケーションを実行してWebサーバーなどのサービスを提供することができます。

電子工作に利用

プログラムを開発し、Raspberry Pi本体の接点からGPIO(汎用入出力)やI²Cデバイスを使用することができるため電子回路を作り、例えばロボットを制御することもできます。

Windows10 IoT Coreとは

IoTデバイスで動作する無料Windowsです。

Windows10 IoT Editionsの一部

従来の組み込み向けWindowsであるWindows Embeddedがリニューアルし、Windows 10 IoT Coreのほかに下記のエディションがあります。 for industry devices POSやATMなどを想定 for mobile devices 業務用ハンディーターミナルなどを想定

対応デバイス

Windows10 IoT Coreでできること

Universal Windows Platform(UWP)アプリの実行

これは、この後記載のHello World画面表示で説明します。

サーバーアプリケーションの実行

IISは今のところ提供されていませんが、UWPアプリでサーバーを開発することができ、ブラウザ上からGPIOを制御することもできます。

Windows10 IoT Coreではできないこと

デスクトップパソコンとしての利用

シェル(GUI)やブラウザなどのアプリケーションが提供されていないため、デスクトップパソコンとして利用することは難しいです。この理由は、デスクトップアプリケーションをUWPで作ってしまえば可能ですが、難しいですよね。

直接のデバイス設定

キーボードとマウスは利用できますが、シェルが提供されていないため直接設定することができず、リモート接続したPower ShellかSSHかブラウザ画面から設定することになります。

無線LAN接続(2015年8月23日現在)

IoT Coreが提供している無線LANドライバがOfficel Raspberry Pi WiFi dongleのみでこれが国内販売されていないため、有線LAN接続が必要になります。多分ですが、技適通っていないっぽいです。

セットアップ

必要なもの

Raspberry Pi 2本体のほか

  • USB電源(2A出力)
  • microSDカード(8GB以上 Class10以上のもの)
  • ケース
  • モニターとHDMIケーブル
  • LANケーブル

Windows10 IoT Core

  • Windows10 パソコン
  • MicroSDカードリーダー
  • Windows10 IoT Core for Raspberry Pi 2
  • Visual Studio 2015(Community Edition以上)

PCのセットアップ

Windows10(Version 10.0.10240以上)をインストールする

Windows7以上からのアップデートは今のところ無料ですし、アップデートしちゃいましょう。

Visual Studio 2015(Community Edition以上)をインストールする

ProfessionalやEnterpriseでも使用できるみたいです。その他、インストール時にカスタムインストールでTools and Windows SDKを有効にする必要があるそうです。

Windows IoT Core Project Templatesをインストールする

Visual Studioのプロジェクトの新規作成にIoT Core開発のテンプレートを追加します。

Windows10の開発者モード設定を有効にする

設定 ⇒ 更新とセキュリティ ⇒ 開発者向け 更新とセキュリティ.png

Raspberry Piのセットアップ

Windows10 IoT Core toolsをパソコンにインストールする

インストールするとプログラムフォルダにイメージファイルがインストールされているはずです。(C:\Program Files (x86)\Microsoft IoT\FFU\RaspberryPi2)

SDカードにイメージを書き込む

スタートメニューにWindowsIoTImageHelperと入力し、Windows IoT Core Image Helperを起動し、上記のイメージを指定して、[Flash]ボタンをクリックします。 setup.png

Raspberry Pi 2を起動します

以下の手順で起動します 1. SDカード挿入 2. LANケーブル 3. HDMLケーブル 4. USB電源

しばらくすると下記画面が表示されると思います。 iort.png

参考文献

Raspberry Pi - Wikipedia Windows IoT - Setup your PC for Raspberry Pi 2 Windows IoT - Setup your Raspberry Pi 2 鈴木淳也の「Windowsフロントライン」:「Windows 10 IoT」とはどのようなOSなのか? (1/3) - ITmedia PC USER

ためしにMarkdownでHello World!

何となく見つけたプログラミングトピックスがQiitaに投稿されていて、何となく技術ブログっぽいものを再開してみたくなりました。ただ、Qiitaだけだとそのサービスが終了した場合やそれに近い状態になった時に面倒なことになりそうなので、はてなブログと相互運用することにしました。幸い両サイトともMarkdown形式で投稿することができるので、今回はお試しで投稿してみます。

はじめに

Hello Worldと呼ばれるプログラムは、世界一有名なプログラムだそうです。確かに、初心者向けの多くのプログラミング言語の参考書には必ずその記述例が記載されていると思います。そんなHello WorldをためしにC++で書いてみます。あ、方言の指定は特にないです。たぶん、どんなコンパイラでもC++03でもC++14でもコンパイルが通って実行できるはずです。Cfrontはわかりませんが…。

Hello World

#include <iostream>
int main() {
    std::cout << "Hello World!" << std::endl;
}

おわりに

特にないですが、槍が飛んでこないか心配です。C++はこれっきりにします。

参考文献

Hello world - Wikipedia

プログラミング生放送勉強会 第35回@株式会社DMM.comラボ(恵比寿)

f:id:kanaharu:20150830212348p:plain
はじめまして。仲根かなはるちゃん17歳です。男の娘です。
最近全然ブログ書いていなかったけどタイトルの件について書いてみようと思ったので、久しぶりだけどいろいろ書いてみます。あらかじめ伝えておきますが、長文失礼です。

僕の立場について

まず、今回のプロ生での僕の立場を簡潔に書くと、DMM.comラボ側とプロ生側の両運営とその間の調整役と、僕が指名したスピーカーでした。
全然簡潔じゃないですねwこれじゃよくわからないので、下に書きます。

DMM.comラボ側の運営

那珂中の人です。ツチノコエンジニア*1です。

主な僕の作業は、担当部署と自社からのスピーカーとの調整です。

プロ生側の運営

涼宮ハルヒの憂鬱のみくるちゃん的ポジション*2です。

主な僕の作業は、下記です。

  • jz5さんとの調整
  • スピーカーの手配と調整
  • ATND作成
  • おやつ・ソフトドリンクの手配
  • ビアバッシュの手配
  • コスプレ

両者間の調整役

前回自社ではじめて社外勉強会を行い、担当部署が社外勉強会のノウハウがなかったので調整が多めでしたが、今回は比較的楽にできました。

今回行った調整は、下記です

  • 日程・開催時間
  • 開催概要の説明
  • ATNDの確認

僕が指名したスピーカー

自分がやった方が楽なこともあるんですよ!

前回もそうですが、今回もスピーカーへの登壇のお願いは、主に僕が行っています。
ただ、スピーカーをお願いできる知り合いが少ないことや予定が合わないこともあり、今回は僕も登壇することにしました。

ある意味、これ楽なんですよね。というのも、なにせ自分なので調整いらずで必ず登壇できる枠が一枠埋まるから。まあ今回は、ビアバッシュの手配もあったり、パソコンとコスプレだけじゃなくてデバイスの準備もあったりで、他の人に顔色で心配されるほどいっぱいいっぱいになっちゃいましたが…。

ビアバッシュについて

前回の懇親会どうでしたか?

どうも、前回の懇親会がどうもプチぼったくりの店っぽくて評判が悪かったと思います。料理も少なかったり、飲み放題はすべてピッチャー交換制。日本酒もピッチャーで持ってくる有様。これはちょっとなんとかしないとと思い、勉強会会場でビアバッシュしようと思いました。

ネゴシエーション

比較的楽でした。

自社内でもビアバッシュの開催実績があったのでwただ、お金の管理は、そっちでやってねということでした。

予算の概算

難しかったです。

まず、概算を出す理由ですが、これは前日までに日中のお菓子とソフトドリンクの手配のほか当日大量の料理をお願いしないといけないですが、予算が確定した次点では予約の制限時間に間に合わないと思ったからです。実際、ピザ以外は、オードブルもお酒も冷やしてもらわないといけないため、14時がリミットでした。

また、概算の計算方法ですが、考慮しなければならないことが多かったです。これは、まず参加料が未成年なら無料、成人学生なら1000円、それ以外なら2000円と複雑でした。また、当日キャンセルも考慮しなければなりません。具体的な数字は省略しますが、ほぼ概算通りに行けました。

お菓子とソフトドリンクの購入

センスが問われますね。

カクヤスで購入しました。カクヤスは23区ならビール1本から1時間枠で無料配送してくれるそうですし、あらかじめお願いしておけば冷えた状態で配送してくれます。また、店名通り格安なので重宝します。なんか宣伝っぽいw
お酒の通販サイト【なんでも酒やカクヤス】ビール・ワイン・焼酎等を豊富に販売

お菓子を直接選んで購入したかったので実際の店舗に行って選び、ソフトドリンクは品名と本数を指定し、配送を時間帯指定で冷やしてお願いしましたが、どうでしたか?ビアバッシュ中にポテト以外は無くなっていたので、センスが良かったですよね!ちなみに、カクヤスの店舗にはうまい棒が見当たりませんでした…。酒に合うと思うんだけどなあ?これは死活問題なので、コンビニで購入しました。

お酒の手配

恵比寿でちょっと贅沢なビール飲みたいですよね。
ちなみに僕はプレミアムビールは合わないです。一番搾りラバー。

成人1人2本で、エビス48本。その他ビールで、宗教戦争が起こらないように、スーパードライ一番搾り黒ラベルで6本ずつ。角ハイ12本、チューハイ12本を注文しましたが、どうでしたか?残ったら僕が袖机入れて、毎日飲む分だけ持ち帰るからと言ったら残らず無くなったので、良かったですよね!ちなみに僕は、一番搾りを3本、角ハイを1本飲みましたが。www

まとめ

いろいろやりすぎると良くないですね。

どれも自分が決めてやっているので大変でもしようがないですが、他の人に心配させるのはよくないかなっと。でもよかったでしょ?

*1:DMM.comの動画配信サービスで相当トラフィックがあるはずなのに捌き切れていて、技術力はあるはずなのにツチノコみたいに社外勉強会で見かけたことがないので、ツチノコエンジニアと言われています。

*2:いやいや、お前が勝手に毎回コスプレしているだけだろって言われそうだけど、これ以上の表現はないと思います。他にあったら教えてください。

Javaコンパイルまとめ

Javaをコマンドラインでコンパイル・実行したことがない人のまとめ

Eclipse等のIDEでしかJavaしたことない

EclipseしかJavaしたことないと書こうと思ったけど、よくよく考えたらVisual J++とか、NetBeansとかもちょこっと使ったことあったお。でも、コマンドラインでJavaしたことは一度もないwこれ、ちょっと自慢ね(えっ?

そもそも

JenkinsでWarファイルを作成しようとした時、クラスが無いと怒られちゃったお。どうやら、JUnitのjarファイルが見つからず、コンパイルに失敗するみたい。さて、どうしたものか?たぶん、JUnitのjar置いてクラスパスを追加すれば、コンパイルできる、google先生に尋ねちゃおうと思ったんだけどね。そもそも、Javaをコマンドラインでコンパイル・実行したことないんだし、ちょうどいいから勉強しちゃおうと思って、まとめちゃいます。

HelloJava.java

まず、Javaプログラムから。

そもそも、mainメソッドもちゃんと入力して書いたことはなかった気がするお。*1

public class HelloJava {
	public static void main(String[] args) {
		System.out.println("Hello Java!");
	}
}

コンパイル

もちろん、JDKはインストール済み、かつ、パスが通っていることが前提ね。*2

javac HelloJava.java

コンパイルエラーがなければ、同じディレクトリにHelloJava.classが作成されているはず。

実行

java HelloJava

Hello Java!と標準出力されれば、成功っと。

*1:だって、Eclipseで新規クラス作るときに、チェック入れるだけでmainメソッド作ってくれるじゃんw

*2:ちなみに、かなはるのLinux環境へのJDKのインストール方法は、javaディレクトリをホーム直下に切る、そこにJDKを入れて解凍する、解凍したフォルダにcurrentとlatestのシンボリックリンクを貼る、.bash_profileにパスを設定するだけど、どうなんだろう?

ハードディスクのバックアップを取る

ハードディスクのバックアップは、ハードディスクで取るしかないよね。

バックアップが困難な理由

ハードディスクの容量がテラを超えたあたりから、リムーバブルディスクを使ったバックアップが困難になりました。Blu-rayでも2層で50GB、3TBのハードディスクで、単純計算で60枚必要です。毎月60枚焼けますか?現実的ではないですよね?他にもバックアップの方法として、磁気テープやクラウドがありますが、どっちも法人向けって感じで、個人が気安く安価で扱えるものではないと思います。*1

だから、ハードディスクのバックアップは、ハードディスクで取るしかないと思います。

旧自分バックアップポリシー

自分も、これまでは二度と同じデータが手に入らないものは、ハードディスクでバックアップを取っていました。その他、ちょっと苦労すればまた同じデータを取得できそうなデータは、クラッシュしちゃったらそれまでだよねって見切りを付けていました。しかし、この前むふふーなデータ満載の一台クラッシュさせて泣きそうになりました。

だからって訳じゃないけど、ハードディスクのバックアップをしっかり取ることを考えました。

ちなみに、Windowsのユーザプロファイルは、Active Directoryの移動プロファイルで、Windowsサーバにミラーするようにしています。だから、アプリケーションのみインストールするCドライブはバックアップは不要かというと、そうではありません。AppDataのLocalとLocalLowフォルダは、バックアップされないため、Outlookを使っている場合などはバックアップが必要です。この前、SSDでストライピングしていたCドライブがクラッシュして泣きそうになりましたw

ミラーリングは見送りました

一番手っ取り早い方法は、同じ型番のハードディスクを2枚買ってきて、ミラーリング(RAID-1)することです。ミラーリングすると、ハードディスクの容量は1枚分となってしまいますが、書き込み時に両方のハードディスクに書き込まれ、仮に1枚壊れてももう1枚が壊れていなければデータを取り出すことができます。また、読み込み時は2枚のハードディスクから半分ずつ平行して読み込むため、データサイズが多いファイルなどを開く時に、読み込みが最大2倍速かったりします。

ミラーリングの利点

  • 1枚壊れてももう1枚からデータが取り出せること
  • 読み込み速度が最大2倍速くなること

ミラーリングの難点としては、ハードディスクが2枚になるため、場所と電力も2枚分取ること、製造日時が近い同じハードディスクを、同じ条件で同じ動作をずっと続けさせるため、もう1枚も同時に壊れてしまうかもしれないことです。

ミラーリングの難点

  • 2枚分の場所、電力を取ること
  • もう1枚も同時に壊れてしまうかもしれないこと

その他、RAIDに公私で痛い目にあっていることもあって、しばらく使いたくないお!で、ミラーリングは見送りました。

外付けハードディスク?

リムーバブルディスクとして、外付けハードディスクを用いるというのはアリだと思います。バックアップしたいときだけ、USB3.0で接続する。終わったら棚にしまっておくという運用でしょう。この方法を一番に考えていましたが、マシーン以外の場所を取っちゃうんですよね。

内蔵ハードディスク

完全に内蔵させてしまうのは、最近はWindowsがハードディスクの電源を勝手に落とすため、電気も消耗も少ないと思います。でも、必要ないときは、自分で電源切りたいよねということで、こんなホットスワップできるハードディスクラックマウンタを見つけました。

MVK ウルトララックマウンタ  MV-HDD5RAC-BK(ブラック)

このマウンタ。他のマウンタとちょっと違います。横に、電源ボタンが付いています。必要がないときは、電源を落としておくことも、マシーン動作中に安全な取り外しで電源を落とすこともできます。

結局

回りくどい説明になっちゃいましたが、上記マウンタに3TBのハードディスクを購入してバックアップすることにしました。

バックアップ方法としては、既存のハードディスクのルートにxcopyバッチを配置して、手動でバックアップするようにしようと思っています。今は、既存のハードディスクの整理中。これと言ってポリシーがなかったから大変だお。

*1:個人向けで2TB 3000円/年ぐらいなクラウドサービスがあったら教えてください

はてなフォトライフに画像をアップロードするプログラムをC#で実装してみた

自分のコスプレ写真を(ry

前回に引き続き、今回ははてなフォトライフに画像をアップロードするC#ライブラリをまとめます。
http://developer.hatena.ne.jp/ja/documents/fotolife/apis/atom

概要

はてなのユーザIDとパスワードを指定すると、はてなフォトライフに画像をアップロードできるライブラリ。
※前回のWSSE認証のヘッダを生成するライブラリが必要です。http://cpp.kanaharu.com/entry/2013/01/06/132031

HatenaPhotoLifeUploader.cs

using System;
using System.Text;
using System.IO;
using System.Net;

namespace Harunakasoft.Library
{
    public class HatenaPhotoLifeUploader
    {
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="userName">はてなユーザ名</param>
        /// <param name="password">はてなパスワード</param>
        public HatenaPhotoLifeUploader(string userName, string password)
        {
            this.hatenaAccount = new HatenaAccount(userName, password);
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="hatenaAccount">はてなアカウント</param>
        public HatenaPhotoLifeUploader(HatenaAccount hatenaAccount)
        {
            this.hatenaAccount = hatenaAccount;
        }

        /// <summary>
        /// 画像をアップロードする
        /// </summary>
        /// <param name="path">画像パス</param>
        /// <param name="title">画像タイトル</param>
        /// <param name="folder">画像フォルダ</param>
        public void Upload(string path, string title, string folder = "")
        {
            byte[] data = GetImageBinaryData(path);
            Upload(data, title, folder);
        }

        /// <summary>
        /// 画像をアップロードする
        /// </summary>
        /// <param name="data">画像バイナリデータ</param>
        /// <param name="title">画像タイトル</param>
        /// <param name="folder">画像フォルダ</param>
        public void Upload(byte[] data, string title, string folder = "")
        {
            // アップロード用XML作成
            string uploadXML = MakeUpdateXML(data, title, folder);
            byte[] uploadBinary = Encoding.UTF8.GetBytes(uploadXML);

            // POST実行
            logger.Info("画像アップロード[POST]開始");
            logger.Info(" title  =" + title);
            logger.Info(" folder =" + folder);
            HttpWebRequest request;
            Stream stream;
            try
            {
                request = (HttpWebRequest)WebRequest.Create("http://f.hatena.ne.jp/atom/post");
                request.Headers.Add("X-WSSE", hatenaAccount.WSSEHeader);
                request.Method = "POST";
                stream = request.GetRequestStream();
                stream.Write(uploadBinary, 0, uploadBinary.Length);
                stream.Close();
            }
            catch (Exception e)
            {
                logger.Error("画像アップロード[POST]失敗 message=" + e.ToString());
                throw e;
            }
            logger.Info("画像アップロード[POST]終了");

            // レスポンス待ち
            logger.Info("画像レスポンス待ち開始");
            try
            {
                WebResponse response = request.GetResponse();
                Stream resStream = response.GetResponseStream();
                StreamReader reader = new StreamReader(resStream);
                string result = reader.ReadToEnd();
                resStream.Close();
                logger.Debug(result);
                reader.Close();
            }
            catch (Exception e)
            {
                logger.Error("画像レスポンス待ち失敗 message=" + e.ToString());
                throw e;
            }
            logger.Info("画像レスポンス待ち終了");
        }

        /// <summary>
        /// パスのファイルを開きバイナリデータを取得する
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private byte[] GetImageBinaryData(string path)
        {
            if (path == null)
            {
                throw new ArgumentNullException("パスがnull");
            }
            if (!File.Exists(path))
            {
                throw new FileNotFoundException("指定されたパスにファイルが存在しない path=[{0}]", path);
            }

            logger.Info("画像ファイル読み込み開始");
            logger.Info(" path=" + path);
            byte[] data;
            try
            {
                FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
                long size = stream.Length;
                data = new byte[size];
                stream.Read(data, 0, (int)size);
                stream.Dispose();
            }
            catch (Exception e)
            {
                logger.Error("画像ファイル読み込み失敗 message=" + e.ToString());
                throw e;
            }
            logger.Info("画像ファイル読み込み終了 path=" + path);

            return data;
        }

        /// <summary>
        /// アップロード用XML作成
        /// </summary>
        /// <param name="data">画像バイナリデータ</param>
        /// <param name="title">画像タイトル</param>
        /// <param name="folder">画像フォルダ</param>
        /// <returns></returns>
        private string MakeUpdateXML(byte[] data, string title, string folder = "")
        {
            logger.Info("はてなフォトライフアップロード用XML作成開始");
            logger.Debug(" title  =" + title);
            logger.Debug(" folder =" + folder);
            logger.Debug(" datalen=" + data.Length);
            if (data == null)
            {
                throw new ArgumentNullException("データがnull");
            }
            if (data.Length <= 0)
            {
                throw new ArgumentException("データが空");
            }
            if (title == null)
            {
                throw new ArgumentNullException("タイトルがnull");
            }
            if (folder == null)
            {
                throw new ArgumentNullException("フォルダがnull");
            }

            StringBuilder buffer = new StringBuilder();
            buffer.Append("<entry xmlns=\"http://purl.org/atom/ns#\">\n");
            buffer.Append("  <title>" + title + "</title>");
            if (folder != null && folder.Length > 0)
            {
                buffer.Append("  <dc:subject>" + folder + "</dc:subject>\n");
            }
            buffer.Append("  <content mode=\"nonce64\" type=\"image/jpeg\">");
            buffer.Append(Convert.ToBase64String(data));
            buffer.Append("</content>\n");
            buffer.Append("</entry>");

            logger.Info("はてなフォトライフアップロード用XML作成終了");
#if DEBUG
            logger.Debug(buffer.ToString());
#endif
            return buffer.ToString();
        }

        private readonly HatenaAccount hatenaAccount;
        private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    }
}

Uploadメソッドのfolder引数にフォルダ名を指定すると、同名のフォルダがすでにある場合は、そのフォルダに画像が配置。同名のフォルダがない場合は、新規作成した上で画像が配置されます。

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Harunakasoft.Library;

namespace HatenaPhotoLifeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                HatenaPhotoLifeUploader uploader = new HatenaPhotoLifeUploader("name", "password");
                uploader.Upload(@"C:\Users\kanaharu\Desktop\reimu_twitter.jpg", "test");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
        private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    }
}

はてなのWSSE認証をC#で実装してみた

プログラムで撮影した自分のコスプレ写真など、はてなフォトライフにプログラムからアップロードするには、はてなのWeb APIを使用する必要があります。*1 はてなのWeb APIを使用するには、WSSE認証が必要なので、そのC#実装をまとめます。
http://developer.hatena.ne.jp/ja/documents/auth/apis/wsse

概要

はてなのユーザIDとパスワードからWSSE認証のヘッダを生成するライブラリ。*2

WSSE認証とは

詳しくは、上記URIgoogle先生に尋ねてください。

要は、Basic認証は、ユーザ名とパスワードが平文でサーバーに送られてしまうから、脆弱だお。だから、ダイジェスト認証の一つのWSSE認証を使うんだお!ってことでしょ?たぶん。

HatenaAccount.cs

using System;
using System.Security.Cryptography;
using System.Text;

namespace Harunakasoft.Library
{
    public class HatenaAccount
    {
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="userName">ユーザ名</param>
        /// <param name="password">パスワード</param>
        public HatenaAccount(string userName, string password)
        {
#if DEBUG
            logger.Debug("はてなアカウント UserName=" + userName);
            logger.Debug("はてなアカウント Password=" + password);
#endif
            if (userName == null)
            {
                throw new ArgumentNullException("ユーザ名がNULL");
            }
            if (userName.Length == 0)
            {
                throw new ArgumentException("ユーザ名が空");
            }
            if (password == null)
            {
                throw new ArgumentNullException("パスワードがNULL");
            }
            if (password.Length == 0)
            {
                throw new ArgumentException("パスワードが空");
            }

            header = MakeWSSEHeader(userName, password);
            logger.Info("はてなアカウントヘッダ作成完了");
            logger.Info(" header=" + header);
        }

        /// <summary>
        /// WSSEヘッダの作成
        /// </summary>
        /// <param name="userName">ユーザ名</param>
        /// <param name="password">パスワード</param>
        /// <returns>WSSEヘッダ</returns>
        private string MakeWSSEHeader(string userName, string password)
        {
            DateTime created = new DateTime(DateTime.Now.Ticks);
            string nonce = GenNounce(40);
            byte[] nonceBytes = Encoding.ASCII.GetBytes(nonce);
            string nonce64 = Convert.ToBase64String(nonceBytes);
            string digest = GenDigest(password, created, nonce);
            string createdString =  created.ToString("s");
            string header = string.Format(
                @"UsernameToken Username=""{0}"", PasswordDigest=""{1}"", Nonce=""{2}"", Created=""{3}""",
                userName, digest, nonce64, createdString);
            return header;
        }

        /// <summary>
        /// Digestの作成
        /// </summary>
        /// <param name="password">パスワード</param>
        /// <param name="created">作成日時</param>
        /// <param name="nonce64">Nonce</param>
        /// <returns>Digest</returns>
        private string GenDigest(string password, DateTime created, string nonce)
        {
            byte[] digest;
            using (SHA1Managed sha1 = new SHA1Managed())
            {
                string digestText = nonce + created.ToString("s") + password;
                byte[] digestBytes= Encoding.ASCII.GetBytes(digestText);
                digest = sha1.ComputeHash(digestBytes);
            }
            string digest64 = Convert.ToBase64String(digest);
            return digest64;
        }

        /// <summary>
        /// Nonceの作成
        /// </summary>
        /// <param name="length">Nonce長</param>
        /// <returns>Nonce</returns>
        private string GenNounce(int length)
        {
            RNGCryptoServiceProvider rnd = new RNGCryptoServiceProvider();
            byte[] buffer = new byte[length];
            rnd.GetBytes(buffer);
            string nonce64 = Convert.ToBase64String(buffer);
            return nonce64;
        }

        /// <summary>
        /// ヘッダ
        /// </summary>
        public string WSSEHeader { get { return header; } }
        private readonly string header;

        private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    }
}

*1:もちろん、自分のコスプレ写真をアップロードする必要があるは、別の問題ですw

*2:たぶん、はてな以外でも使えると思われる