2014年12月19日金曜日

Windows Vista/7以降のリモートセッション

Windows でのリモートセッションについて、つい最近まで誤解をしていた。

ここでリモートセッションとは、クライアントがリモートデスクトップでサーバにログインしているセッションで、クライアント側にセッションのデスクトップが表示される、ことを指す。

Windows 2000/XP/2003 ではアプリケーションで GetSystemMetrics に SM_REMOTESESSION 引数を与えて呼び出すと、アプリケーションが実行されているセッションがリモートセッションであると判断して構わなかった。同じように Vista以降の OS でも SM_REMOTESESSION でリモートセッションかどうかを判断して構わないと思っていたが、これは誤った認識だった。

Vista以降のWindowsを対象にしたプログラムで SM_REMOTESESSION を使ってリモートセッションであるかどうかを判断するコードを追加したのだが、しばらくはその部分は呼び出されなかった。その後、その部分を利用するようになってから、プログラムが訳の分からない動作をするようになった。原因は、ローカルセッション(コンソールに接続されているセッション)にも関わらず、SM_REMOTESESSION はリモートセッションであると判断していたことだった。WebでSM_REMOTESESSIONを検索すると、色々なページで SM_REMOTESESSIONを使って判断できるようなことが書いてある。以前にも、SM_REMOTESESSIONが返す値がおかしいと疑ったが、Web検索では、問題があるようなことはどこにも見つからなかった。そのため、SM_REMOTESESSIONを使ったコードを残したままだったことが、長い間問題の種になってしまったようだ。

リモートデスクトップ経由でリモートのクライアントに表示されているセッションであるかどうかは、WTSQuerySessionInformation API の WTS_INFO_CLASS引数に WTSClientProtocoleType を指定して呼び出して判断す方法が正しい結果になるようだ。 ユーザ切り替えを標準でサポートし始めた Vista以降では、セッションの情報は、セッション自身に問い合わせるのがベストプラクティスだと思う。



2012年4月26日木曜日

USBデバイスブロックソフト

USBメモリからデータ流出を防ぐために、利用制限を掛けるソフトはいろいろ出ていたが簡単に導入できるようなものが見つからなかった。

何年かぶりで調べてみると、手ごろな価格で、評価が高く使えそうなものが見つかった。
忘備録として 2点 URL を記載してみた。

USB Blocker (Freeware) - NetWrix Corporation

http://www.netwrix.com/usb_blocker_freeware.html


サーバとクライアントエージェントで3000円程度。インドの会社のようだ。

uHook Enterprise
http://dataresolve.com/products/uhook-enterprise/


2011年10月6日木曜日

Mac版Excel 2011 マクロで共有ライブラリ( dylib )関数を利用する

Mac版Excel 2008では VBA が使えなくなったようですが、現時点で最新版のExcel 2011 では利用できます。Windows 版との互換性もありそうです。

マクロの互換性の問題になりそうなのは、Windows版で WIN32 API やDLLに含まれる関数を利用しているケースです。WIN32 API は Mac では利用できないため、代替となる機能を Mac 環境で見つけなければ解決できません。 DLL 関数を利用しているのであれば、DLL を共有ライブラリ dylib に書き換えることで解決できるかもしれません。

Webで dylib をExcelから利用する方法を検索してみましたが、これだというものは見つかりませんでした。同じように dylib を呼び出す方法についての質問で、"マイクロソフトではできると言っている" という部分がありました。そこで、試しに Windows版DLLとまったく同じように呼び出してみたところ、確かに、呼び出すことができました。


dylib 内の エクスポートされている 関数の呼び出し方法:

1. Window版のDLL関数とまったく同じように dylib関数を declare 文で宣言

  Declare Function 関数名 Lib "dylibパス" (引数) As 戻り値型
 
  Declare Function myfunc lib "/usr/local/lib/libsample.dylib" () As Integer


2. 宣言した関数をマクロで利用 ( 宣言された関数 mfunc はマクロで利用できるようになっています )

Excel で利用する前に、C/C++プログラムからdylib 関数 を間違えなく呼び出せることを確認しておく方がいいかもしれません。

2011年3月25日金曜日

USBメモリをプロテクトキー(ドングル)として利用するための SDK



弊社では Matrixプロテクションシステム( http://www.ribig.co.jp/matrix )を販売しています。この製品はプロテクト専用キーとしてハードウェア(キー複製を防ぐ専用IDチップ、キー側での演算処理を行うCPU)からソフトウェアー(キーのファームウェア専用APIや付属のユーティリティ)に至るまで十分な機能を提供します。プロテクト専用キーにとって不要と考えられるストレージメモリ機能は持っていません。メモリ機能を持たせるためにはハブ機能の追加などが必要となり、機能に見合う以上にコストがかかります。

プロテクトキーを検討しているユーザには、プロテクトするソフトウェアをUSBキーの収め、同時にUSBキーでプロテクトすることを考えていることがあります。弊社ではMatrixキーとUSBメモリの併用を薦めていますが、どうしても1本のUSBキーでの実現しなければならない案件もあります。

そのようなユーザのため、USBメモリキーをドングルとして利用するための SDK を提供します。 USB メモリキーはストレージ機能を目的としたもので、ドングルとして利用するために必要なハードウェアー的な強固さはありません。ファイルシステムをマウントするため、ドングルのようにそのまま抜き取ることはできません。取り外し操作が必要です。キー側で演算処理することもできません。また、ソフトウェアー的にもドングルのように専用API経由のアクセスによるセキュリティを確保することもできません。だれでも利用可能なWindowsの機能に依存しなければなりません。ハード、ソフトの両面から専用セキュリティキーと同等の機能を実現するには遠く及びません。SDKはこのようUSBメモリとドングルの相違を十分理解した上でご利用されることを前提としています。

USBメモリには可搬性のあるプログラムを収めるのが一般的です。プログラムを実行するコンピュータに必要なランタイムがインストールされていなければ実行できない、実行環境に制限がある、のでは使い物になりません。このSDKは含まれるソフトだけで完結します。他のソフトに依存しません。

汎用ハードのUSBメモリを利用するため、APIや処理内容をユーザ毎に変更しなければセキュリティを確保できません。そのためAPIなど詳細は公開できません。パッケージとして提供していますのでカスタマイズ費用、保守費用などはかかりません。セキュリティレベルを上げるためSDKには別のパッケージ製品が添付されています。利用するUSBメモリキーが最低50-100本程度なければ専用ドングルと比較してかなり割高になります

マニュアル:   http://www.ribig.co.jp/usbmem/download/manual.pdf
APIマニュアル: http://www.ribig.co.jp/usbmem/download/api_manual.pdf
評価版パッケージ:  http://www.ribig.co.jp/usbmem/download/usbser.zip

製品Webページは近日公開予定です。

2011年1月14日金曜日

Windows 2000/XP と Windows Vista/7 はどこが違うのか?


弊社リビッグでは Windows 2000/XP 対応のUSBキーWindows認証ソフト MxLogon、そして、Vista/2008/7対応の MxLogon4Vista を自社開発してきました。その開発から Windowsの大きな変化が見えてきました。

Windows NT から Windows XP までログオン認証は GINA という方式で
カスタマイズ/拡張できました。Vistaからは GINA と非互換の Credential
Provider (CP) という方式が採用されています。



GINAの実体は WinLogonというプログラムから呼び出される関数を含むDLL です。名前の通りWinLogonがログオンに必要な共通処理を行います。WinLogonが大枠の処理をおこない、ユーザインターフェース表示や資格情報入力を受け付ける部分は GINA 関数が行うという仕組みです。

CPの実体も WinLogonから呼び出される DLL ですが、関数ではなくCOMコンポーネントを実装しています。大枠の処理を行うWinLogonが処理過程で COMインタフェースを呼び出します。ここまでは実装手段以外、GINAと大差はありません。大きく異なるのは CP にはユーザインターフェースを表示する必要はない(表示することはできない)という点です。ユーザインターフェースは WinLogonが表示します。 CP WinLogonにユーザインターフェース(UI)要素の種類や数を知らせて、データとしてUI要素を提供します。受け取ったWinLogon UI を表示します。これが可能なのは、UIの構成はWindows側で決めており、利用可能なUI要素が限定されているためです。GINAのような自由度はありません。

まとめ

-     拡張方法の標準化とコンポーネント機能の限定化
UIを標準化することにより、コンポーネント機能が限定されました。コンポーネントが処理を行える範囲が狭まったためシステム全体の安定性が高まります。

-     オブジェクト指向
OS はオブジェクトの集まりとなり、拡張コンポーネントもオブジェクトとなりました。

GINA DLLWinLogonによってOS起動時にメモリにロードされます。一度読み込まれると、そのままずっとロードされたままです。OS が起動している間、GINAでタイマー処理などをバックグランドで実行することができます。ハードウェアー処理も GINA で行えます。CAD( Ctrl+Alt+Delete ) キーが押されると、メモリー上のGINA内の関数が呼び出されます。 このようにGINAOSの一部として機能するため、開発が非常に面倒です。GINAを変更したら、どんな小さな変更であってもWindowsを再起動しなければなりません。また、GINAに不具合があるとシステム全体に影響します。Windowsが実行中に、GINAのエラーでシステムが停止することもあります。

一方、CPは WinLogonによって必要な時だけロードされ、使い終わったら解放されます。メモリーにずっととどまることはありません。CPはバックグランド処理を行えません。このため、システム全体に影響するような問題を起こすことはありません。開発もかなり楽になります。変更したらDLLを置き換えて、ログオフするだけでテストできます。

まとめ

―拡張コンポーネントの局所化
  システム全体に影響するようなコンポーネントの使い方をしません。コンポーネントのエラーによる不安定化が回避されます。


GINAは多くの機能を実装しなければなりません。例えば、Windows終了時にアップデートが存在するか確認するのはGINAの役目です。Windows標準のGINAの処理と同等の機能を実現するのは至難の業です。そこで、拡張GINA Windows標準GINAの機能を実装するためにGINAチェーンという手法を用います。 すべての GINA は同じ関数を持ちますので、GINA1Aという関数から GINA2Aという関数を呼び出すことが可能です。これが GINAチェーンです。例えば、拡張GINAの関数すべてが何もしないで、Windows標準GINA内の対応関数を呼び出せば、スタブGINA が完成します。GINAチェーンを用いる場合、拡張GINA作成の難しさは Windows標準GINAとどのように連携させるかという点です。独自の処理+連携を実現しなければなりません。しかし、“独自でWindows標準GINAの機能を実現するよりもまし“のため、その手法に頼らなければなりません。

CPは限定された機能だけを実装するだけです。共通処理はすべてWinLogon側がやってくれます。Windows標準CPと同等の機能を拡張CPで実現できます。そのため GINAチェーンのような手法は不要です。また、複数のCPを同時に利用できるような仕組みになっています。複数のCPが利用できるということは、それに関連した新しい機能があるということで、実際に他のCPを無効にしたり、有効にするにはといったGINAでは考慮する必要のなかった機能が必要です。

まとめ

-     整理された共通機能
Windows NTで導入されたログオン拡張方式のGINAにはいろいろな機能が求められていますが、複数のGINA間で共通処理がどれで、本当にGINAだけでやるべき処理はどれなのか、整理されていませんでした。CP方式では共通機能が整理され、CPには本当にCPだけで行うべき処理を行わせるようになっています。これにより不安定化の原因だったり、GINA開発を困難の原因だったGINAチェーンが不要になりました。


GINAOSが起動中ロードされつづけるため、バックグランド処理ができます。CPはバックグランド処理ができません。その部分はWindowsサービス として実装することになりますが、Vista/7ではWindowsサービスの実行のされ方が変わりました。 セッション 0 分離というもので、すべてのサービスがセッション 0 で動きます。 XPまではコンソールにログインした最初のセッション内でサービスが動いていました。ユーザプログラムとサービスが共存しているため、特権レベルで動作するWindowsサービスをユーザプログラムプログラムが狙うことができました。サービスを専用のセッションに分離することでセキュリティが高まります。
XPまでは Windowsサービスがユーザプログラムと同じセッションにあるため、UI を表示することができました(Fast User Switchでユーザ切り替えしたときは例外)Vista/7ではサービスが異なるセッションにあるため、ユーザセッションに UIを表示できません。 XPまでは Windowsサービスがユーザプログラムに入力を渡すことができましたが、Vista/7ではできません(同じセッションのユーザプログラム間のデータ受け渡しにも制限ができました)。セキュリティリスクを軽減するため、他のプログラムを操作することが難しくなっています。

Vista/7は複数のセッションをサポートするようになりました。XPまでは、ログインしたらセッションを他のセッションに切り替える仕組みがありませんでした( XPでは Fast User Switch が追加されましたが OS の基本設計レベルで想定されていたものではないはずです - サービスが異なるセッションで異なる動きをしていました)。 Vista/7では、複数のセッションが同時に存在できます。複数のセッションを利用するために、セッション間の行き来する機能が追加されています。

まとめ

-互換性よりセキュリティ、安定性を追求
 CPGINAと互換性はありません。サービスも大きく変わりました。Vista/7ではいままでのバージョンアップと比較して、おもいきったセキュリティ、安定性を追求した方針がとられているようです。NTからずっと互換性にこだわってきた Windows が、Linuxの安定性に迫るワークステーションに変貌したような気がします。

-     マルチユーザワークステーション
 シングルユーザだったWindowsが本当にマルチユーザワークステーションになりました。

Windows 7Vistaを洗練したもので、ほぼ同じ OS と考えられます。XPと比較するとVista は以上のような違いがあり、よく考えられた OS と弊社では見ていました。XPと比較してVistaは安定しており(不安定になる設計が見直されている)セキュリティも根本的な対応がとられているLinuxに近づいたマルチユーザワークステーションと思いましたが、一般的には、XPとの比較は、起動/ログインして使えるようになるまで時間やアプリケーションプログラムとの相性など表面的な部分での評判が悪く、厳しい評価を受けました。思い切った変更をしたOSであるため、弊社では表面的な部分は許容できると考えていました、一般的には厳しい評価が下されました。7はVistaで指摘された表面的な問題点を改善した Vistaで目指したOSの洗練されたものになっていると考えます。

Windowsの進化という面からみた最新 Windowsは以上のなったと考えます。Linuxとの比較では、取り巻く文化や思想が根本的に違いますので、まった別の視点から観察する必要がありますので、機会があったら投稿したいと思っています。

Windowsについて良い側面ばかりを書いていますが、マイクロソフトとは特別関係はありません。開発側からみた率直な意見だとお考えください。

2010年9月19日日曜日

Java RSA 暗号化 と PHP復号化

Javaで暗号化したデータを PHPスクリプトに渡して復号化するには、
http://blog.local.ch/archive/2007/10/29/openssl-php-to-java.html 
で紹介されている方法と逆をすればできるはずです。


1.ライブラリインストール
必要なライブラリをインストールします。
http://www.bouncycastle.org/
インストール方法
http://www.langedge.jp/blog/index.php?itemid=150


残念ながら、openssl-php-to-java.html に掲載のJavaプログラムを実行するとキャストエラーが発生します。プログラムを変更しなければなりませんが、なかかな解決が難しそうなエラーです。


そこで、プログラムを変更することはしないで、PEM形式の公開鍵、秘密鍵をJavaが直接扱える形式を変換することにします。 形式を変換することで外部ライブラリを使う必要がなくなります。ただし、PHP では公開鍵、秘密鍵はPEM形式である必要がありますので、同じ鍵が複数のフォーマットで存在することになります。


2.JavaによるデータのRC4暗号化と RC4暗号キーの公開鍵暗号化


//データをRC4暗号化する
//  plainKey = RC4暗号鍵
//  plainText = 暗号化するデータ
private static byte[] encryptRC4(byte[] plainKey, byte[] plainText) throws Exception 

SecretKey skeySpec = new SecretKeySpec(plainKey, "RC4");  
Cipher cipher = Cipher.getInstance("RC4");  
cipher.init(Cipher.ENCRYPT_MODE, skeySpec); 
return cipher.doFinal(plainText);
}


// RC4暗号キーを公開鍵で暗号化する
//  plainKey = RC4暗号キー
//  path      = 公開鍵ファイル
private static byte[] encryptRSA(byte[] plainKey, String path) throws Exception

{
File keyFile = new File(path);
byte[] encodedKey = new byte[(int)keyFile.length()];  
FileInputStream fis = new FileInputStream(keyFile); 
fis.read(encodedKey); fis.close();
X509EncodedKeySpec publicKeySpec = 
new X509EncodedKeySpec(encodedKey);
KeyFactory kf = KeyFactory.getInstance("RSA" );
PublicKey pubKey = kf.generatePublic(publicKeySpec);
Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, pubKey);
return rsa.doFinal(plainKey); 
}


byte[] ciphertext = encryptRC4(plainKey.getBytes(), plainText.getBytes());
byte[] cipherKey = encryptRSA(plainKey.getBytes(), path);
 
PHPでデータを公開鍵で暗号化するには以下のようにします。

$public_key = openssl_pkey_get_public(file_get_contents( <PEM形式公開鍵ファイル> ));
openssl_seal($data, $encrypted_data, $env_key, array($public_key));

プレーンデータ $dataが $public_key で RC4暗号化されます。RC4の暗号鍵は自動生成されます。結果として $encrypted_data に暗号化されたデータが、$env_key[0]に RC4暗号鍵が暗号化されたものがセットされます。

つまり、$encrypted_data = ciphertext, $enc_key[0] = cipherKey です。ただし、ciphertext と cipherKeyはバイナリですので、BASE64に変換後、文字列にしてから PHP に渡すことで、PHP側で復号化できます。

String ciphertextBase64String = new String ( Base64.encodeBase64(ciphertext), "UTF-8");

String cipherKeyBase64String = new String( Base64.encodeBase64(cihperKey), "UTF-8");

上記コードのBase64変換は Apache Commons ライブラリの場合です。

3.PHP側での暗号されたデータと鍵の復号化
$private_key = openssl_pkey_get_private(file_get_contents(<PEM形式秘密鍵パス>));

$encrypted_data = base64_decode(<base64化されたciphertext>);
$env_key[0]     = base64_decode(<base64化されたcipherKey >);

// 復号処理
if (!openssl_open($encrypted_data, $decrypted_data, $env_key[0], $private_key))
 {
die(openssl_error_string() . "\n");
 }

// 鍵リソースの解放
openssl_free_key($private_key);

$decrypted_dataが復号化されたプレーンテキストになります。



4.結果
Windows 上のJavaで暗号化したものは PHP で復号化できますが、Linux上のJavaで暗号化したものはPHPで復号エラーが発生します。Javaで復号化する限り問題はまったくありません。

Windows 上でRC4鍵を暗号化すると、毎回異なる暗号データが生成されるがLinux上では毎回同じ暗号データが生成されてしまいます。 WindowsとLinuxでは異なるバージョンの Java を使っているための相違だと考え、両者で同じバージョンを使ってみましたが問題は解決しませんでした。.


5. Linux対応
Linux上の PHPでは復号化処理中に PCKS1パッディングエラーとなっていましたので、それに関連のエラーであることは予想できましたが、対応策が分かりませんでした。Webを検索しているとRSA暗号のパディング指定ができることがわかりました。 


encryptRSA(byte[] plainKey, String path) 関数の1行を以下のように変更します。


変更前 Cipher rsa = Cipher.getInstance("RSA");
変更後 Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");


パディングを指定すると Linuxでも Javaで暗号化したものをPHPで復号化できるようになりました。




6. OpenSSL vs mcrypt
PHPと Javaの連携では PHPの mcrypt を利用する方法があるようです。



レンタルサーバでは OpenSSLはほとんど利用できるようですが、mcryptパッケージは利用できないことがあります。レンタルサーバを使うのであれば、PHPでの暗号化はOpenSSLを前提とした方が可搬性があるように思われます。


Java RSA 暗号化(鍵のフォーマット変換)

JavaではPEM形式を扱えないため DER形式に変換してから使います。秘密鍵は PKCS8フォーマットにします。以下 PEM 形式の変換方法です。

1. 秘密鍵作成 (PEM形式)
openssl genrsa -out private_key.pem

2. 公開鍵作成(PEM形式)
openssl rsa -pubout -in private_key.pem -out public_key.pem

3. 公開鍵のPEMからDER形式へ変換 
openssl rsa -inform pem -outform der –pubin -in public_key.pem  -out public_key.der  
openssl rsa -inform pem -outform der  -in public_key.pem -pubin -out public_key.der

(-pubin オプションの指定場所が Linux/Windows で異なるため注意要)

4. 秘密鍵のPEMからDER形式(PKCS8)へ変換

openssl pkcs8 -topk8 -in private_key.pem -inform pem –nocrypt -out private_key.der -outform der
openssl pkcs8 -topk8 -in private_key.pem -inform pem -out private_key.der -outform der -nocrypt 
(-nocrypt オプションの指定場所が Linux/Windows で異なるため注意要. パスワードを尋ねられたら -nocypt オプションは無効状態)



1.Java公開鍵暗号化
//path -> 公開鍵のパス
File keyFile = new File(path);        
byte[] encodedKey = new byte[(int)keyFile.length()];

FileInputStream in = new FileInputStream(keyFile);
in.read(encodedKey);
in.close();
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedKey);

KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pubKey = kf.generatePublic(publicKeySpec);

Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] cipherText = rsa.doFinal(plainTest);


2.Java秘密鍵復号化
//path -> 秘密鍵のパス
File keyFile = new File(path);       

byte[] encodedKey = new byte[(int)keyFile.length()];
new FileInputStream(keyFile).read(encodedKey);

KeyFactory keyFactory = KeyFactory.getInstance("RSA");

PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedKey);

PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);

Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.DECRYPT_MODE, privateKey);
byte[] plainText = rsa.doFinal(cipherText);