(57)【要約】【課題】伝送中のデータに対する機密性が確保されるためデータの安全性を実現できるとともに、アクセス権限失効に伴うデータ再暗号化効率のよい分散ネットワークストレージシステムにおける暗号化データ格納方法の提供。【解決手段】複数のデータ格納装置のうち、一のデータ格納装置は、共有データが暗号鍵Ｋ１で暗号化されたプライマリデータを格納し、他のデータ格納装置は、共有データが暗号鍵Ｋ１とは異なる暗号鍵Ｋ２で暗号化されたバックアップデータを格納し、複数のユーザのうちの少なくとも１つがアクセス権限を失効した場合に、前記バックアップデータを新たなプライマリデータに、前記プライマリデータを新たなバックアップデータに置き換える。【選択図】図１
2008197998.tif 000002
【特許請求の範囲】
【請求項１】
アクセス権限を有する複数のユーザによって共有される共有データを格納する複数のデータ格納装置と、ユーザが前記データ格納装置にアクセスするためにユーザアクセス認証機能を備えたクライアント計算機とがネットワークを介して接続された分散ネットワークストレージシステムにおける暗号化データ格納方法であって、
前記複数のデータ格納装置のうち、一のデータ格納装置は、前記共有データが暗号鍵Ｋ１で暗号化されたプライマリデータを格納し、他のデータ格納装置は、前記共有データが前記プライマリデータの暗号鍵Ｋ１とは異なる暗号鍵Ｋ２で暗号化されたバックアップデータを格納し、
複数のユーザのうちの少なくとも１つがアクセス権限を失効した場合に、前記バックアップデータを新たなプライマリデータに、前記プライマリデータを新たなバックアップデータに置き換え、前記共有データに対するアクセス権限を有するユーザのみが前記新たなプライマリデータに前記暗号鍵Ｋ２でアクセス可能とすることを特徴とする分散ネットワークストレージシステムにおける暗号化データ格納方法。
【請求項２】
複数のユーザのうちの少なくとも１つがアクセス権限を失効した場合に、前記他のデータ格納装置のプライマリ格納部に前記新たなプライマリデータを移動するとともに、前記一のデータ格納装置は、前記暗号鍵Ｋ１およびＫ２とは異なる暗号鍵Ｋ３で元のプライマリデータを再暗号化して新たなバックアップデータとしてバックアップ部に格納することを特徴とする請求項１に記載の分散ネットワークストレージシステムにおける暗号化データ格納方法。
【請求項３】
複数のユーザのうちの少なくとも１つがアクセス権限を失効した場合に、前記他のデータ格納装置のプライマリ格納部に前記新たなプライマリデータを移動するとともに、前記一の格納装置は、前記暗号鍵Ｋ１およびＫ２とは異なる暗号鍵Ｋ３で元のプライマリデータを再暗号化して新たなバックアップデータとした後、前記一のデータ格納装置および前記他のデータ格納装置とは異なる別のデータ格納装置にネットワークを介して前記新たなバックアップデータを転送し、前記別のデータ格納装置は前記新たなバックアップデータをバックアップ部に格納することを特徴とする請求項１に記載の分散ネットワークストレージシステムにおける暗号化データ格納方法。
【請求項４】
前記暗号化は、前記共有データを固定長ブロックに分割して暗号化することを特徴とする請求項１〜請求項３のいずれか１項に記載の分散ネットワークストレージシステムにおける暗号化データ格納方法。
【請求項５】
前記共有データを暗号化する暗号鍵は、更に各ユーザが保持する固有の秘密鍵によってのみ参照できるように暗号化され、前記暗号化された共有データが格納されたデータ格納装置に格納され、暗号鍵を保有しないユーザが前記共有データにアクセスする場合、まず、前記秘密鍵によってのみ復号化できる暗号鍵がユーザに送信された後、暗号化された共有データが前記ユーザに送信されるようにすることを特徴とする請求項１〜請求項３のいずれか１項に記載の分散ネットワークストレージシステムにおける暗号化データ格納方法。
【請求項６】
前記共有データを暗号化する暗号鍵は、更に各ユーザが保持する固有の秘密鍵によってのみ参照できるように暗号化され、前記暗号化された共有データが格納されたデータ格納装置とは異なるデータ格納装置に格納され、暗号鍵を保有しないユーザが前記共有データにアクセスする場合、まず、前記秘密鍵によってのみ復号化できる暗号鍵がユーザに送信された後、暗号化された共有データが前記ユーザに送信されるようにすることを特徴とする請求項１〜請求項３のいずれか１項に記載の分散ネットワークストレージシステムにおける暗号化データ格納方法。
【請求項７】
前記共有データを暗号化する暗号鍵は、更に各ユーザが保持する固有の秘密鍵によってのみ参照できるように暗号化され、前記暗号化された共有データが格納されたデータ格納装置とはネットワークを介してアクセス可能な前記クライアント計算機に格納され、暗号鍵を保有しないユーザが前記共有データにアクセスする場合、まず、前記秘密鍵によってのみ復号化できる暗号鍵がユーザに送信された後、暗号化された共有データが前記ユーザに送信されるようにすることを特徴とする請求項１〜請求項３のいずれか１項に記載の分散ネットワークストレージシステムにおける暗号化データ格納方法。
【発明の詳細な説明】
【技術分野】
【０００１】
本発明は、分散ネットワークストレージシステムにおける暗号化データ格納方法に関する。
【背景技術】
【０００２】
近年、ストレージ装置のネットワーク化が進んでいる。本発明者らは、これまで，ストレージ装置の演算処理能力を利用し、耐故障化、負荷均衡化、容量分散などの機能を自律的に実行する高機能な分散ネットワークストレージシステムとして、自律ディスクを提案した（非特許文献１）。この自律ディスクは、ネットワークに接続された高機能ディスクノードのクラスタによって構成される。
【０００３】
一方、情報セキュリティが重要視されるようになってきている近年において、このようなネットワークストレージでは伝送中のデータの保護は必須である。情報セキュリティの重要性が大きくなり、ネットワークストレージでは悪意あるユーザによる盗聴から伝送路上のデータを保護することが必須となる。データの保護には主に暗号が用いられる。ここで、伝送中のデータの保護について、機密性の保障に着目する。機密性とは、データがアクセス権のないユーザには見ることができない状態であることが保障されるという性質で、ほとんどの場合暗号を用いて実現されている。
【０００４】
伝送路上のデータを悪意あるユーザの傍受から守るため、伝送中のデータに対する機密性を保障するための方法として、エンクリプト・オン・ワイヤ（encrypt-on-wire）方式とエンクリプト・オン・ディスク（encrypt-on-disk）方式の２つの方法がある。エンクリプト・オン・ワイヤ方式は、セッション毎に新たに生成される暗号鍵を用いてデータを暗号化し、伝送する方式である。一方、エンクリプト・オン・ディスク方式は、予めデータを暗号化した状態でストレージに格納しておき、伝送時は暗号化処理を行わず、そのまま送信する方式である。暗号化されたデータをストレージに格納しておくエンクリプト・オン・ディスク方式は、ストレージ側のデータ送信／受信時に暗号化／復号の処理を行う必要がないため、伝送時に必ず暗号化／復号処理を実行しなければならないエンクリプト・オン・ワイヤ方式よりもデータ転送に関して効率がよい。さらにエンクリプト・オン・ワイヤ方式ではセッション毎に新しい暗号鍵を生成するコストがかかることからも、エンクリプト・オン・ディスク方式のほうが性能面で有利である。しかし、このようなエンクリプト・オン・ディスク方式では、複数のユーザによってデータを共有する環境では、エンクリプト・オン・ディスク方式ではユーザのアクセス権限失効（revocation）に伴ってデータを再暗号化する必要がある。例えば、アクセス権を有する複数のユーザのうち、１人のユーザがアクセス権を失効した場合、データの機密性を確保するためには、新たな鍵でデータを再暗号化して、アクセス権を失効したユーザがアクセスできないようにする必要が生じる。
【０００５】
この再暗号化の方法として、直ちに再暗号化を行うアクティブ・レボケーション（active revocation）と、次のデータ更新時まで再暗号化を遅延するレージー・レボケーション（lazy revocation）とがある。アクティブ・レボケーションは直ちに実行しなければならない再暗号化による性能低下と性能面でのコストが高く、レージー・レボケーションは再暗号化を遅延することによって古い鍵で暗号化された脆弱な状態が残る、という相反する欠点が存在するという問題がある（非特許文献２および非特許文献３）。
【０００６】
すなわち、アクティブ・レボケーションは、アクセス権限失効が発生すると直ちに対象データを新しい暗号鍵で再暗号化を実行する方式である。アクセス権限失効したユーザは アクセス権限失効の発生直後から対象データを復号できなくなるため、後述のレージー・レボケーションと比較して安全であるが、アクセス権を持つユーザでも再暗号化処理が終了するまで対象データにアクセスできなくなるため、性能を低下させる可能性がある。これはアクセス権限失効の対象データが複数に及ぶ場合顕著である。
【０００７】
一方、レージー・レボケーションは，対象データの再暗号化を次の更新時まで遅延する方式である（非特許文献４および非特許文献５参照）。このレージー・レボケーションにおいては、データの更新処理は、暗号化処理を伴うため、アクセス権限失効のための復号および暗号化処理を兼ねることができる。また、頻繁に更新されないデータでは、アクセス権限失効の度に再暗号化を行わなければならないアクティブ・レボケーションと比べて、複数回のアクセス権限失効の再暗号化処理をまとめることができるので、その性能差は大きなものとなる。このレージー・レボケーション方式では、アクセス権限失効の事態が発生した後、未だ更新されていないデータは、アクセス権限失効の発生前と同じで、アクセス権限失効したユーザが保持している恐れのある暗号鍵で暗号化された状態で扱われる。これは、アクセス権限失効したユーザがすでに知っている可能性のある更新前の情報は、アクセス権限失効したユーザに漏洩しても問題ない、という考えに基づくものであるが、アクティブ・レボケーションと比較するとセキュリティ面で劣ることになる。
【非特許文献１】Haruo Yokota. Autonomous Disks for Advanced Database Applica-tions. In Proc. of International Symposium on Database Applications in Non-Traditional Environments (DANTE'99), pp. 441?448, Nov. 1999.
【非特許文献２】Erik Riedel, Mahesh Kallahalla, and Ram Swaminathan. A frame-work for evalating storage system security. In FAST '02: Proc. of the 1st USENIX Conf. , pp. 15?30. USENIX Association, 2002.
【非特許文献３】Paul Stanton. Securing Data in Storage: A Review of Current Re-serch. ArXiv Computer Science e-prints, 2004.
【非特許文献４】Kevin Fu. Group sharing and random access in cryptographic storage file system. Master’s thesis, MIT, 1999.
【非特許文献５】Mahesh Kallahalla, Erik Riedel, Ram Swaminathan, Qian Wang, and Kevin Fu. Plutus: Scalable Secure File Sharing on Untrusted Storage. In FAST '03: Proc. of the 1st USENIX Conf., pp. 29?42. USENIX Association, 2003.
【発明の開示】
【発明が解決しようとする課題】
【０００８】
そこで、本発明の課題は、伝送中のデータに対する機密性が確保されるためアクティブ・レボケーションと同等のデータの安全性を実現できるとともに、アクセス権限失効に伴うデータ再暗号化のための効率のよい分散ネットワークストレージシステムにおける暗号化データ格納方法を提供するものである。
【課題を解決するための手段】
【０００９】
前記課題を解決するため、請求項１に係る発明は、アクセス権限を有する複数のユーザによって共有される共有データを格納する複数のデータ格納装置と、ユーザが前記データ格納装置にアクセスするためにユーザアクセス認証機能を備えたクライアント計算機とがネットワークを介して接続された分散ネットワークストレージシステムにおける暗号化データ格納方法であって、前記複数のデータ格納装置のうち、一のデータ格納装置は、前記共有データが暗号鍵Ｋ１で暗号化されたプライマリデータを格納し、他のデータ格納装置は、前記共有データが前記プライマリデータの暗号鍵Ｋ１とは異なる暗号鍵Ｋ２で暗号化されたバックアップデータを格納し、複数のユーザのうちの少なくとも１つがアクセス権限を失効した場合に、前記バックアップデータを新たなプライマリデータに、前記プライマリデータを新たなバックアップデータに置き換え、前記共有データに対するアクセス権限を有するユーザのみが前記新たなプライマリデータに前記暗号鍵Ｋ２でアクセス可能とすることを特徴とする。
【００１０】
この分散ネットワークストレージシステムにおける暗号化データ格納方法では、複数のユーザのうちの少なくとも１つがアクセス権限を失効した場合に、前記バックアップデータを新たなプライマリデータに、前記プライマリデータを新たなバックアップデータに置き換え、前記共有データに対するアクセス権限を有するユーザのみが前記新たなプライマリデータに前記暗号鍵Ｋ２でアクセス可能とすることによって、アクセス権限失効が発生した場合に、低コストかつ迅速に権限失効に伴う新たなデータの機密化処理を終了させることができる。これにより、アクセス権限を保持しているユーザは、実際に再暗号化処理を実行する前に前記共有データへアクセスできるようになるため、アクティブ・レボケーションよりも待機時間が短くなる。また、アクティブ・レボケーションのように複数のディスクで再暗号化処理を実行している状態がなくなり、性能低下を抑えることができる。
【００１１】
請求項２に係る発明は、前記の分散ネットワークストレージシステムにおける暗号化データ格納方法において、複数のユーザのうちの少なくとも１つがアクセス権限を失効した場合に、前記他のデータ格納装置のプライマリ格納部に前記新たなプライマリデータを移動するとともに、前記一のデータ格納装置は、前記暗号鍵Ｋ１およびＫ２とは異なる暗号鍵Ｋ３で元のプライマリデータを再暗号化して新たなバックアップデータとしてバックアップ部に格納することを特徴とする。
【００１２】
この分散ネットワークストレージシステムにおける暗号化データ格納方法では、複数のユーザのうちの少なくとも１つがアクセス権限を失効した場合に、前記他の格納装置のプライマリ格納部に前記新たなプライマリデータを移動するとともに、前記一の格納装置は、前記暗号鍵Ｋ１およびＫ２とは異なる暗号鍵Ｋ３で元のプライマリデータを再暗号化して新たなバックアップデータとしてバックアップ部に格納することによって、新たなバックアップデータを別のデータ格納装置に転送する必要がなく、低コストかつ迅速に権限失効に伴う新たなデータの機密化処理を終了させることができる。
【００１３】
請求項３に係る発明は、前記の分散ネットワークストレージシステムにおける暗号化データ格納方法において、複数のユーザのうちの少なくとも１つがアクセス権限を失効した場合に、前記他のデータ格納装置のプライマリ格納部に前記新たなプライマリデータを移動するとともに、前記一の格納装置は、前記暗号鍵Ｋ１およびＫ２とは異なる暗号鍵Ｋ３で元のプライマリデータを再暗号化して新たなバックアップデータとした後、前記一のデータ格納装置および前記他のデータ格納装置とは異なる別のデータ格納装置にネットワークを介して前記新たなバックアップデータを転送し、前記別のデータ格納装置は前記新たなバックアップデータをバックアップ部に格納することを特徴とする。
【００１４】
この分散ネットワークストレージシステムにおける暗号化データ格納方法では、複数のユーザのうちの少なくとも１つがアクセス権限を失効した場合に、前記他のデータ格納装置のプライマリ格納部に前記新たなプライマリデータを移動するとともに、前記一の格納装置は、前記暗号鍵Ｋ１およびＫ２とは異なる暗号鍵Ｋ３で元のプライマリデータを再暗号化して新たなバックアップデータとした後、前記一のデータ格納装置および前記他のデータ格納装置とは異なる別のデータ格納装置にネットワークを介して前記新たなバックアップデータを転送し、前記別のデータ格納装置は前記新たなバックアップデータをバックアップ部に格納することによって、各データ格納装置におけるプライマリデータとバックアップデータの配置を崩さずに、低コストかつ迅速に権限失効に伴う新たなデータの機密化処理を終了させることができ、バックアップデータの配置に制約がある場合に有効である。
【００１５】
請求項４に係る発明は、前記の分散ネットワークストレージシステムにおける暗号化データ格納方法において、前記暗号化は、前記共有データを固定長ブロックに分割して暗号化することを特徴とする。
【００１６】
この分散ネットワークストレージシステムにおける暗号化データ格納方法では、ユーザがファイルを更新する際の差分データの単位をこのブロックにすることで、転送および暗号化のコストを削減することができる。
【００１７】
請求項５に係る発明は、前記の分散ネットワークストレージシステムにおける暗号化データ格納方法において、前記共有データを暗号化する暗号鍵は、更に各ユーザが保持する固有の秘密鍵によってのみ参照できるように暗号化され、前記暗号化された共有データが格納されたデータ格納装置に格納され、暗号鍵を保有しないユーザが前記共有データにアクセスする場合、まず、前記秘密鍵によってのみ復号化できる暗号鍵がユーザに送信された後、暗号化された共有データが前記ユーザに送信されるようにすることを特徴とする。
【００１８】
この分散ネットワークストレージシステムにおける暗号化データ格納方法では、共有データを暗号化する暗号鍵が、更に各ユーザが保持する固有の秘密鍵によってのみ参照できるように暗号化され、前記暗号化された共有データが格納されたデータ格納装置に格納され、暗号鍵を保有しないユーザが前記共有データにアクセスする場合、まず、前記秘密鍵によってのみ復号化できる暗号鍵がユーザに送信された後、暗号化された共有データが前記ユーザに送信されるようにすることによって、さらに暗号化された共有データのネットワーク伝送中のデータに対する機密性が確保される。
【００１９】
請求項６に係る発明は、前記の分散ネットワークストレージシステムにおける暗号化データ格納方法において、前記共有データを暗号化する暗号鍵は、更に各ユーザが保持する固有の秘密鍵によってのみ参照できるように暗号化され、前記暗号化された共有データが格納されたデータ格納装置とは異なるデータ格納装置に格納され、暗号鍵を保有しないユーザが前記共有データにアクセスする場合、まず、前記秘密鍵によってのみ復号化できる暗号鍵がユーザに送信された後、暗号化された共有データが前記ユーザに送信されるようにすることを特徴とする。
【００２０】
この分散ネットワークストレージシステムにおける暗号化データ格納方法では、共有データを暗号化する暗号鍵が、更に各ユーザが保持する固有の秘密鍵によってのみ参照できるように暗号化され、前記暗号化された共有データが格納されたデータ格納装置とは異なるデータ格納装置に格納され、暗号鍵を保有しないユーザが前記共有データにアクセスする場合、まず、前記秘密鍵によってのみ復号化できる暗号鍵がユーザに送信された後、暗号化された共有データが前記ユーザに送信されるようにすることによって、さらに暗号化された共有データのネットワーク伝送中のデータに対する機密性が確保される。
【００２１】
請求項７に係る発明は、前記の分散ネットワークストレージシステムにおける暗号化データ格納方法において、前記共有データを暗号化する暗号鍵は、更に各ユーザが保持する固有の秘密鍵によってのみ参照できるように暗号化され、前記暗号化された共有データが格納されたデータ格納装置とはネットワークを介してアクセス可能な前記クライアント計算機に格納され、暗号鍵を保有しないユーザが前記共有データにアクセスする場合、まず、前記秘密鍵によってのみ復号化できる暗号鍵がユーザに送信された後、暗号化された共有データが前記ユーザに送信されるようにすることを特徴とする。
【００２２】
この分散ネットワークストレージシステムにおける暗号化データ格納方法では、共有データを暗号化する暗号鍵は、更に各ユーザが保持する固有の秘密鍵によってのみ参照できるように暗号化され、前記暗号化された共有データが格納されたデータ格納装置とはネットワークを介してアクセス可能な前記クライアント計算機に格納され、暗号鍵を保有しないユーザが前記共有データにアクセスする場合、まず、前記秘密鍵によってのみ復号化できる暗号鍵がユーザに送信された後、暗号化された共有データが前記ユーザに送信されるようにすることによって、さらに暗号化された共有データのネットワーク伝送中のデータに対する機密性が確保される。
【発明の効果】
【００２３】
本発明の分散ネットワークストレージシステムにおける暗号化データ格納方法によれば、アクセス権限失効の発生に伴う再暗号化処理を高効率で行うことができる。そして、アクセス権限の失効が発生したときには、実際に再暗号化処理を行う替わりにバックアップデータをプライマリデータに置き換えることで、迅速かつ低コストなアクセス権限失効時の処理を実現することができる。すなわち、本発明においては、アクセス権限の失効が発生した際には、対象データのバックアップデータを、バックアップデータがあるディスクのプライマリに移動して新たなプライマリデータとし、元のプライマリデータは新たなバックアップとして別の暗号鍵によって再暗号化し、すぐに新たなプライマリデータへのアクセスを受け付けることができる。これによって、低コストかつ迅速に処理を終了させることが可能となる。しかも、実際に再暗号化処理を実行する前に対象ファイルへアクセスできるようになるため、アクティブ・レボケーションによる場合よりも待機時間が短くなり、複数のディスクで再暗号化処理を実行している状態がなくなり、性能低下を抑えることができる。
【発明を実施するための最良の形態】
【００２４】
以下、本発明に係る分散ネットワークストレージシステムにおける暗号化データ格納方法（以下、「本発明の暗号化データ格納方法」という）の実施形態について、適宜図面を参照しながら詳細に説明する。
【００２５】
図１は、本発明の暗号化データ格納方法を適用する分散ネットワークストレージシステム１を示す概念図である。この分散ネットワークストレージシステム１は、ネットワークＮを介して、複数のユーザＣＬ１，ＣＬ２，ＣＬ３・・・と、複数のデータ格納装置（ディスクノード）２Ａ，２Ｂ，２Ｃ・・・とがネットワーク接続されている構成を有する。
【００２６】
複数のユーザＣＬ１，ＣＬ２，ＣＬ３・・・のそれぞれは、並列にネットワークＮに接続され、ネットワークＮを介して、データ格納装置２Ａ，２Ｂ，２Ｃ・・・において、所望のデータ（ファイル）にアクセスするものである。
【００２７】
データ格納装置２Ａ，２Ｂ，２Ｃ・・・は、並列にネットワークＮに接続され、それぞれプライマリ格納部Ａ１，Ｂ１，Ｃ１・・・と、バックアップ格納部Ａ２，Ｂ２，Ｃ２，・・・とを備え、演算処理能力を利用してデータの管理を自律的に行う高機能ディスクノードである。プライマリ格納部Ａ１，Ｂ１，Ｃ１・・・は、ユーザＣＬ１，ＣＬ２，ＣＬ３・・・のそれぞれがアクセス権を有するプライマリデータ（ファイル）が格納された領域であり、バックアップ格納部Ａ２，Ｂ２，Ｃ２，・・・は、前記プライマリデータに対応するバックアップデータが格納されている領域である。この演算処理能力を利用してデータの管理を自律的に行う高機能ディスクノードであるデータ格納装置の具体例として、自律ディスク、Ｌｕｓｔｒｅ（米国Cluster File System．Inc社が開発した分散ストレージシステムを構築するためのソフトウエア：http://www.clusterfs.com/（Cluster File System．Inc社のＷＥＢサイト；http://www.lustre.org/docs/selecting-a-cfs.pdf : Lustreに関するドキュメント）を用いた計算機クラスタによるストレージシステム、Google FileSystem(米国Google社が開発した分散データ格納システムを構築するためのソフトウェア：http://www.google.com/:Google社のWEBサイト;http://www.eecs.harvard.edu/cs261/papers/ghemawat.pdf: Google File Systemに関する文献；Sanjay Ghemawat, Howard Gobioff, Shun-Tak Leung，"The Google File System",SOSP 2003.)を用いたデータ格納システムなどが挙げられる。この高機能ディスクノードの演算能力を利用することによって、ストレージ側で耐故障化、負荷均衡化、容量分散等の機能を自律的に実行し、クライアント（ユーザ）によるストレージ管理の負担を軽減することができる。したがって、アクセス権限失効の発生にともなう再暗号化処理や、新しい暗号鍵の配布などの処理は、極力このデータ格納装置側で行うことが好ましい。
【００２８】
この分散ネットワークストレージシステム１において、バックアップ格納部Ａ２，Ｂ２，Ｃ２，・・・の各バックアップ格納部に格納されているバックアップデータは、他のデータ格納装置のプライマリ格納部に格納されているプライマリデータに対応するバックアップデータである。すなわち、例えば、データ格納装置２Ｂのバックアップ格納部Ｂ２に格納されているバックアップデータ（バックアップ１）は、データ格納装置２Ａのプライマリ格納部Ａ１に格納されているプライマリデータ（プライマリ１）に対応するバックアップデータであり、データ格納装置２Ｃのバックアップ格納部Ｃ２に格納されているバックアップデータ（バックアップ２）は、データ格納装置Ｂ１のプライマリ格納部Ｂ１に格納されているプライマリデータ（プライマリ２）に対応するバックアップデータである。また、データ格納装置２Ａのバックアップ格納部Ａ２には、データ格納装置２Ｎ（図示せず）のプライマリ格納部ＡＮ（図示せず）に格納されたプライマリデータに対応するバックアップデータが格納される。さらに、データ格納装置２Ｃのプライマリ格納部Ｃ１に格納されているプライマリデータ（プライマリ３）は、別のデータ格納装置（図示せず）のバックアップ格納部（図示せず）に格納されている。このように、各バックアップ格納部に、そのバックアップ格納部を有するデータ格納装置とは別のデータ格納装置のプライマリ格納部に格納されているプライマリデータに対応するバックアップデータを格納することによって、データの耐故障性を向上させ、データの堅牢性を確保することができる。
【００２９】
この分散ネットワークストレージシステム１において、１のプライマリデータと、そのプライマリデータに対応するバックアップデータとは、それぞれ異なる暗号鍵で暗号化されている。例えば、分散ネットワークストレージシステム１においては、データ格納装置２Ａのプライマリ格納部Ａ１に格納されているプライマリデータ（プライマリ１）が暗号鍵ＫＡ１で暗号化され、このプライマリデータ（プライマリ１）に対応し、データ格納装置２Ｂのバックアップ格納部Ｂ２に格納されているバックアップデータ（バックアップ１）は、暗号鍵ＫＡ１とは異なる暗号鍵ＫＡ２で暗号化されている。また、データ格納装置２Ｂのプライマリ格納部Ｂ１に格納されているプライマリデータ（プライマリ２）と、データ格納装置２Ｃのバックアップ格納部Ｃ２に格納されているバックアップデータ（バックアップ２）とは、異なる暗号鍵で暗号化されている。さらに、他のデータ格納装置のプライマリ格納部に格納されているプライマリデータと、そのプライマリデータに対応するバックアップデータも、同様に異なる暗号鍵で暗号化されている。
【００３０】
この分散ネットワークストレージシステム１では、通常の状態では、ユーザＣＬ１，Ｃｌ２，ＣＬ３，・・・・・は、それぞれ自身がアクセス権限を有するプライマリデータのみにアクセス可能である。そして、一つの共有データに対してアクセス権限を有するユーザが複数存在する場合に、その複数のユーザのうちの少なくとも１つがアクセス権限を失効したときに、バックアップデータが新たなプライマリデータに、そのバックアップデータに対応するプライマリデータが新たなバックアップデータに置き換えられる。そして、その複数のユーザのうち、アクセス権限を失効したユーザ以外の、アクセス権限を保持しているユーザのみが、バックアップデータからプライマリデータに置き換えられた共有データにアクセス可能となり、アクセス権限を失効したユーザは、その新たなプライマリデータにアクセスが不可能となるようにされる。例えば、ユーザＣＬ１，ＣＬ２，ＣＬ３のみがデータ格納装置２Ａのプライマリ格納部Ａ１に格納されたプライマリデータ（プライマリ１）にアクセスするアクセス権限を有する場合、そのプライマリデータ（プライマリ１）に対応する暗号鍵ＫＡ１によって、通常の状態では、ユーザＣＬ１，ＣＬ２，ＣＬ３はプライマリデータ（プライマリ１）にアクセスすることができる。そして、ユーザＣＬ３が、何等かの事情（例えば、職務担当が変更になった等の事情）が発生して、プライマリデータ（プライマリ１）に対するアクセス権限を失効した場合に、プライマリデータ（プライマリ１：暗号鍵ＫＡ１）をバックアップデータに、バックアップデータ（バックアップ１：暗号鍵ＫＡ２）をプライマリデータに置き換える。そして、元のプライマリデータ（プライマリ１）にアクセス権限を有していたユーザＣＬ１およびＣＬ２は、新たにプライマリデータとなった元のバックアップデータに暗号鍵ＫＡ２によってアクセス可能となる。また、元のプライマリデータ（プライマリ１）へのアクセス権限を失効したユーザＣＬ３は、暗号鍵ＫＡ１によって、新たにプライマリデータとなった元のバックアップデータ（バックアップ１：暗号鍵ＫＡ２）にアクセス不能となる。これによって、アクセス権限失効が発生した場合に、低コストかつ迅速に権限失効に伴う新たなデータの機密化処理を終了させることができる。これにより、アクセス権限を保持しているユーザは、実際に再暗号化処理を実行する前および再暗号化処理を行っている間にも前記共有データへアクセスできるようになるため、アクティブ・レボケーションよりも待機時間が短くなる。また、アクティブ・レボケーションのように複数のディスクで再暗号化処理を実行している状態がなくなり、性能低下を抑えることができる。
【００３１】
この分散ネットワークストレージシステム１におけるプライマリデータとバックアップデータの配置および置き換えの処理方式について、図２（Ａ）および図２（Ｂ）に示す２つの実施形態を説明する。
【００３２】
図２（Ａ）に示す第１の実施形態においては、複数のユーザ（例えば、前記のユーザＣＬ１，ＣＬ２，ＣＬ３，・・・）のうちの少なくとも１つ（例えば、ユーザＣＬ３）がアクセス権限を失効した場合に、データ格納装置（ディスクＡ）のプライマリ格納部Ａ１に格納され、暗号鍵Ｋ１で暗号化されていたプライマリデータ［Ｋ１（Ｆ）：Ｋ１］を、新たな暗号鍵Ｋ３で再暗号化して新たなバックアップデータ［Ｋ１（Ｆ）：Ｋ３］として、同じデータ格納装置（ディスクＡ）のバックアップ格納部Ａ２に移動して格納する。そして、別のデータ格納装置（ディスクＢ）のバックアップ格納部Ｂ２に格納され、前記のプライマリデータ［Ｋ１（Ｆ）：Ｋ１］と同じデータであって、異なる暗号鍵Ｋ２で暗号化されていたバックアップデータ［Ｋ２（Ｆ）：Ｋ２］を、そのデータ格納装置（ディスクＢ）のプライマリ格納部Ｂ１に移動して格納する。これによって、新たなバックアップデータを別のデータ格納装置に転送する必要がなく、低コストかつ迅速に権限失効に伴う新たなデータの機密化処理を終了させることができる。
【００３３】
図２（Ｂ）に示す第２の実施形態においては、複数のユーザ（例えば、前記のユーザＣＬ１，ＣＬ２，ＣＬ３，・・・）のうちの少なくとも１つ（例えば、ユーザＣＬ３）がアクセス権限を失効した場合に、データ格納装置（ディスクＡ）のプライマリ格納部Ａ１に格納され、暗号鍵Ｋ１で暗号化されていたプライマリデータ［Ｋ１（Ｆ）：Ｋ１］を、新たな暗号鍵Ｋ３で再暗号化して［Ｋ１（Ｆ）：Ｋ３］として、別のデータ格納装置（図示せず）にネットワークＮを介して転送し、そのデータ格納装置のバックアップ格納部（図示せず）に格納する。そして、データ格納装置（ディスクＢ）のバックアップ格納部Ｂ２に格納され、前記のプライマリデータ［Ｋ１（Ｆ）：Ｋ１］と同じデータであって、異なる暗号鍵Ｋ２で暗号化されていたバックアップデータ［Ｋ２（Ｆ）：Ｋ２］を、そのデータ格納装置（ディスクＢ）のプライマリ格納部Ｂ１に移動して新たなプライマリデータとして格納する。これによって、各データ格納装置におけるプライマリデータとバックアップデータの配置を崩さずに、低コストかつ迅速に権限失効に伴う新たなデータの機密化処理を終了させることができ、バックアップデータの配置に制約がある場合に有効である。
【００３４】
また、この分散ネットワークストレージシステム１において、プライマリデータおよびバックアップデータの暗号鍵による暗号化方法および暗号鍵の管理方法の具体例について説明する。
この分散ネットワークストレージシステム１において、前記プライマリデータおよびバックアップデータを暗号化するために用いられる暗号鍵（Ｋ１，Ｋ２，Ｋ３・・）は、それぞれロックボックスＬ１，Ｌ２，Ｌ３に格納して管理される。ここで、ロックボックスとは、その使用権を有するユーザのみ、所要の暗号鍵を取り出すことができるように構成されるデータ構造である。
このロックボックスは、クライアント計算機がネットワークを介してアクセス可能な場所に設けられる。それぞれのデータと同じデータ格納装置に設けると性能面での効率がよい。また、データと異なるデータ格納装置や、クライアントでもストレージ装置でもない計算機に設けると、同じデータ格納装置に設けるより安全である。
【００３５】
そして、前記プライマリデータおよびバックアップデータを暗号化するために用いられる暗号鍵（Ｋ１，Ｋ２，Ｋ３・・）の格納およびユーザによる取得は、例えば、下記の（ａ）〜（ｃ）の態様によって行うことができる。
（ａ）前記プライマリデータおよびバックアップデータを暗号化するために用いられる暗号鍵（Ｋ１，Ｋ２，Ｋ３・・）は、更に各ユーザが保持する固有の秘密鍵によってのみ参照できるように暗号化され、前記暗号化された共有データが格納されたデータ格納装置に格納され、暗号鍵を保有しないユーザが前記共有データにアクセスする場合、まず、前記秘密鍵によってのみ復号化できる暗号鍵がユーザに送信された後、暗号化された共有データが前記ユーザに送信されるようにする。
（ｂ）前記プライマリデータおよびバックアップデータを暗号化するために用いられる暗号鍵（Ｋ１，Ｋ２，Ｋ３・・）は、更に各ユーザが保持する固有の秘密鍵によってのみ参照できるように暗号化され、前記暗号化された共有データが格納されたデータ格納装置とは異なるデータ格納装置に格納され、暗号鍵を保有しないユーザが前記共有データにアクセスする場合、まず、前記秘密鍵によってのみ復号化できる暗号鍵がユーザに送信された後、暗号化された共有データが前記ユーザに送信されるようにする。
（ｃ）前記プライマリデータおよびバックアップデータを暗号化するために用いられる暗号鍵（Ｋ１，Ｋ２，Ｋ３・・）は、更に各ユーザが保持する固有の秘密鍵によってのみ参照できるように暗号化され、前記暗号化された共有データが格納されたデータ格納装置とはネットワークを介してアクセス可能な前記クライアント計算機に格納され、暗号鍵を保有しないユーザが前記共有データにアクセスする場合、まず、前記秘密鍵によってのみ復号化できる暗号鍵がユーザに送信された後、暗号化された共有データが前記ユーザに送信されるようにする。
【００３６】
このロックボックスの具体例として、図３に示すキーオブジェクト（key object）構造について説明する。
図３に示すキーオブジェクトは、ファイルを暗号化するための1つの共通鍵Ｋを格納している。キーオブジェクトには、1個または複数個のファイルが関連付けられていて、関連付けられたファイル（データ）は全てその暗号鍵で暗号化されているものとする。
キーオブジェクトのタプルは、そのファイルへのアクセス権を与えられているユーザＩＤ（Ｕｓｅｒ  ＩＤ）、ユーザの公開鍵Ｋｘ＋で暗号化されたファイルの共通鍵（Ｅｎｃｒｙｐｔｅｄ  ｋｅｙ）、ユーザが認可されている読み込みや書き込みなどのアクセス権（Ｐｅｒｍｉｓｓｉｏｎｓ）Ｐｘからなる。このキーオブジェクトに格納された共通鍵を獲得するには、公開鍵と対を成す、ユーザが保持している秘密鍵を用いて復号しなければならないので、指定されたユーザのみが共通鍵を得ることができる。また、万一、キーオブジェクトが改竄されても検出できるよう、編集を行ったクライアントはキーオブジェクトをハッシュ関数に入力した結果（ハッシュ）を、自身の秘密鍵で暗号化したシグナチュア（Signature）を生成しなければならない。もし改竄が行われると、その時点のキーオブジェクトをハッシュ関数に入力して得られるハッシュと、シグナチュアを復号して得られるハッシュが異なるため、検出できる。ファイルにアクセス権限を有するユーザは、このキーオブジェクトを用いることによって暗号鍵の受け渡しを行うことができる。また、安全な暗号鍵保存機能だけでなく、そのまま各クライアントに転送しても問題ない形式で保存してあることから、鍵配送の効率がよいという機能もある。
【００３７】
次に、ロックボックスにおけるキーオブジェクトの具体的構造であるファイルオブジェクト（file object）について説明する。
図４は、ファイルオブジェクトにおけるデータ構造を示す。
このファイルオブジェクトにおいて、各ユーザおよびデータ格納装置（ディスク）は公開鍵と秘密鍵の対(Ｋｘ＋，Ｋｘ?)を持ち、データ（ファイル）は共通鍵Ｋｎで暗号化されている。ここで、データ（ファイル）は、そのまま、あるいは複数個の固定長ブロックに分割して暗号化することが好ましい。これによって、ユーザがファイルを更新する際の差分データの単位をこのブロックにすることで、転送および暗号化のコストを削減することができる。
【００３８】
プライマリデータ，バックアップデータとなるファイルオブジェクトは、共に、ファイル名（Ｆｉｌｅ  ｎａｍｅ）、所有者のＩＤ（Ｏｗｎｅｒ  ＩＤ）、アクセス権限失効時の処理方式（Ｒｅｖｏｃａｔｉｏｎ  ｔｙｐｅ）、暗号化ファイルの位置（Ｐａｔｈ）、複製データのあるディスク（ＲｅｐｌｉｃａＤａｔａ  Ｄｉｓｋ）、ロックボックスの情報（Ｌｏｃｋｂｏｘ）が格納されている。ロックボックスは、ファイルへのアクセス権を持つユーザ用の暗号鍵（ＫＡ＋（Ｋｎ）、ＫＢ＋（Ｋｎ）等）の他に、再暗号化等の処理を行うために、格納されたディスク固有の鍵（Ｋ＋ｄｉｓｋ（Ｋｎ））も保持している。
さらに、本発明においては、バックアップデータのファイルオブジェクトでは、プライマリデータに使用されている暗号鍵を保存している。これはバックアップデータに使用されている暗号鍵はユーザに知られてはならないため、更新データなどはプライマリの暗号鍵で暗号化し、やり取りするのが好ましいからである。
【００３９】
以下、アクティブ・レボケーション方式、図２（Ｂ）に示すデータの配置および置き換えの処理方式、および図２（Ａ）に示すデータの配置および置き換えの処理方式の各方式において、アクセス権限失効が発生した場合のデータの処理および格納方法について、図５〜７を用いて説明する。これらの図５〜７において、縦の矢印は一つのディスク（データ格納装置）における処理を表し、状況に応じて複数スレッドによる並行処理が行われるものとする。実際の処理では、図５〜７に示す以外にファイルオブジェクトの入出力や変更処理も行われるが、入出力の処理時間は充分小さく、また処理も時間がかからないため、図５〜７では省略した。
【００４０】
図５は、アクティブ・レボケーション方式において、アクセス権限失効が発生した場合のデータの処理および格納方法を示す図である。
この方式では、図５に示すとおり、アクセス権限の失効が発生した場合、プライマリデータとバックアップデータは直ちに再暗号化される。そして、ディスクＡでは、新しい共通鍵を生成した後、ファイルの読み出し（read）、復号（dec）、暗号化（enc）、ファイルの書き込み（write）を実行しつつ、別スレッドで鍵、ファイル名およびユーザ名をディスクＢに送信し、バックアップデータについても同様の処理を行う。プライマリデータの処理が終了した時点で、要求元ユーザ（アクセス権限失効処理をストレージシステムに対して発行したユーザ）へ終了通知を送信する。また、再暗号化処理が終了するまでアクセス権限の失効が発生したプライマリデータへのアクセスはブロックされる。すなわち、この方式では、プライマリデータとバックアップデータとは直ちに再暗号化される。そして、ディスクＡで新しい共通鍵を生成後、ファイルの差分データを、保存してあった共通鍵で復号し，バックアップデータの共通鍵で暗号化しなおして適用することになる。
【００４１】
図６は、図２（Ｂ）に示すデータの配置および置き換えの処理方式において、アクセス権限失効が発生した場合のデータの処理および格納方法を示す図である。
図６において、アクセス権限失効が発生したデータ（ファイル）のプライマリデータが格納されているディスク（データ格納装置）をディスクＡ、そのプライマリデータに対応するバックアップデータが格納されているディスク（データ格納装置）をディスクＢ、新たにバックアップデータを格納するディスク（データ格納装置）をディスクＣとする。
【００４２】
図６に示す方式では、プライマリデータとバックアップデータの配置を崩さないように、元のプライマリデータをディスクＡからディスクＣへ転送する。このとき、ファイルの再暗号化は転送前、ロックボックスの新しい暗号鍵での更新は転送後に行う。これは、アクセス権限失効が発生する前の暗号鍵で暗号化された脆弱なデータはネットワークに流すべきではなく、また、その時点でアクセス権限のあるユーザは、次回のアクセス権限失効が発生した時にプライマリデータに対する暗号鍵となる新しい暗号鍵が登録されたロックボックスからまだ知られてはいけない、新しい暗号鍵を取り出すことができるようにするためである。
【００４３】
そして、ディスクＡが要求を受け取ると、その情報をディスクＢへ送信して待機する。バックアップデータが格納されたディスクＢ側では、ファイルオブジェクトからアクセス権限が失効したユーザ用の暗号鍵を削除し、元のバックアップデータを新たなプライマリデータに設定してプライマリ格納部に格納してディスクＡへ終了通知を送信する。この時点で、新しいプライマリデータへのアクセスが可能な状態となるので、要求元ユーザ（アクセス権限失効処理をストレージシステムに対して発行したユーザ）へ終了通知を送信する。また、ここまでの処理が終わる前にディスクＡに対象ファイルへのアクセス要求が来た場合は、処理終了後に新しい位置情報とともに再アクセス要求を返し、ユーザはその情報に従って再アクセスすることができる。その後、ディスクＡは、ディスクＢから共通鍵を受信し、別に生成した新たなバックアップデータ用の共通鍵で下のプライマリデータを再暗号化した後、ディスクＣへその新たなバックアップデータを送信し、ディスクＣのバックアップ格納部に格納させる。
【００４４】
図７は、図２（Ａ）に示すデータの配置および置き換えの処理方式の方式において、アクセス権限失効が発生した場合のデータの処理および格納方法を示す図である。
図７に示す方式では、アクセス権限失効が発生した場合に元のプライマリデータを再暗号化して新たなバックアップデータを生成するまでは、前記図６に示す方式と同じ処理を行う。そして、図２（Ａ）に示すデータの格納方法では、プライマリデータとバックアップデータとの配置に制約がないため、元のプライマリデータを再暗号化して生成した新たなバックアップデータは、そのまま、ディスクＡのバックアップ格納部に格納される。
【実施例】
【００４５】
以下、本発明の実施例および比較例によって、本発明をより具体的に説明するが、本発明は、これらの実施例に限定されるものではない。
【００４６】
ＰＣクラスタ上で動作する、エンクリプト・オン・ディスク方式のファイルサーバ・クライアントプログラムを作成した。このファイルサーバ・クライアントプログラムでは，ｐｕｔ、ｇｅｔ、ｕｐｄａｔｅ、アクセス権付与およびアクセス権限失効の５つのコマンドを実行できる。これらのコマンドについては、後記するとおりである。
【００４７】
また、試験に使用した各ディスクにおけるデータ構造は、図２（Ａ）および図２（Ｂ）に示したものとした。すなわち、各ユーザおよびディスクは公開鍵、秘密鍵の対（Ｋｘ＋，Ｋｘ ?)を持ち、ファイルは共通鍵Ｋｎで暗号化されている。ここで、ファイルはそのまま、あるいは複数個の固定長ブロックに分割して暗号化する。また、ファイルオブジェクトは、ファイル名、所有者のＩＤ、アクセス権限失効時の処理方式、暗号化ファイルの位置、複製データのあるディスク、ロックボックスの情報を持つ。このロックボックスはファイルへのアクセス権を持つユーザ用の暗号鍵の他に、再暗号化等の処理を行うために、格納されたディスク固有の鍵も保存している。さらに、バックアップデータのファイルオブジェクトは、プライマリデータに使用されている暗号鍵を保存している。
【００４８】
実験は、表１に示す構成のＰＣクラスタ上で行った。このとき、各ディスクに保存されるデータのサイズは一定で、ファイル数はその総データサイズに従う。また受信ファイルは、アクセス権限失効対象ファイルへのアクセスが重なりやすいよう、アクセスされるファイルの選択に偏りを持たせるために、パラメタθによって決まるＺｉｐｆ分布に従って選ばれるものとした。そして、固定のパラメタとして表２に示す公開鍵、共通鍵、暗号化モード、パディング、総プライマリデータサイズ、Ｚｉｐｆ母数θを使用した。また，ファイルを分割して暗号化した場合における更新処理を実行する状況を作るために，暗号化モードとして共通鍵固定のブロックサイズ毎に独立して暗号化を行うＥＣＢ（Electronic CodeBook）を用いた。
【００４９】
【表１】
2008197998.tif 000003
【００５０】
【表２】
2008197998.tif 000004
【００５１】
予備実験
ファイル受信時における、エンクリプト・オン・ディスク方式のエンクリプト・オン・ワイヤ方式に対する優位性を確認するため、下記の内容で、エンクリプト・オン・ディスク方式のファイルサーバクライアントプログラム（１）と、暗号を用いない方式のクライアント・サーバプログラム（２）と、エンクリプト・オン・ワイヤ方式のクライアント・サーバプログラム（３）とをそれぞれ作成し、比較を行った。エンクリプト・オン・ワイヤ方式プログラムは、予めサーバとユーザが共通の鍵を所持している場合はそれを使用し、所持していない場合はサーバが新たな鍵を生成し、ファイルと共にユーザへ送信する。
【００５２】
エンクリプト・オン・ディスク方式のファイルサーバクライアントプログラム（１）：
エンクリプト・オン・ディスク方式のファイルサーバクライアントプログラム（１）は、下記のクライアントプログラムおよびサーバプログラムからなり、ユーザ側で以下の（ａ）のコマンドをネットワーク上の（あるいはローカルの）サーバ（ディスク）に送信すると、コマンドに従ってサーバが処理を行い、結果をユーザへ返すプログラムとした。
【００５３】
エンクリプト・オン・ディスク方式のサーバプログラム：
package encrypt_on_disk_server;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.security.*;
import java.util.*;
import javax.crypto.*;
/**
* サーバ側メインクラス
* @author takayama
*
*/
public class EncryptOnDiskServer {
private Properties prop = new Properties();
private int port;
private String pubAlg = "";
private int pubLen;
private List<String> connectList = null;
private PrivateKey serverPrikey = null;
private PublicKey serverPubkey = null;
private HashMap<String, PublicKey> pubkeyTable = new HashMap<String, PublicKey>();
private Map<String, String> fileStateTable = Collections.synchronizedMap(new HashMap<String, String>());
private Map<String, String> backupFileStateTable  = Collections.synchronizedMap(new HashMap<String, String>());
//コンストラクタ
public EncryptOnDiskServer(){
try {
FileInputStream input = new FileInputStream("/exp/takayama/EncOnDiskServer.properties");
prop.load(input);
port = Integer.parseInt(prop.getProperty("port"));
pubAlg = prop.getProperty("pubAlg");
pubLen = Integer.parseInt(prop.getProperty("pubLen"));
connectList = Arrays.asList(prop.getProperty("connectList").split(":"));
KeyPairGenerator kpg = KeyPairGenerator.getInstance(pubAlg);
kpg.initialize(pubLen);
KeyPair keys = kpg.generateKeyPair();
serverPrikey = keys.getPrivate();
serverPubkey = keys.getPublic();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
new EncryptOnDiskServer().run();
}
private void run() {
ServerSocketChannel serverChannel = null;
try {
serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress(port));
System.out.println("Encrypt on disk serverが起動しました(" + serverChannel.socket().getLocalSocketAddress() + ")");
//他サーバへの書き込み専用のソケットを生成
/*
Hashtable<InetAddress, String> unconnectedList = connectServer();
System.out.println(unconnectedList.values().toString() + ": Not connect yet.");
*/
//サーバのアドレスリスト作成
Iterator<String> it = connectList.iterator();
Vector<InetAddress> addressList = new Vector<InetAddress>();
while(it.hasNext()){
addressList.add(InetAddress.getByName(it.next()));
}
while(true){
SocketChannel channel = serverChannel.accept();
SocketAddress remoteAddress = channel.socket().getRemoteSocketAddress();
System.out.println(remoteAddress + ":[接続されました]");
InetAddress address = channel.socket().getInetAddress();
if(addressList.contains(address)){
//サーバ間待受けスレッド生成
new Thread(new ServerConnectThread(channel, prop, serverPubkey, serverPrikey, pubkeyTable, fileStateTable, backupFileStateTable)).start();
System.out.println("ServerConnectThread(" + address + "): start");
}else{
//対クライアント待受けスレッド生成
new Thread(new EncryptOnDiskThread(channel, prop, serverPubkey, serverPrikey, pubkeyTable, fileStateTable, backupFileStateTable)).start();
System.out.println("EncryptOnDiskThread(" + address + "): start");
}
}
} catch (IOException e) {
e.printStackTrace();
}finally{
if(serverChannel != null && serverChannel.isOpen()){
try{
System.out.println("Encrypt on disk serverを停止します．");
serverChannel.close();
}catch(IOException e){}
}
}
}
}
/**
* 接続毎に生成され，リクエストを受けて実行するスレッドクラス
* @author takayama
*
*/
public class EncryptOnDiskThread implements Runnable {
private Properties prop = null;
private String pubAlg = "";
private SocketChannel channel = null;
private String userName = "";
private PrivateKey serverPrikey = null;
private PublicKey serverPubkey = null;
private HashMap<String, PublicKey> pubkeyTable = null;
private Map<String, String> fileStateTable;
private Map<String, String> backupFileStateTable;
public EncryptOnDiskThread(SocketChannel channel, Properties prop, PublicKey serverPubkey, PrivateKey serverPrikey, HashMap<String, PublicKey> pubkeyTable, Map<String, String> fileStateTable, Map<String, String> backupFileStateTable) {
this.channel = channel;
this.prop = prop;
this.serverPubkey = serverPubkey;
this.serverPrikey = serverPrikey;
this.pubkeyTable = pubkeyTable;
this.fileStateTable = fileStateTable;
this.backupFileStateTable = backupFileStateTable;
pubAlg = prop.getProperty("pubAlg");
}
public void run() {
try{
//ユーザ名の受信
Charset charset = Charset.forName("UTF-8");
ByteBuffer buf = CommandHandler.receiveShortData(channel, true);
userName = charset.decode(buf).toString();
System.out.println("user: " + userName);
//コマンドの受信
buf = ByteBuffer.allocate(256);
CommandHandler handler = null;
if(channel.read(buf) < 0){
return;
}
buf.flip();
String cmd = charset.decode(buf).toString();
//ackを返す
channel.write(charset.encode("ack"));
System.out.println(cmd);
if(cmd.startsWith("e")){
}else if (cmd.equals("put")){
handler = new PutCommandHandler(channel, prop, userName, pubkeyTable, serverPrikey, fileStateTable);
}else if (cmd.equals("setpubkey")){
setPubkey();
return;
}else if (cmd.equals("get")){
handler = new GetCommandHandler(channel, prop, userName, fileStateTable);
}else if (cmd.equals("auth")){
handler = new AuthCommandHandler(channel, prop, userName, serverPrikey, pubkeyTable, fileStateTable);
}else if (cmd.equals("revoke")) {
handler = new RevokeCommandHandler(channel, prop, userName, serverPubkey, serverPrikey, pubkeyTable, fileStateTable, backupFileStateTable);
}else if (cmd.equals("update2")){
handler = new UpdateCommandHandler2(channel, prop, userName, serverPrikey, serverPubkey, pubkeyTable, fileStateTable);
}else if (cmd.equals("update")){
handler = new UpdateCommandHandler(channel, prop, userName, serverPrikey, serverPubkey, pubkeyTable, fileStateTable);
}else if (cmd.equals("update3")){
handler = new UpdateCommandHandler3(channel, prop, userName, serverPrikey, serverPubkey, pubkeyTable, fileStateTable);
}
handler.execute();
} catch (IOException e){
e.printStackTrace();
}finally{
SocketAddress remoteAddress = channel.socket().getRemoteSocketAddress();
System.out.println(remoteAddress + ":[切断しました]");
if(channel != null && channel.isOpen()){
try {
channel.close();
} catch (IOException e) {}
}
}
}
/**
* クライアントからpubkeyTable（＝サーバの公開鍵リスト）を受信するメソッド
*
*/
private void setPubkey() {
try {
//ユーザ公開鍵の受信
ByteBuffer buf = CommandHandler.receiveShortData(channel, false);
byte[] encKey = buf.array();
X509EncodedKeySpec eks = new X509EncodedKeySpec(encKey);
PublicKey pubkey = KeyFactory.getInstance(pubAlg).generatePublic(eks);
pubkeyTable.put(userName, pubkey);
//サーバ公開鍵の送信
encKey = serverPubkey.getEncoded();
buf = ByteBuffer.wrap(encKey);
CommandHandler.sendShortData(channel, buf, false);
//pubkeyTable（含他サーバ公開鍵）の受信，マージ
buf = CommandHandler.receiveData(channel, true);
byte[] data  = buf.array();
ByteArrayInputStream bin = new ByteArrayInputStream(data);
ObjectInputStream in = new ObjectInputStream(bin);
HashMap<String, PublicKey> map = (HashMap<String, PublicKey>)in.readObject();
System.out.println(map);
pubkeyTable.putAll(map);
in.close();
bin.close();
System.out.println(pubkeyTable);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
/**
* サーバ間通信を行うスレッドクラス
* @author takayama
*
*/
public class ServerConnectThread implements Runnable {
private SocketChannel channel = null;
private Properties prop = null;
private PublicKey serverPubkey = null;
private PrivateKey serverPrikey = null;
private HashMap<String, PublicKey> pubkeyTable = null;
private Map<String, String> fileStateTable = null;
private Map<String, String> backupFileStateTable;
public ServerConnectThread(SocketChannel channel, Properties prop, PublicKey serverPubkey, PrivateKey serverPrikey, HashMap<String, PublicKey> pubkeyTable, Map<String, String> fileStateTable, Map<String, String> backupFileStateTable) {
this.channel = channel;
this.prop = prop;
this.serverPubkey = serverPubkey;
this.serverPrikey = serverPrikey;
this.pubkeyTable = pubkeyTable;
this.fileStateTable = fileStateTable;
this.backupFileStateTable = backupFileStateTable;
}
public void run() {
try {
Charset charset = Charset.forName("UTF-8");
ByteBuffer buf = ByteBuffer.allocate(256);
//要求を読む
if(channel.read(buf) < 0){
return;
}
buf.flip();
String cmd = charset.decode(buf).toString();
System.out.println(cmd);
//ackを返す
channel.write(charset.encode("ack"));
if(cmd.equals("backup")){
new ReceiveBackup(channel, prop, serverPubkey, serverPrikey, pubkeyTable, backupFileStateTable).run();
System.out.println("ReceiveBackup: done");
}else if(cmd.equals("auth")){
new ReceiveAuthData(channel, prop, serverPrikey, pubkeyTable, backupFileStateTable).run();
System.out.println("ReceiveAuthData: done");
}else if(cmd.equals("revoke")){
new ReceiveRevokeData(channel, prop, serverPrikey, serverPubkey, pubkeyTable, fileStateTable, backupFileStateTable).run();
System.out.println("RecieveRevokeData: done");
}else if(cmd.equals("update2")){
new ReceiveUpdateData2(channel, prop, serverPrikey, serverPubkey, pubkeyTable, backupFileStateTable).run();
}else if(cmd.equals("update")){
new ReceiveUpdateData(channel, prop, serverPrikey, serverPubkey, pubkeyTable, backupFileStateTable).run();
}else if(cmd.equals("moveBackup")){
new ReceiveMoveBackupData(channel, prop, serverPrikey, serverPubkey, pubkeyTable, backupFileStateTable).run();
}else if(cmd.equals("update3")){
new ReceiveUpdateData3(channel, prop, serverPrikey, serverPubkey, pubkeyTable, backupFileStateTable).run();
}
} catch (IOException e) {
e.printStackTrace();
}finally{
SocketAddress remoteAddress = channel.socket().getRemoteSocketAddress();
System.out.println(remoteAddress + ":[切断しました]");
if(channel != null && channel.isOpen()){
try {
channel.close();
} catch (IOException e) {}
}
}
}
/*
private ByteBuffer receiveData(boolean next) throws IOException {
return CommandHandler.receiveData(channel, next);
}
*/
}
/**
* ファイルオブジェクト
* @author takayama
*
*/
public class FileObject implements Serializable{
/**
* シリアルバージョンID
*/
private static final long serialVersionUID = -5710297060225770902L;
public String fileName = "";
public String path = "";
public String owner = "";
public String backupHost = "";
public HashMap<String, byte[]> keyTable = new HashMap<String, byte[]>();
public String revokeType = "";
public boolean revokeFlug = false;
//コンストラクタ
public FileObject(String fileName, String path, String backupHost, Properties prop, SecretKey skey, String userName, PublicKey pubkey, byte[] encKey) {
this.fileName = fileName;
this.path = path.concat(fileName).concat(".crypt");
this.owner = userName;
this.backupHost = backupHost;
this.revokeType = prop.getProperty("revokeType");
try {
Cipher wcipher = Cipher.getInstance(prop.getProperty("pubAlg"));
//ユーザ公開鍵による共通鍵の格納
wcipher.init(Cipher.WRAP_MODE, pubkey);
keyTable.put(owner, wcipher.wrap(skey));
//サーバ公開鍵による共通鍵の格納
keyTable.put("server", encKey);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
}
public void updateTable(SecretKey skey, byte[] encKey, PublicKey serverPubkey, HashMap<String, PublicKey> pubkeyTable, String pubAlg) {
try {
//共通鍵がすでにサーバ公開鍵で暗号化されていれば別処理
if(encKey != null){
keyTable.remove("server");
}
PublicKey pubkey = null;
Cipher wcipher = Cipher.getInstance(pubAlg);
Iterator<String> it  = keyTable.keySet().iterator();
while(it.hasNext()){
String name = it.next();
System.out.println(name);
if(name.equals("server")){
pubkey = serverPubkey;
}else if(name.equals("current")){
continue;
}else{
pubkey = pubkeyTable.get(name);
}
wcipher.init(Cipher.WRAP_MODE, pubkey);
keyTable.put(name, wcipher.wrap(skey));
}
//別処理
if(encKey != null){
keyTable.put("server", encKey);
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
}
/**
* keyTableに新たな鍵を加えるメソッド
* @param name 
* @param encKey
*/
public void addKey(String name, byte[] encKey) {
keyTable.put(name, encKey);
}
/**
* ユーザのアクセス権があるか返すメソッド
* @param userName
* @return
*/
public boolean canAccess(String userName) {
return keyTable.containsKey(userName);
}
/**
* ユーザ用の共通鍵を返すメソッド
* @param userName
* @return
*/
public byte[] getKey(String userName) {
return keyTable.get(userName);
}
/**
* ユーザがオーナーか判別するメソッド
* @param userName
* @return
*/
public boolean isOwner(String userName) {
// TODO 自動生成されたメソッド・スタブ
return owner.equals(userName);
}
/**
* 指定されたユーザ用の鍵をkeyTableに登録するメソッド
* @param authName
* @param serverPrikey
* @param key
* @param prop
*/
public void authUser(String authName, PrivateKey serverPrikey, PublicKey pubkey, Properties prop) {
try {
//サーバ秘密鍵による共通鍵の取り出し
byte[] encKey = keyTable.get("server");
Cipher cipher = Cipher.getInstance(prop.getProperty("pubAlg"));
cipher.init(Cipher.UNWRAP_MODE, serverPrikey);
SecretKey skey = (SecretKey)cipher.unwrap(encKey, prop.getProperty("secAlg"), Cipher.SECRET_KEY);
//ユーザ公開鍵での格納
cipher.init(Cipher.WRAP_MODE, pubkey);
encKey = cipher.wrap(skey);
keyTable.put(authName, encKey);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
}
/**
* backupHostを返すメソッド
* @return
*/
public String getBackupHost() {
return backupHost;
}
/**
* revokeTypeを返すメソッド
* @return
*/
public String getRevokeType() {
// TODO 自動生成されたメソッド・スタブ
return revokeType;
}
/**
* 指定されたユーザの鍵をkeyTableから削除するメソッド
* @param revokedName
*/
public void removeKey(String revokedName) {
keyTable.remove(revokedName);
}
/**
* 新しい鍵を生成し，ファイルを再暗号化するメソッド
* @param prop
* @param skey 
* @param serverPrikey
* @param serverPubkey
* @param pubkeyTable
*/
public void reEncrypt(Properties prop, SecretKey skey, PrivateKey serverPrikey, PublicKey serverPubkey, HashMap<String, PublicKey> pubkeyTable, boolean change) {
try {
String secAlg = prop.getProperty("secAlg");
int secLen = Integer.parseInt(prop.getProperty("secLen"));
String pubAlg = prop.getProperty("pubAlg");
String transform = prop.getProperty("transform");
String backup = prop.getProperty("backup");
//共通鍵が与えられていなければ新しく生成
SecretKey newKey = null;
if(skey == null){
KeyGenerator keygen = KeyGenerator.getInstance(secAlg);
keygen.init(secLen);
newKey = keygen.generateKey();
}else{
newKey = skey;
}
//復号
Cipher ucipher = Cipher.getInstance(pubAlg);
ucipher.init(Cipher.UNWRAP_MODE, serverPrikey);
SecretKey oldKey = (SecretKey)ucipher.unwrap(keyTable.get("server"), secAlg, Cipher.SECRET_KEY);
FileChannel input = new FileInputStream(path).getChannel();
int csize = (int)input.size();
ByteBuffer cbuf = ByteBuffer.allocate(csize);
input.read(cbuf);
cbuf.flip();
input.close();
Cipher cipher = Cipher.getInstance(transform);
cipher.init(Cipher.DECRYPT_MODE, oldKey);
ByteBuffer pbuf = ByteBuffer.allocate(cipher.getOutputSize(csize));
cipher.doFinal(cbuf, pbuf);
pbuf.flip();
cbuf.clear();
//再暗号化
cipher.init(Cipher.ENCRYPT_MODE, newKey);
cipher.doFinal(pbuf, cbuf);
cbuf.flip();
//格納(change=trueならバックアップへ移動)
if(change){
new File(path).delete();
this.path = backup.concat(fileName).concat(".crypt");
}
FileChannel output = new FileOutputStream(path).getChannel();
output.write(cbuf);
output.close();
//keyTableの更新
updateTable(newKey, null, serverPubkey, pubkeyTable, pubAlg);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ShortBufferException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
}
/**
* revokeFlugを変更するメソッド
* @param revokeFlug
*/
public void setRevokeFlug(boolean revokeFlug) {
this.revokeFlug = revokeFlug;
}
/**
* fileNameを返すメソッド
* @return
*/
public String getFileName() {
return fileName;
}
/**
* backupHostを変更するメソッド
* @param host
*/
public void setBackupHost(String host) {
this.backupHost = host;
}
/**
* pathを変更するメソッド
* @param newPath
*/
public void setPath(String newPath) {
this.path = newPath;
}
/**
* revokeFlugを返すメソッド
* @return revokeFlug
*/
public boolean getRevokeFlug() {
return revokeFlug;
}
/**
* pathを返すメソッド
* @return
*/
public String getPath() {
return path;
}
}
/**
* 各コマンドハンドラクラスのスーパークラス
* @author takayama
*
*/
public abstract class CommandHandler {
public abstract void execute();
/**
* 大きなサイズのデータを送信するためのメソッド
* @param channel
* @param buf
* @param next
* @throws IOException
*/
public static void sendData(SocketChannel channel, ByteBuffer buf, boolean next) throws IOException{  
//要素数を送信
ByteBuffer count = ByteBuffer.allocate(8).putInt(buf.remaining());
count.flip();
channel.write(count);
//Ackを待つ
ByteBuffer ack = ByteBuffer.allocate(3);
channel.read(ack);
ack.clear();
//本体の送信
while(buf.remaining() > 0){
channel.write(buf);
}
//次もsendDataならAckを待つ
if(next){
channel.read(ack);
}
return;
}
/**
* 大きなデータを受信する為のメソッド
* @param channel
* @param next
* @return 受信したByteBuffer
* @throws IOException
*/
public static ByteBuffer receiveData(SocketChannel channel, boolean next) throws IOException{
ByteBuffer count = ByteBuffer.allocate(8);
Charset charset = Charset.forName("UTF-8");
//要素数の受信
channel.read(count);
count.flip();
int size = count.getInt();
//Ackを返す
channel.write(charset.encode("ack"));         
//本体の受信
ByteBuffer buf = ByteBuffer.allocate(size);
while(buf.remaining() > 0){
channel.read(buf);
}
buf.flip();
//次もreceiveDataを実行するならAckを返す
if(next){
channel.write(charset.encode("ack"));
}
return buf;
}
/**
* Stringデータを送信するメソッド
* @param channel
* @param buf
* @param next
* @throws IOException
*/
public static void sendShortData(SocketChannel channel, ByteBuffer buf, boolean next) throws IOException{
//データの送信
channel.write(buf);
//次も送信処理ならackを待つ
ByteBuffer ack = ByteBuffer.allocate(3);
if(next){
channel.read(ack);
}
return;
}
/**
* サイズの小さいデータ（文字列：256以下，共通鍵128，公開鍵162）を受信するメソッド
* @param channel
* @param next
* @return 受信したByteBuffer
* @throws IOException
*/
public static ByteBuffer receiveShortData(SocketChannel channel, boolean next) throws IOException{
ByteBuffer buf = ByteBuffer.allocate(256);
Charset charset = Charset.forName("UTF-8");
//本体の受信
channel.read(buf);
buf.flip();
//次もreceiveDataを実行するならAckを返す
if(next){
channel.write(charset.encode("ack"));
}
return buf;
}
/**
* コマンドを通信先に送信するメソッド
* @param cmd
* @param channel
* @throws IOException
*/
public static void commandSet(String cmd, SocketChannel channel) throws IOException {
Charset charset = Charset.forName("UTF-8");
channel.write(charset.encode(cmd));
//ackを待つ
ByteBuffer buf = ByteBuffer.allocate(3);
channel.read(buf);
}
}
/**
* putコマンドに対応するクラス
* @author takayama
*
*/
public class PutCommandHandler extends CommandHandler {
private Properties prop = null;
private String primary = "";
private String pubAlg = "";
private String secAlg = "";
private int secLen = -1;
private String backupHost = "";
private SocketChannel channel = null;
private String userName = "";
private HashMap<String, PublicKey> pubkeyTable = null;
private PrivateKey serverPrikey = null;
private Map<String, String> fileStateTable = null;
public PutCommandHandler(SocketChannel channel, Properties prop, String userName, HashMap<String, PublicKey> pubkeyTable, PrivateKey serverPrikey, Map<String, String> fileStateTable) {
this.prop = prop;
this.channel = channel;
this.userName = userName;
this.pubkeyTable = pubkeyTable;
this.serverPrikey = serverPrikey;
this.fileStateTable = fileStateTable;
primary = prop.getProperty("primary");
pubAlg = prop.getProperty("pubAlg");
secAlg = prop.getProperty("secAlg");
secLen = Integer.parseInt(prop.getProperty("secLen"));
backupHost = prop.getProperty("backupHost");
}
@Override
public void execute() {
try {
System.out.println("PutCommandhandrer: start");
//ファイル名の受信
ByteBuffer fnameBuf = receiveShortData(channel, true);
Charset charset = Charset.forName("UTF-8");
String fileName = charset.decode(fnameBuf).toString();
String encName = fileName.concat(".crypt");
fnameBuf.rewind();
System.out.println(fileName);
//鍵とファイルの受信
ByteBuffer buf = receiveData(channel, false);
//鍵の読み出し
byte[] encKey = new byte[secLen];
buf.get(encKey);
Cipher ucipher = Cipher.getInstance(pubAlg);
ucipher.init(Cipher.UNWRAP_MODE, serverPrikey);
SecretKey skey = (SecretKey)ucipher.unwrap(encKey, secAlg, Cipher.SECRET_KEY);
System.out.println(skey);
//ロック
synchronized (fileStateTable) {
while(fileStateTable.containsKey(fileName) && fileStateTable.get(fileName).equals("lock")){
try {
fileStateTable.wait();
} catch (InterruptedException e) {}
}
fileStateTable.put(fileName, "lock");
fileStateTable.notifyAll();
}
//ファイル格納
ByteBuffer fileBuf = ByteBuffer.allocate(buf.remaining());
fileBuf.put(buf);
fileBuf.flip();
FileChannel output = new FileOutputStream(primary.concat(encName)).getChannel();
output.write(fileBuf);
output.close();
fileBuf.rewind();
//バックアップ作成
new Thread(new SendBackupThread(backupHost, userName, fnameBuf, fileBuf, skey, pubkeyTable.get(backupHost), prop)).start();
//ファイルオブジェクト作成
setFileObject(fileName, skey, userName, encKey);
//ロック解除
synchronized (fileStateTable) {
fileStateTable.put(fileName, "");
fileStateTable.notifyAll();
}
//ack
channel.write(charset.encode("ack"));
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
/**
* ファイルオブジェクトを作成するメソッド
* @param fileName
* @param skey
* @param userName
* @param encKey
*/
private void setFileObject(String fileName, SecretKey skey, String userName, byte[] encKey) {
try {
FileObject file = null;
String foPath = primary.concat(fileName).concat(".file");
//ファイルオブジェクトを読み込む．存在しなければ生成
try {
ObjectInputStream input = new ObjectInputStream(new FileInputStream(foPath));
file = (FileObject)input.readObject();
input.close();
//keyTableを新しい鍵に更新
file.updateTable(skey, encKey, null, pubkeyTable, pubAlg);
} catch (FileNotFoundException e){
file = new FileObject(fileName, primary, backupHost, prop, skey, userName, pubkeyTable.get(userName), encKey);
}
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(foPath));
output.writeObject(file);
output.flush();
output.close();
} catch (IOException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}
}
/**
* backup作成のために指定サーバへデータを送信するクラス
* @author takayama
*
*/
public class SendBackupThread implements Runnable {
private SocketChannel channel = null;
private ByteBuffer fnameBuf = null;
private ByteBuffer fileBuf = null;
private SecretKey skey = null;
private String userName = "";
private PublicKey backupPubkey = null;
private String backupHost = "";
//コンストラクタ
public SendBackupThread(String backupHost, String userName, ByteBuffer fnameBuf, ByteBuffer fileBuf, SecretKey skey, PublicKey backupPubkey, Properties prop) {
this.fnameBuf = fnameBuf;
this.fileBuf = fileBuf;
this.skey = skey;
this.userName = userName;
this.backupPubkey = backupPubkey;
this.backupHost = backupHost;
try {
channel = SocketChannel.open(new InetSocketAddress(backupHost, Integer.parseInt(prop.getProperty("port"))));
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {       
try {
//要求の送信
Charset charset = Charset.forName("UTF-8");
channel.write(charset.encode("backup"));
//ackを待つ
ByteBuffer buf = ByteBuffer.allocate(3);
channel.read(buf);
System.out.println("backup: " + charset.decode(fnameBuf).toString() + " > " + backupHost);
fnameBuf.rewind();
//ファイル名の送信
System.out.println(fnameBuf);
sendShortData(fnameBuf, true);
//ファイル本体の送信
System.out.println(fileBuf);
sendData(fileBuf, true);
//オーナー名の送信
sendShortData(charset.encode(userName), true);
//共通鍵の送信
Cipher wcipher = Cipher.getInstance(backupPubkey.getAlgorithm());
wcipher.init(Cipher.WRAP_MODE, backupPubkey);
byte[] encKey = wcipher.wrap(skey);
System.out.println(encKey);
ByteBuffer keyBuf = ByteBuffer.wrap(encKey);
System.out.println(keyBuf);
sendShortData(keyBuf, true);
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace(); 
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
}
private void sendShortData(ByteBuffer buf, boolean next) throws IOException {
CommandHandler.sendShortData(channel, buf, next);
}
private void sendData(ByteBuffer buf, boolean next) throws IOException {
CommandHandler.sendData(channel, buf, next);
}
}
/**
* primary側からデータを受信し，バックアップを作成するクラス
* @author takayama
*
*/
public class ReceiveBackup{
private SocketChannel channel = null;
private Properties prop = null;
private PublicKey serverPubkey = null;
private PrivateKey serverPrikey = null;
private HashMap<String, PublicKey> pubkeyTable = null;
private Map<String, String> backupFileStateTable;
public ReceiveBackup(SocketChannel channel, Properties prop, PublicKey serverPubkey, PrivateKey serverPrikey, HashMap<String, PublicKey> pubkeyTable, Map<String, String> backupFileStateTable) {
this.channel = channel;
this.prop = prop;
this.serverPubkey = serverPubkey;
this.serverPrikey = serverPrikey;
this.pubkeyTable = pubkeyTable;
this.backupFileStateTable = backupFileStateTable;
}
public void run() {
String pubAlg = prop.getProperty("pubAlg");
String secAlg = prop.getProperty("secAlg");
String revokeType = prop.getProperty("revokeType");
int secLen = Integer.parseInt(prop.getProperty("secLen"));
String backup = prop.getProperty("backup");
try {
//ファイル名の受信
ByteBuffer buf = receiveShortData(true);
Charset charset = Charset.forName("UTF-8");
String fileName = charset.decode(buf).toString();
System.out.println("file name: " + fileName);
//ファイル本体の受信
ByteBuffer fileBuf = receiveData(true);
//オーナー名の受信
buf = receiveShortData(true);
String userName = charset.decode(buf).toString();
System.out.println(userName);
//鍵の受信
ByteBuffer keyBuf = receiveShortData(true);
System.out.println(keyBuf);
byte[] encKey = new byte[keyBuf.remaining()];
System.out.println(encKey.length);
keyBuf.get(encKey);
System.out.println(encKey);
System.out.println(encKey.length);
Cipher ucipher = Cipher.getInstance(pubAlg);
ucipher.init(Cipher.UNWRAP_MODE, serverPrikey);
SecretKey skey = (SecretKey)ucipher.unwrap(encKey, secAlg, Cipher.SECRET_KEY);
//提案手法では再暗号化
byte[] encCurrentKey = null;
if(revokeType.equals("proposal") || revokeType.equals("proposal2")){
//ファイル復号
Cipher cipher = Cipher.getInstance(secAlg);
cipher.init(Cipher.DECRYPT_MODE, skey);
buf = ByteBuffer.allocate(cipher.getOutputSize(fileBuf.capacity()));
cipher.doFinal(fileBuf, buf);
buf.flip();
//新しい鍵の生成
encCurrentKey = encKey;
KeyGenerator keygen = KeyGenerator.getInstance(secAlg);
keygen.init(secLen);
skey = keygen.generateKey();
//鍵のラップ
Cipher wcipher = Cipher.getInstance(pubAlg);
wcipher.init(Cipher.WRAP_MODE, serverPubkey);
encKey = wcipher.wrap(skey);
cipher.init(Cipher.ENCRYPT_MODE, skey);
fileBuf.clear();
cipher.doFinal(buf, fileBuf);
fileBuf.flip();
}
//ロック
synchronized (backupFileStateTable) {
while(backupFileStateTable.containsKey(fileName) && backupFileStateTable.get(fileName).equals("lock")){
try {
backupFileStateTable.wait();
} catch (InterruptedException e) {}
}
backupFileStateTable.put(fileName, "lock");
backupFileStateTable.notifyAll();
}
//ファイル格納
FileChannel output = new FileOutputStream(backup.concat(fileName).concat(".crypt")).getChannel();
output.write(fileBuf);
output.close();
//ファイルオブジェクト作成
setFileObject(fileName, skey, userName, encKey, encCurrentKey);
//ロック解除
synchronized (backupFileStateTable) {
backupFileStateTable.put(fileName, "");
backupFileStateTable.notifyAll();
}
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (ShortBufferException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
}
private void setFileObject(String fileName, SecretKey skey, String userName, byte[] encKey, byte[] encCurrentKey) {
try {
String backup = prop.getProperty("backup");
String pubAlg = prop.getProperty("pubAlg");
String revokeType = prop.getProperty("revokeType");
String backupHost = prop.getProperty("backupHost");
String primaryHost = prop.getProperty("primaryHost");
FileObject file = null;
String foPath = backup.concat(fileName).concat(".file");
//ファイルオブジェクトを読み込む．存在しなければ生成
try {
ObjectInputStream input = new ObjectInputStream(new FileInputStream(foPath));
file = (FileObject)input.readObject();
input.close();
//keyTableを新しい鍵に更新
file.updateTable(skey, encKey, null, pubkeyTable, pubAlg);
if(revokeType.equals("proposal") || revokeType.equals("proposal2")){
file.addKey("current", encCurrentKey);
}
} catch (FileNotFoundException e){
file = new FileObject(fileName, backup, backupHost, prop, skey, userName, pubkeyTable.get(userName), encKey);
if(revokeType.equals("proposal")){
file.addKey("current", encCurrentKey);
file.setBackupHost(primaryHost);
}else if(revokeType.equals("proposal2")){
file.addKey("current", encCurrentKey);
}
}
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(foPath));
output.writeObject(file);
output.flush();
output.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private ByteBuffer receiveData(boolean next) throws IOException {
return CommandHandler.receiveData(channel, next);
}
private ByteBuffer receiveShortData(boolean next) throws IOException {
return CommandHandler.receiveShortData(channel, next);
}
}
/**
* getコマンドに対応するクラス
* @author takayama
*
*/
public class GetCommandHandler extends CommandHandler {
private SocketChannel channel = null;
private Properties prop = null;
private String userName = "";
private Map<String, String> fileStateTable = null;
public GetCommandHandler(SocketChannel channel, Properties prop, String userName, Map<String, String> fileStateTable) {
this.channel = channel;
this.prop = prop;
this.userName = userName;
this.fileStateTable = fileStateTable;
}
@Override
public void execute() {
try {
String primary = prop.getProperty("primary");
//ファイル名の受信
Charset charset = Charset.forName("UTF-8");
ByteBuffer buf = receiveShortData(channel, true);
String fileName = charset.decode(buf).toString();
//共通鍵転送有無の指定を受信
buf = receiveShortData(channel, false);
boolean send = Boolean.parseBoolean(charset.decode(buf).toString());
//ファイルの存在とlockを確認
synchronized (fileStateTable) {
//ロック解除まで待つ
while(fileStateTable.get(fileName).equals("lock")){
try {
fileStateTable.wait();
} catch (InterruptedException e) {}
}
//状態がmove:…ならばrevoke処理が完了するまで待ち，再実行要求を出す
String state = fileStateTable.get(fileName);
if(state.startsWith("move")){
String newHost = state.split(":")[1];
sendData(channel, charset.encode("mov:".concat(newHost).concat(":")), false);
System.out.println("error: " + fileName + " move to " + newHost);
fileStateTable.notifyAll();
return;
}
fileStateTable.put(fileName, "lock");
fileStateTable.notifyAll();
}
//ファイルオブジェクトの読み込み
ObjectInputStream obinput = new ObjectInputStream(new FileInputStream(primary.concat(fileName).concat(".file")));
FileObject file = (FileObject)obinput.readObject();
obinput.close();
if(file.canAccess(userName)){
FileChannel input = new FileInputStream(file.getPath()).getChannel();
if(send){
//共通鍵とファイル本体を読み込み
byte[] encKey = file.getKey(userName);
ByteBuffer keyBuf = ByteBuffer.wrap(encKey);
buf = ByteBuffer.allocate(keyBuf.capacity() + (int)input.size());
buf.put(keyBuf);
input.read(buf);
buf.flip();
input.close();
}else{
//ファイル本体を読み込み
buf = ByteBuffer.allocate((int)input.size());
input.read(buf);
buf.flip();
input.close();
}
//ロック解除
synchronized (fileStateTable) {
fileStateTable.put(fileName, "");
fileStateTable.notifyAll();
}
//データの送信
sendData(channel, buf, false);
}else{
//ロック解除
synchronized (fileStateTable) {
fileStateTable.put(fileName, "");
fileStateTable.notifyAll();
}
sendData(channel, charset.encode("401"), false);
}
return;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
/**
* アクセス権付与リクエストに対応するクラス
* @author takayama
*
*/
public class AuthCommandHandler extends CommandHandler {
private SocketChannel channel = null;
private Properties prop = null;
private String userName = "";
private PrivateKey serverPrikey = null;
private HashMap<String, PublicKey> pubkeyTable = null;
private Map<String, String> fileStateTable = null;
private String primary = "";
//コンストラクタ
public AuthCommandHandler(SocketChannel channel, Properties prop, String userName, PrivateKey serverPrikey, HashMap<String, PublicKey> pubkeyTable, Map<String, String> fileStateTable) {
this.channel = channel;
this.prop = prop;
this.userName = userName;
this.serverPrikey = serverPrikey;
this.pubkeyTable = pubkeyTable;
this.fileStateTable = fileStateTable;
primary = prop.getProperty("primary");
}
@Override
public void execute() {
try {
String response = "";
//ファイル名の受信
Charset charset = Charset.forName("UTF-8");
ByteBuffer fnameBuf = receiveShortData(channel, true);
String fileName = charset.decode(fnameBuf).toString();
fnameBuf.rewind();
System.out.println("auth: file name = " + fileName);
//対称ユーザ名の受信
ByteBuffer authBuf = receiveShortData(channel, false);
String authName = charset.decode(authBuf).toString();
authBuf.rewind();
System.out.println("auth: user = " + authName);
//ロック
synchronized (fileStateTable) {
if(fileStateTable.containsKey(fileName)){
while(fileStateTable.get(fileName).equals("lock")){
try {
fileStateTable.wait();
} catch (InterruptedException e) {}
}
fileStateTable.put(fileName, "lock");
fileStateTable.notifyAll();
}else{
response = fileName.concat(": not found");
System.out.println(response);
sendShortData(channel, charset.encode(response), false);
fileStateTable.notifyAll();
return;
}
}
//ファイルオブジェクトの読み込み
String foPath = primary.concat(fileName).concat(".file");
ObjectInputStream obin = new ObjectInputStream(new FileInputStream(foPath));
FileObject file = (FileObject)obin.readObject();
obin.close();
//処理
if(file.isOwner(userName)){
String backupHost = file.getBackupHost();
new Thread(new SendAuthDataThread(fnameBuf, authBuf, backupHost, prop)).start();
file.authUser(authName, serverPrikey, pubkeyTable.get(authName), prop);
response = "Authorization is completed";
ObjectOutputStream obout = new ObjectOutputStream(new FileOutputStream(foPath));
obout.writeObject(file);
obout.flush();
obout.close();
}else{
response = "error: Only owner can do AUTH command";
}
//ロック解除
synchronized (fileStateTable) {
fileStateTable.put(fileName, "");
fileStateTable.notifyAll();
}
//結果の送信
System.out.println(response);
sendShortData(channel, charset.encode(response), false);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
/**
* プライマリ側で，バックアップにアクセス権付与情報を送信するクラス
* @author takayama
*
*/
public class SendAuthDataThread implements Runnable {
private SocketChannel channel = null;
private ByteBuffer fnameBuf = null;
private ByteBuffer authBuf = null;
private String backupHost = "";
public SendAuthDataThread(ByteBuffer fnameBuf, ByteBuffer authBuf, String backupHost, Properties prop) {
this.fnameBuf = fnameBuf;
this.authBuf = authBuf;
this.backupHost = backupHost;
try {
channel = SocketChannel.open(new InetSocketAddress(backupHost, Integer.parseInt(prop.getProperty("port"))));
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
try {
//要求の送信
Charset charset = Charset.forName("UTF-8");
channel.write(charset.encode("auth"));
//ackを待つ
ByteBuffer buf = ByteBuffer.allocate(3);
channel.read(buf);
System.out.println("send auth data > " + backupHost);
//ファイル名の送信
System.out.println(charset.decode(fnameBuf).toString());
fnameBuf.rewind();
System.out.println(fnameBuf);
sendShortData(fnameBuf, true);
//対称ユーザ名の送信
sendShortData(authBuf, true);
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendShortData(ByteBuffer buf, boolean next) throws IOException {
CommandHandler.sendShortData(channel, buf, next);
}
}
/**
* バックアップ側で，プライマリ側からアクセス権付与情報を受け取るクラス
* @author takayama
*
*/
public class ReceiveAuthData {
private SocketChannel channel = null;
private Properties prop = null;
private PrivateKey serverPrikey = null;
private HashMap<String, PublicKey> pubkeyTable = null;
private Map<String, String> backupFileStateTable;
public ReceiveAuthData(SocketChannel channel, Properties prop, PrivateKey serverPrikey, HashMap<String, PublicKey> pubkeyTable, Map<String, String> backupFileStateTable) {
this.channel = channel;
this.prop = prop;
this.serverPrikey = serverPrikey;
this.pubkeyTable = pubkeyTable;
this.backupFileStateTable = backupFileStateTable;
}
public void run() {
try {
//ファイル名の受信
ByteBuffer buf = receiveShortData(true);
System.out.println(buf);
System.out.println(buf.toString());
Charset charset = Charset.forName("UTF-8");
ByteBuffer b = charset.encode("10MB.txt");
System.out.println(b.equals(buf));
String fileName = charset.decode(buf).toString();
System.out.println("auth: file name = " + fileName);
//ロック
synchronized (backupFileStateTable) {
while(backupFileStateTable.get(fileName).equals("lock")){
try {
backupFileStateTable.wait();
} catch (InterruptedException e) {}
}
backupFileStateTable.put(fileName, "lock");
backupFileStateTable.notifyAll();
}
//ファイルオブジェクトの読み込み
String foPath = prop.getProperty("backup").concat(fileName).concat(".file");
ObjectInputStream obin = new ObjectInputStream(new FileInputStream(foPath));
FileObject file = (FileObject)obin.readObject();
obin.close();
//対称ユーザ名の受信
buf = receiveShortData(true);
String authName = charset.decode(buf).toString();
System.out.println("auth: user = " + authName);
//処理
file.authUser(authName, serverPrikey, pubkeyTable.get(authName), prop);
System.out.println(file.keyTable);
//ファイルオブジェクトの保存
ObjectOutputStream obout = new ObjectOutputStream(new FileOutputStream(foPath));
obout.writeObject(file);
obout.flush();
obout.close();
//ロック解除
synchronized (backupFileStateTable) {
backupFileStateTable.put(fileName, "");
backupFileStateTable.notifyAll();
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private ByteBuffer receiveShortData(boolean next) throws IOException {
return CommandHandler.receiveShortData(channel, next);
}
}
/**
* アクセス権失効リクエストに対応するクラス
* @author takayama
*
*/
public class RevokeCommandHandler extends CommandHandler {
private SocketChannel channel = null;
private Properties prop = null;
private String userName = "";
private PublicKey serverPubkey = null;
private PrivateKey serverPrikey = null;
private HashMap<String, PublicKey> pubkeyTable = null;
private Map<String, String> fileStateTable = null;
private Map<String, String> backupFileStateTable;
public RevokeCommandHandler(SocketChannel channel, Properties prop, String userName, PublicKey serverPubkey, PrivateKey serverPrikey, HashMap<String, PublicKey> pubkeyTable, Map<String, String> fileStateTable, Map<String, String> backupFileStateTable) {
this.channel = channel;
this.prop = prop;
this.userName = userName;
this.serverPubkey = serverPubkey;
this.serverPrikey = serverPrikey;
this.pubkeyTable = pubkeyTable;
this.fileStateTable  = fileStateTable;
this.backupFileStateTable = backupFileStateTable;
}
@Override
public void execute() {
try {
String response = "";
//ファイル名の受信
Charset charset = Charset.forName("UTF-8");
ByteBuffer fnameBuf = receiveShortData(channel, true);
String fileName = charset.decode(fnameBuf).toString();
String foPath = prop.getProperty("primary").concat(fileName).concat(".file");
fnameBuf.rewind();
//ロック
synchronized (fileStateTable) {
if(fileStateTable.containsKey(fileName)){
while(fileStateTable.get(fileName).equals("lock")){
fileStateTable.wait();
}
fileStateTable.put(fileName, "lock");
fileStateTable.notifyAll();
}else{
response = fileName.concat(": not found");
sendData(channel, charset.encode(response), false);
fileStateTable.notifyAll();
return;
}
}
//対称ユーザ名の受信
ByteBuffer revokedBuf = receiveShortData(channel, false);
String revokedName = charset.decode(revokedBuf).toString();
revokedBuf.rewind();
//ファイルオブジェクトの読み込み
ObjectInputStream obin = new ObjectInputStream(new FileInputStream(foPath));
FileObject file = (FileObject)obin.readObject();
obin.close();
//処理
String revokeType = file.getRevokeType();
String backupHost = file.getBackupHost();
if(!file.isOwner(userName)){
response  = "error: Only owner can do REVOKE command.";
}else if(!file.canAccess(revokedName)){
response = "error: The revokedUser doesn't have access right.";
}else{
//鍵の削除
file.removeKey(revokedName);
//タイプにより分岐
if(revokeType.equals("active")){
//共通鍵生成
KeyGenerator keygen = KeyGenerator.getInstance(prop.getProperty("secAlg"));
keygen.init(Integer.parseInt(prop.getProperty("secLen")));
SecretKey skey = keygen.generateKey();
//情報と新しい鍵をbackupへ送信
SendRevokeDataThread thread = new SendRevokeDataThread(backupHost, skey, fnameBuf, revokedBuf, file, serverPrikey, serverPubkey, pubkeyTable, prop);
thread.start();
//再暗号化
file.reEncrypt(prop, skey, serverPrikey, serverPubkey, pubkeyTable, false);
response = "Active revocation is done.";
}else if(revokeType.equals("lazy")){
file.setRevokeFlug(true);
SendRevokeDataThread thread = new SendRevokeDataThread(backupHost, fnameBuf, revokedBuf, file, serverPrikey, serverPubkey, pubkeyTable, prop);
thread.start();
response = "set lazy revocation";
}else if(revokeType.equals("proposal") || revokeType.equals("proposal2")){
//backupに情報を送信し，処理を待つ
SendRevokeDataThread thread = new SendRevokeDataThread(backupHost, fnameBuf, revokedBuf, file, serverPrikey, serverPubkey, pubkeyTable, prop, backupFileStateTable);
synchronized (thread) {
thread.start();
thread.wait();
}
//fileStateTableで移動先ディスクを明示
synchronized (fileStateTable) {
fileStateTable.put(fileName, "move:".concat(backupHost));
fileStateTable.notifyAll();
}
//結果を送信し終了（ファイルオブジェクトはSendRevokeDataThread内，MoveToBackupThreadで格納）
response = "Proposal revocation is done.";
sendShortData(channel, charset.encode(response), false);
return;
}
//ファイルオブジェクト格納
ObjectOutputStream obout = new ObjectOutputStream(new FileOutputStream(foPath));
obout.writeObject(file);
obout.flush();
obout.close();
//ロック解除
synchronized (fileStateTable) {
fileStateTable.put(fileName, "");
fileStateTable.notifyAll();
}
//                          System.out.println(fileStateTable);
}
//結果の送信（proposal以外）
System.out.println(response);
sendShortData(channel, charset.encode(response), false);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* プライマリ側で，バックアップにアクセス権失効情報を送信するクラス
* @author takayama
*
*/
public class SendRevokeDataThread extends Thread {
private SocketChannel channel = null;
private String backupHost = "";
private SecretKey skey = null;
private ByteBuffer fnameBuf = null;
private ByteBuffer revokedBuf = null;
private FileObject file = null;
private PrivateKey serverPrikey = null;
private PublicKey serverPubkey = null;
private HashMap<String, PublicKey> pubkeyTable = null;
private Properties prop = null;
private Map<String, String> backupFileStateTable = null;
//active用コンストラクタ
public SendRevokeDataThread(String backupHost, SecretKey skey, ByteBuffer fnameBuf, ByteBuffer revokedBuf, FileObject file, PrivateKey serverPrikey, PublicKey serverPubkey, HashMap<String, PublicKey> pubkeyTable, Properties prop) {
this.backupHost = backupHost;
this.skey = skey;
this.fnameBuf = fnameBuf;
this.revokedBuf = revokedBuf;
this.file = file;
this.serverPrikey = serverPrikey;
this.serverPubkey = serverPubkey;
this.pubkeyTable = pubkeyTable;
this.prop  = prop;
try {
channel = SocketChannel.open(new InetSocketAddress(backupHost, Integer.parseInt(prop.getProperty("port"))));
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//lazy用コンストラクタ
public SendRevokeDataThread(String backupHost, ByteBuffer fnameBuf, ByteBuffer revokedBuf, FileObject file, PrivateKey serverPrikey, PublicKey serverPubkey, HashMap<String, PublicKey> pubkeyTable, Properties prop) {
this.backupHost = backupHost;
this.fnameBuf = fnameBuf;
this.revokedBuf = revokedBuf;
this.file = file;
this.serverPrikey = serverPrikey;
this.serverPubkey = serverPubkey;
this.pubkeyTable = pubkeyTable;
this.prop = prop;
try {
channel = SocketChannel.open(new InetSocketAddress(backupHost, Integer.parseInt(prop.getProperty("port"))));
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//proposal, proposal2用コンストラクタ
public SendRevokeDataThread(String backupHost, ByteBuffer fnameBuf, ByteBuffer revokedBuf, FileObject file, PrivateKey serverPrikey, PublicKey serverPubkey, HashMap<String, PublicKey> pubkeyTable, Properties prop, Map<String, String> backupFileStateTable) {
this.backupHost = backupHost;
this.fnameBuf = fnameBuf;
this.revokedBuf = revokedBuf;
this.file = file;
this.serverPrikey = serverPrikey;
this.serverPubkey = serverPubkey;
this.pubkeyTable = pubkeyTable;
this.prop = prop;
this.backupFileStateTable = backupFileStateTable;
try {
channel = SocketChannel.open(new InetSocketAddress(backupHost, Integer.parseInt(prop.getProperty("port"))));
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
String pubAlg = prop.getProperty("pubAlg");
try {
//要求の送信
Charset charset = Charset.forName("UTF-8");
channel.write(charset.encode("revoke"));
//ackを待つ
ByteBuffer buf = ByteBuffer.allocate(3);
while(channel.read(buf) <= 0){}
//ファイル名の送信
sendShortData(fnameBuf, true);
//"active"の場合別処理
String revokeType = file.getRevokeType();
if(revokeType.equals("active")){
//対称ユーザ名の送信
sendShortData(revokedBuf, true);
//新しい共通鍵の送信
PublicKey pubkey = pubkeyTable.get(file.getBackupHost());
Cipher wcipher = Cipher.getInstance(pubAlg);
wcipher.init(Cipher.WRAP_MODE, pubkey);
byte[] encKey = wcipher.wrap(skey);
sendShortData(ByteBuffer.wrap(encKey), false);
}else{
//対称ユーザ名の送信
sendShortData(revokedBuf, true);
}
//"proposal"の場合
if(revokeType.equals("proposal") || revokeType.equals("proposal2")){
//このホスト名を送信
sendShortData(charset.encode(prop.getProperty("host")), false);
//終了通知を待つ
buf = ByteBuffer.allocate(3);
while(channel.read(buf) <= 0){}
buf.flip();
System.out.println(charset.decode(buf).toString());
//呼び出し元を再開
synchronized (this) {
notifyAll();
}
//新primaryの鍵を受信
buf = receiveShortData(true);
byte[] encKey = new byte[buf.remaining()];
buf.get(encKey);
channel.close();
//backupへ移動し再暗号化
if(revokeType.equals("proposal")){
new MoveToBackupThread(file, encKey, serverPrikey, serverPubkey, pubkeyTable, prop, backupFileStateTable).run();
}else if(revokeType.equals("proposal2")){
new MoveToOtherBackupThread(file, encKey, serverPrikey, serverPubkey, pubkeyTable, prop).run();
}
}else{
channel.close();
}
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
}
private ByteBuffer receiveShortData(boolean next) throws IOException {
return CommandHandler.receiveShortData(channel, next);
}
private void sendShortData(ByteBuffer buf, boolean next) throws IOException {
CommandHandler.sendShortData(channel, buf, next);
}
}
/**
* バックアップ側で，プライマリからアクセス権失効情報を受け取るクラス
* @author takayama
*
*/
public class ReceiveRevokeData {
private SocketChannel channel = null;
private Properties prop = null;
private PrivateKey serverPrikey = null;
private PublicKey serverPubkey = null;
private HashMap<String, PublicKey> pubkeyTable = null;
private Map<String, String> fileStateTable = null;
private Map<String, String> backupFileStateTable = null;
//コンストラクタ
public ReceiveRevokeData(SocketChannel channel, Properties prop, PrivateKey serverPrikey, PublicKey serverPubkey, HashMap<String, PublicKey> pubkeyTable, Map<String, String> fileStateTable, Map<String, String> backupFileStateTable) {
this.channel = channel;
this.prop = prop;
this.serverPrikey = serverPrikey;
this.serverPubkey = serverPubkey;
this.pubkeyTable = pubkeyTable;
this.fileStateTable = fileStateTable;
this.backupFileStateTable = backupFileStateTable;
}
public void run() {
String backup = prop.getProperty("backup");
String primary = prop.getProperty("primary");
String pubAlg = prop.getProperty("pubAlg");
String secAlg = prop.getProperty("secAlg");
long time = 0;
try {
//ファイル名の受信
time = System.currentTimeMillis();
ByteBuffer buf = receiveShortData(true);
Charset charset = Charset.forName("UTF-8");
String fileName = charset.decode(buf).toString();
System.out.println("receive fileName:" + (System.currentTimeMillis() - time));
//ロック処理
synchronized (backupFileStateTable) {
while(backupFileStateTable.get(fileName).equals("lock")){
try {
backupFileStateTable.wait();
} catch (InterruptedException e) {}
}
backupFileStateTable.put(fileName, "lock");
backupFileStateTable.notifyAll();
}
//ファイルオブジェクトの読み込み
time = System.currentTimeMillis();
String foPath = backup.concat(fileName).concat(".file");
ObjectInputStream obin = new ObjectInputStream(new FileInputStream(foPath));
FileObject file = (FileObject)obin.readObject();
obin.close();
System.out.println(file.keyTable);
System.out.println("read FileObject:" + (System.currentTimeMillis() - time));
//"active"の場合別処理
String revokeType = file.getRevokeType();
String revokedName = "";
if(revokeType.equals("active")){
//対称ユーザ名の受信
time = System.currentTimeMillis();
buf = receiveShortData(true);
revokedName = charset.decode(buf).toString();
file.removeKey(revokedName);
System.out.println("receive revokedName:" + (System.currentTimeMillis() - time));
//新しい共通鍵の受信
buf = receiveShortData(false);
byte[] encKey = new byte[buf.remaining()];
buf.get(encKey);
Cipher ucipher = Cipher.getInstance(pubAlg);
ucipher.init(Cipher.UNWRAP_MODE, serverPrikey);
SecretKey skey = (SecretKey)ucipher.unwrap(encKey, secAlg, Cipher.SECRET_KEY);
//再暗号化
file.reEncrypt(prop, skey, serverPrikey, serverPubkey, pubkeyTable, false);
}else{
//対称ユーザ名の受信
time = System.currentTimeMillis();
buf = receiveShortData(true);
revokedName = charset.decode(buf).toString();
file.removeKey(revokedName);
System.out.println("receive revokedName:" + (System.currentTimeMillis() - time));
//"lazy"の場合フラグ設定
if(revokeType.equals("lazy")){
file.setRevokeFlug(true);
}
}
//"proposal"の場合別処理
if(revokeType.equals("proposal") || revokeType.equals("proposal2")){         
//旧ホスト名の受信
buf = receiveShortData(false);
String oldHost = charset.decode(buf).toString();
//ファイルの移動（->移動なしに変更   ファイルオブジェクトのみ移動）
time = System.currentTimeMillis();
String newPath = primary.concat(fileName).concat(".crypt");
System.out.println("move file:" + (System.currentTimeMillis() - time));
//ファイルオブジェクトのpathの修正
time = System.currentTimeMillis();
System.out.println("set path:" + (System.currentTimeMillis() - time));
//ファイルオブジェクトの格納
time = System.currentTimeMillis();
new File(foPath).delete();
foPath = primary.concat(fileName).concat(".file");
ObjectOutputStream obout = new ObjectOutputStream(new FileOutputStream(foPath));
obout.writeObject(file);
obout.flush();
obout.close();
System.out.println("write fileObject:" + (System.currentTimeMillis() - time));
//fileTableを更新
time = System.currentTimeMillis();
synchronized (fileStateTable) {
fileStateTable.put(fileName, "");
fileStateTable.notifyAll();
}
System.out.println("update fileTable:" + (System.currentTimeMillis() - time));
//クライアントへfileTable修正データを送信（SimulationClient起動時のみ）
new SendFileTableChangeDataThread(fileName, oldHost, prop.getProperty("host"), prop).run();
//終了を通知
channel.write(charset.encode("end"));
//backupFileStateTableからfileNameを削除
synchronized (backupFileStateTable) {
backupFileStateTable.remove(fileName);
backupFileStateTable.notifyAll();
}
//新しい共通鍵をバックアップに送信
time = System.currentTimeMillis();
String backupHost = file.getBackupHost();
byte[] encKey = file.getKey("server");
Cipher cipher = Cipher.getInstance(pubAlg);
cipher.init(Cipher.UNWRAP_MODE, serverPrikey);
SecretKey skey = (SecretKey)cipher.unwrap(encKey, secAlg, Cipher.SECRET_KEY);
cipher.init(Cipher.WRAP_MODE, pubkeyTable.get(backupHost));
encKey = cipher.wrap(skey);
sendShortData(ByteBuffer.wrap(encKey), true);
//SocketChannel sc = socketTable.get(backupHost);
System.out.println("send encKey:" + (System.currentTimeMillis() - time));
}else{
ObjectOutputStream obout = new ObjectOutputStream(new FileOutputStream(foPath));
obout.writeObject(file);
obout.flush();
obout.close();
//ロック解除
synchronized (backupFileStateTable) {
backupFileStateTable.put(fileName, "");
backupFileStateTable.notifyAll();
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
}
private void sendShortData(ByteBuffer buf, boolean next) throws IOException {
CommandHandler.sendShortData(channel, buf, next);
}
private ByteBuffer receiveShortData(boolean next) throws IOException {
return CommandHandler.receiveShortData(channel, next);
}
}
/**
* RBA-Revで元プライマリをバックアップへ転送するクラス
* @author takayama
*
*/
public class MoveToOtherBackupThread implements Runnable {
private FileObject file = null;
private byte[] encCurrentKey = null;
private PrivateKey serverPrikey = null;
private PublicKey serverPubkey = null;
private HashMap<String, PublicKey> pubkeyTable = null;
private Properties prop = null;
//コンストラクタ
public MoveToOtherBackupThread(FileObject file, byte[] encCurrentKey, PrivateKey serverPrikey, PublicKey serverPubkey, HashMap<String, PublicKey> pubkeyTable, Properties prop) {
this.file = file;
this.encCurrentKey = encCurrentKey;
this.serverPrikey = serverPrikey;
this.serverPubkey = serverPubkey;
this.pubkeyTable = pubkeyTable;
this.prop = prop;         
}
public void run() {
try {
String transform = prop.getProperty("transform");
String secAlg = prop.getProperty("secAlg");
int secLen = Integer.parseInt(prop.getProperty("secLen"));
String pubAlg = prop.getProperty("pubAlg");
String migHost = prop.getProperty("migrationHost");
String primary = prop.getProperty("primary");
int port = Integer.parseInt(prop.getProperty("port"));
String path = file.getPath();
//共通鍵の読み出し
byte[] encOldKey = file.getKey("server");
Cipher ucipher = Cipher.getInstance(pubAlg);
ucipher.init(Cipher.UNWRAP_MODE, serverPrikey);
SecretKey oldKey = (SecretKey)ucipher.unwrap(encOldKey, secAlg, Cipher.SECRET_KEY);
//ファイルの読み出し
FileChannel input = new FileInputStream(path).getChannel();
ByteBuffer encFileBuf = ByteBuffer.allocate((int)input.size());
input.read(encFileBuf);
input.close();
encFileBuf.flip();
//ファイルの削除
new File(path).delete();
String foPath = primary.concat(file.getFileName()).concat(".file");
new File(foPath).delete();
//ファイルの復号
Cipher cipher = Cipher.getInstance(transform);
cipher.init(Cipher.DECRYPT_MODE, oldKey);
ByteBuffer fileBuf = ByteBuffer.allocate(cipher.getOutputSize(encFileBuf.limit()));
cipher.doFinal(encFileBuf, fileBuf);
fileBuf.flip();
encFileBuf.clear();
//共通鍵の生成
KeyGenerator keygen = KeyGenerator.getInstance(secAlg);
keygen.init(secLen);
SecretKey newKey = keygen.generateKey();
//ファイルの再暗号化
cipher.init(Cipher.ENCRYPT_MODE, newKey);
cipher.doFinal(fileBuf, encFileBuf);
encFileBuf.flip();
//共通鍵のラップ
PublicKey migPubkey = pubkeyTable.get(migHost);
Cipher wcipher = Cipher.getInstance(pubAlg);
wcipher.init(Cipher.WRAP_MODE, migPubkey);
byte[] encNewKey = wcipher.wrap(newKey);
//currentKeyを格納
file.addKey("current", encCurrentKey);
//ソケットチャネル生成
SocketChannel channel = SocketChannel.open(new InetSocketAddress(migHost, port));
//コマンド送信，ack待ち
Charset charset = Charset.forName("UTF-8");
channel.write(charset.encode("moveBackup"));
ByteBuffer buf = ByteBuffer.allocate(3);
while(channel.read(buf) <= 0){}
System.out.println("copy backup: " + file.getFileName() + ">" + migHost);
//共通鍵とファイル送信
buf = ByteBuffer.allocate(encNewKey.length + encFileBuf.limit());
buf.put(encNewKey);
buf.put(encFileBuf);
buf.flip();
sendData(channel, buf, true);
//ファイルオブジェクト送信
ByteArrayOutputStream baout = new ByteArrayOutputStream();
ObjectOutputStream obout = new ObjectOutputStream(baout);
obout.writeObject(file);
buf = ByteBuffer.wrap(baout.toByteArray());
sendData(channel, buf, true);
obout.flush();
obout.close();
baout.close();
channel.close();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (ShortBufferException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
}
private void sendData(SocketChannel channel, ByteBuffer buf, boolean next) throws IOException {
CommandHandler.sendData(channel, buf, next);
}
}
/**
* BA-Revで元プライマリをバックアップへ移動するクラス
* @author takayama
*
*/
public class MoveToBackupThread implements Runnable {
private FileObject file = null;
private byte[] encKey = null;
private PrivateKey serverPrikey = null;
private PublicKey serverPubkey = null;
private HashMap<String, PublicKey> pubkeyTable = null;
private Properties prop = null;
private Map<String, String> backupFileStateTable = null;
public MoveToBackupThread(FileObject file, byte[] encKey, PrivateKey serverPrikey, PublicKey serverPubkey, HashMap<String, PublicKey> pubkeyTable, Properties prop, Map<String, String> backupFileStateTable) {
this.file = file;
this.encKey = encKey;
this.serverPrikey = serverPrikey;
this.serverPubkey = serverPubkey;
this.pubkeyTable = pubkeyTable;
this.prop = prop;
this.backupFileStateTable = backupFileStateTable;
}
public void run() {
try {
String fileName = file.getFileName();
//ロック
synchronized (backupFileStateTable) {
backupFileStateTable.put(fileName, "lock");
}
//再暗号化しbackupに格納
file.reEncrypt(prop, null, serverPrikey, serverPubkey, pubkeyTable, true);
//新primaryの共通鍵を登録
file.addKey("current", encKey);
//ファイルオブジェクトを移動
String oldFOPath = prop.getProperty("primary").concat(fileName).concat(".file");
String newFOPath = prop.getProperty("backup").concat(fileName).concat(".file");
new File(oldFOPath).delete();
ObjectOutputStream obout;
obout = new ObjectOutputStream(new FileOutputStream(newFOPath));
obout.writeObject(file);
obout.flush();
obout.close();
//ロック解除
synchronized (backupFileStateTable) {
backupFileStateTable.put(fileName, "");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* RBA-Revにおいて，revocation時にbackupを受信するクラス
* @author takayama
*
*/
public class ReceiveMoveBackupData {
private SocketChannel channel = null;
private Properties prop = null;
private PrivateKey serverPrikey = null;
private PublicKey serverPubkey = null;
private HashMap<String, PublicKey> pubkeyTable = null;
private Map<String, String> backupFileStateTable = null;
public ReceiveMoveBackupData(SocketChannel channel, Properties prop, PrivateKey serverPrikey, PublicKey serverPubkey, HashMap<String, PublicKey> pubkeyTable, Map<String, String> backupFileStateTable) {
this.channel = channel;
this.prop = prop;
this.serverPrikey = serverPrikey;
this.serverPubkey = serverPubkey;
this.pubkeyTable = pubkeyTable;
this.backupFileStateTable = backupFileStateTable;
}
public void run() {
try {
int secLen = Integer.parseInt(prop.getProperty("secLen"));
String pubAlg = prop.getProperty("pubAlg");
String secAlg = prop.getProperty("secAlg");
String backup = prop.getProperty("backup");
String backupHost = prop.getProperty("backupHost");
//共通鍵のファイルを受信
ByteBuffer buf = receiveData(true);
//ファイルオブジェクトの受信
ByteBuffer foBuf = receiveData(true);
ByteArrayInputStream bain = new ByteArrayInputStream(foBuf.array());
ObjectInputStream obin = new ObjectInputStream(bain);
FileObject file = (FileObject)obin.readObject();
//共通鍵の取り出し
byte[] encNewKey = new byte[secLen];
buf.get(encNewKey);
Cipher ucipher = Cipher.getInstance(pubAlg);
ucipher.init(Cipher.UNWRAP_MODE, serverPrikey);
SecretKey newKey = (SecretKey)ucipher.unwrap(encNewKey, secAlg, Cipher.SECRET_KEY);
//ファイルの格納
String fileName = file.getFileName();
String path = backup.concat(fileName).concat(".crypt");
FileChannel output = new FileOutputStream(path).getChannel();
output.write(buf);
output.close();
//ファイルオブジェクトの修正
file.setBackupHost(backupHost);
file.setPath(path);
file.updateTable(newKey, encNewKey, serverPubkey, pubkeyTable, pubAlg);
//ファイルオブジェクトの格納
String foPath = backup.concat(fileName).concat(".file");
ObjectOutputStream obout = new ObjectOutputStream(new FileOutputStream(foPath));
obout.writeObject(file);
obout.flush();
obout.close();
//backupFileStateTableへ登録
synchronized (backupFileStateTable) {
backupFileStateTable.put(fileName, "");
backupFileStateTable.notifyAll();
}
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private ByteBuffer receiveData(boolean next) throws IOException {
return CommandHandler.receiveData(channel, next);
}
}
/**
* ファイル位置移動後，その情報を各クライアントに通知するクラス
* @author takayama
*
*/
public class SendFileTableChangeDataThread implements Runnable {
private String fileName = "";
private String oldHost = "";
private String newHost = "";
private Properties prop = null;
public SendFileTableChangeDataThread(String fileName, String oldHost, String newHost, Properties prop) {
this.fileName = fileName;
this.oldHost = oldHost;
this.newHost = newHost;
this.prop = prop;
}
public void run() {
try {
int port = Integer.parseInt(prop.getProperty("port"));
List<String> clientList = Arrays.asList(prop.getProperty("clientList").split(":"));
Iterator<String> it = clientList.iterator();
while(it.hasNext()){
String client = it.next();
//SocketChannelの生成
SocketChannel channel = SocketChannel.open(new InetSocketAddress(client, port));
//要求の送信
CommandHandler.commandSet("changeTable", channel);
//ファイル名の送信
Charset charset = Charset.forName("UTF-8");
CommandHandler.sendShortData(channel, charset.encode(fileName), true);
//旧ホスト名を送信
CommandHandler.sendShortData(channel, charset.encode(oldHost), true);
//新ホスト名を送信
CommandHandler.sendShortData(channel, charset.encode(newHost), false);
channel.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class UpdateCommandHandler3 extends CommandHandler {
private SocketChannel channel = null;
private Properties prop = null;
private String userName = null;
private PrivateKey serverPrikey = null;
private PublicKey  serverPubkey = null;
private HashMap<String, PublicKey> pubkeyTable = null;
private Map<String, String> fileStateTable = null;
public UpdateCommandHandler3(SocketChannel channel, Properties prop, String userName, PrivateKey serverPrikey, PublicKey serverPubkey, HashMap<String, PublicKey> pubkeyTable, Map<String, String> fileStateTable) {
this.channel = channel;
this.prop = prop;
this.userName = userName;
this.serverPrikey = serverPrikey;
this.serverPubkey = serverPubkey;
this.pubkeyTable = pubkeyTable;
this.fileStateTable = fileStateTable;
}
@Override
public void execute() {
try {
String primary = prop.getProperty("primary");
String secAlg = prop.getProperty("secAlg");
int secLen = Integer.parseInt(prop.getProperty("secLen"));
String pubAlg = prop.getProperty("pubAlg");
String transform = prop.getProperty("transform");
//ファイル名の受信
ByteBuffer fnameBuf = receiveShortData(channel, false);
Charset charset = Charset.forName("UTF-8");
String fileName = charset.decode(fnameBuf).toString();
fnameBuf.rewind();
System.out.println("UpdateCommandHandler3:63 " + fileName);
synchronized (fileStateTable) {
//ロック解除まで待つ
while(fileStateTable.get(fileName).equals("lock")){
try {
fileStateTable.wait();
} catch (InterruptedException e) {}
}
//状態がmove:…ならばrevoke処理が完了するまで待ち，再実行要求を出す
String state = fileStateTable.get(fileName);
if(state.startsWith("move")){
String newHost = state.split(":")[1];
sendData(channel, charset.encode("mov:".concat(newHost).concat(":")), false);
System.out.println("error: " + fileName + " move to " + newHost);
fileStateTable.notifyAll();
return;
}
fileStateTable.put(fileName, "lock");
fileStateTable.notifyAll();
}
//ファイルオブジェクトの読み込み
String foPath = primary.concat(fileName).concat(".file");
ObjectInputStream obinput = new ObjectInputStream(new FileInputStream(foPath));
FileObject file = (FileObject)obinput.readObject();
obinput.close();
//アクセス権判定
if(!file.canAccess(userName)){
sendData(channel, charset.encode("401"), false);
synchronized (fileStateTable) {
fileStateTable.put(fileName, "");
fileStateTable.notifyAll();
}
return;
}
//鍵の送信（lazyなら新しく生成）
SecretKey skey = null;
byte[] encKey = null;
if(file.revokeType.equals("lazy") && file.getRevokeFlug()){
KeyGenerator keygen = KeyGenerator.getInstance(secAlg);
keygen.init(secLen);
skey = keygen.generateKey();
Cipher wcipher = Cipher.getInstance(pubAlg);
wcipher.init(Cipher.WRAP_MODE, pubkeyTable.get(userName));
encKey = wcipher.wrap(skey);
}else{
encKey = file.getKey(userName);
}
ByteBuffer buf = ByteBuffer.wrap(encKey);
System.out.println(buf);
System.out.println(encKey);
sendData(channel, buf, false);
//差分データ受信
ByteBuffer diffBuf = receiveData(channel, false);
//ファイルの読み込み
String path = file.getPath();
FileChannel input = new FileInputStream(path).getChannel();
ByteBuffer encFileBuf = ByteBuffer.allocate((int)input.size());
input.read(encFileBuf);
encFileBuf.flip();
input.close();
String backupHost = file.getBackupHost();
//lazyのみ再暗号化
//file.reEncrypt(prop, skey, serverPrikey, serverPubkey, pubkeyTable, false);
if(file.getRevokeType().equals("lazy") && file.getRevokeFlug()){
//バックアップへのデータ送信
Cipher wcipher = Cipher.getInstance(pubAlg);
wcipher.init(Cipher.WRAP_MODE, pubkeyTable.get(backupHost));
new Thread(new SendUpdateData("update3", backupHost, fnameBuf, diffBuf.duplicate(), prop, wcipher.wrap(skey))).start();
//現在の鍵の取り出し
Cipher ucipher = Cipher.getInstance(pubAlg);
ucipher.init(Cipher.UNWRAP_MODE, serverPrikey);
SecretKey oldKey = (SecretKey)ucipher.unwrap(file.getKey("server"), secAlg, Cipher.SECRET_KEY);
//復号(部分)
Cipher cipher = Cipher.getInstance(transform);
cipher.init(Cipher.DECRYPT_MODE, oldKey);
encFileBuf.position(diffBuf.remaining()-16);
ByteBuffer pbuf = ByteBuffer.allocate(cipher.getOutputSize(encFileBuf.remaining()));
cipher.doFinal(encFileBuf, pbuf);
pbuf.flip();
encFileBuf.clear();
//再暗号化
cipher.init(Cipher.ENCRYPT_MODE, skey);
ByteBuffer cbuf = ByteBuffer.allocate(cipher.getOutputSize(pbuf.remaining()));
cipher.doFinal(pbuf, cbuf);
cbuf.flip();
//差分データのパディング削除
diffBuf.limit(diffBuf.limit() - 16);
//差分適用
encFileBuf.put(diffBuf);
encFileBuf.put(cbuf);
encFileBuf.flip();
//フラグリセット
file.setRevokeFlug(false);
//keyTableの更新
file.updateTable(skey, null, serverPubkey, pubkeyTable, pubAlg);
}else{
//backupへデータ送信
new Thread(new SendUpdateData("update3", backupHost, fnameBuf, diffBuf.duplicate(), prop, null)).start();
//パディング部分の削除
diffBuf.limit(diffBuf.limit() - 16);
//差分の適用
encFileBuf.put(diffBuf);
encFileBuf.rewind();
}
//ファイル格納
FileChannel output = new FileOutputStream(path).getChannel();
output.write(encFileBuf);
output.close();
//ファイルオブジェクト格納
ObjectOutputStream obout = new ObjectOutputStream(new FileOutputStream(foPath));
obout.writeObject(file);
obout.flush();
obout.close();
//ロック解除
synchronized (fileStateTable) {
fileStateTable.put(fileName, "");
fileStateTable.notifyAll();
}
//response
String response = "Update is done.";
sendShortData(channel, charset.encode(response), false);
System.out.println(response);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (ShortBufferException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
}
}
/**
* プライマリ側で，バックアップに更新情報を送信するクラス
* @author takayama
*
*/
public class SendUpdateData implements Runnable {
private SocketChannel channel = null;
private String cmd = "";
private String backupHost = "";
private ByteBuffer fnameBuf = null;
private ByteBuffer encDataBuf = null;
private byte[] encKey = null;
public SendUpdateData(String cmd, String backupHost, ByteBuffer fnameBuf, ByteBuffer encDataBuf, Properties prop, byte[] encKey) {
this.cmd = cmd;
this.backupHost = backupHost;
this.fnameBuf = fnameBuf;
this.encDataBuf = encDataBuf;
this.encKey = encKey;
try {
channel = SocketChannel.open(new InetSocketAddress(backupHost, Integer.parseInt(prop.getProperty("port"))));
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
try {
//コマンドの送信
Charset charset = Charset.forName("UTF-8");
channel.write(charset.encode(cmd));
//ackを待つ
ByteBuffer buf = ByteBuffer.allocate(3);
channel.read(buf);
System.out.println("send update data > " + backupHost);
//ファイル名の送信
sendShortData(fnameBuf, true);
//更新データの送信
sendData(encDataBuf, true);
//"lazy"でrevocation後なら共通鍵を送信
if(encKey != null){
ByteBuffer keyBuf = ByteBuffer.wrap(encKey);
sendData(keyBuf, false);
}
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendData(ByteBuffer buf, boolean next) throws IOException {
CommandHandler.sendData(channel, buf, next);
}
private void sendShortData(ByteBuffer buf, boolean next) throws IOException {
CommandHandler.sendShortData(channel, buf, next);
}
}
/**
* バックアップ側で，更新情報を受け取るクラス
* @author takayama
*
*/
public class ReceiveUpdateData3 {
private SocketChannel channel = null;
private Properties prop = null;
private PrivateKey serverPrikey = null;
private PublicKey serverPubkey = null;
private HashMap<String, PublicKey> pubkeyTable = null;
private Map<String, String> backupFileStateTable = null;
public ReceiveUpdateData3(SocketChannel channel, Properties prop, PrivateKey serverPrikey, PublicKey serverPubkey, HashMap<String, PublicKey> pubkeyTable, Map<String, String> backupFileStateTable) {
this.channel = channel;
this.prop = prop;
this.serverPrikey = serverPrikey;
this.serverPubkey = serverPubkey;
this.pubkeyTable = pubkeyTable;
this.backupFileStateTable = backupFileStateTable;
}
public void run() {
try {
String backup = prop.getProperty("backup");
String pubAlg = prop.getProperty("pubAlg");
String secAlg = prop.getProperty("secAlg");
int secLen = Integer.parseInt(prop.getProperty("secLen"));
String transform = prop.getProperty("transform");
//ファイル名の受信
ByteBuffer buf = receiveShortData(true);
Charset charset = Charset.forName("UTF-8");
String fileName = charset.decode(buf).toString();
//差分データの受信
ByteBuffer diffBuf = receiveData(true);
//ロック
synchronized (backupFileStateTable) {
while(!backupFileStateTable.containsKey(fileName)){      //proposal revocation 直後でbackupがまだできてなかったら待つ．
try {
backupFileStateTable.wait();
} catch (InterruptedException e) {}
}
System.out.println("ReceiveUpdateData:70 " + fileName);
while(backupFileStateTable.get(fileName).equals("lock")){
try {
backupFileStateTable.wait();
} catch (InterruptedException e) {}
}
backupFileStateTable.put(fileName, "lock");
backupFileStateTable.notifyAll();
}
//ファイルオブジェクトとファイルの読み込み
String foPath = backup.concat(fileName).concat(".file");
ObjectInputStream obin = new ObjectInputStream(new FileInputStream(foPath));
FileObject file = (FileObject)obin.readObject();
obin.close();
String path = file.getPath();
FileChannel input = new FileInputStream(path).getChannel();
ByteBuffer encFileBuf = ByteBuffer.allocate((int)input.size());
input.read(encFileBuf);
encFileBuf.flip();
//"lazy"でrevocation後なら共通鍵を受信し再暗号化
String revokeType = file.getRevokeType();
SecretKey newLazyKey = null;
byte[] encNewLazyKey = null;
Cipher ucipher = Cipher.getInstance(pubAlg);
ByteBuffer cbuf = null;
ucipher.init(Cipher.UNWRAP_MODE, serverPrikey);
if(revokeType.equals("lazy") && file.revokeFlug){
//鍵の受信
ByteBuffer keyBuf = receiveData(false);
System.out.println(keyBuf);
encNewLazyKey = new byte[secLen];
keyBuf.get(encNewLazyKey);
newLazyKey = (SecretKey) ucipher.unwrap(encNewLazyKey, secAlg, Cipher.SECRET_KEY);
//現在の鍵の取り出し
SecretKey oldKey = (SecretKey)ucipher.unwrap(file.getKey("server"), secAlg, Cipher.SECRET_KEY);
//復号（部分）
Cipher cipher = Cipher.getInstance(transform);
cipher.init(Cipher.DECRYPT_MODE, oldKey);
encFileBuf.position(diffBuf.remaining()-16);
ByteBuffer pbuf = ByteBuffer.allocate(cipher.getOutputSize(encFileBuf.remaining()));
cipher.doFinal(encFileBuf, pbuf);
pbuf.flip();
encFileBuf.clear();
//再暗号化
cipher.init(Cipher.ENCRYPT_MODE, newLazyKey);
cbuf = ByteBuffer.allocate(cipher.getOutputSize(pbuf.remaining()));
cipher.doFinal(pbuf, cbuf);
cbuf.flip();
//フラグリセット
file.setRevokeFlug(false);
//keyTableの更新
file.updateTable(newLazyKey, null, serverPubkey, pubkeyTable, pubAlg);
}
//proposalなら差分を再暗号化
if(revokeType.equals("proposal") || revokeType.equals("proposal2")){
//復号
SecretKey currentKey = (SecretKey)ucipher.unwrap(file.getKey("current"), secAlg, Cipher.SECRET_KEY);
Cipher cipher = Cipher.getInstance(transform);
cipher.init(Cipher.DECRYPT_MODE, currentKey);
ByteBuffer pdiffBuf = ByteBuffer.allocate(cipher.getOutputSize(diffBuf.limit()));
cipher.doFinal(diffBuf, pdiffBuf);
pdiffBuf.flip();
diffBuf.clear();
//暗号化
SecretKey nextKey = (SecretKey)ucipher.unwrap(file.getKey("server"), secAlg, Cipher.SECRET_KEY);
cipher.init(Cipher.ENCRYPT_MODE, nextKey);
cipher.doFinal(pdiffBuf, diffBuf);
diffBuf.flip();
}
//差分データのパディング削除
diffBuf.limit(diffBuf.limit() - 16);
if(revokeType.equals("lazy") && file.revokeFlug){
//差分適用
encFileBuf.put(diffBuf);
encFileBuf.put(cbuf);
encFileBuf.flip();
}else{
//差分の適用
encFileBuf.put(diffBuf);
encFileBuf.rewind();
}
//ファイル格納
FileChannel output = new FileOutputStream(path).getChannel();
output.write(encFileBuf);
output.close();
//ファイルオブジェクト格納
ObjectOutputStream obout = new ObjectOutputStream(new FileOutputStream(foPath));
obout.writeObject(file);
obout.flush();
obout.close();
//ロックの解除
synchronized (backupFileStateTable) {
backupFileStateTable.put(fileName, "");
backupFileStateTable.notifyAll();
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (ShortBufferException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
}
private ByteBuffer receiveData(boolean next) throws IOException {
return CommandHandler.receiveData(channel, next);
}
private ByteBuffer receiveShortData(boolean next) throws IOException {
return CommandHandler.receiveShortData(channel, next);
}
}
/**
* 設定ファイル生成クラス
* @author takayama
*
*/
public class EncOnServerProperties {
//このサーバ名
public static final String HOST = "adisk129";
//backupを置くサーバ
public static final String BACKUP_HOST = "adisk130";
//このディスクにあるbackupのprimaryがあるホスト
public static final String PRIMARY_HOST = "adisk131";
//全サーバリスト(HOST1:HOST2:・・・)
public static final String SERVER_LIST = "adisk129:adisk130:adisk131";
//接続サーバリスト
//public static final String CONNECT_LIST = BACKUP_HOST.concat(":").concat(PRIMARY_HOST);
public static final String CONNECT_LIST = SERVER_LIST;
//接続クライアントリスト
public static final String CLIENT_LIST = "adisk132:adisk133";
//（"proposal2"における）マイグレーション先ホスト
public static final String MIGRATION_HOST = "adisk131";
//ポート番号
public static final String PORT = "50000";
//公開鍵暗号アルゴリズム
public static final String PUB_ALG = "RSA";
//公開鍵暗号の鍵長
public static final String PUB_LEN = "1024";
//共通鍵暗号アルゴリズム
public static final String SEC_ALG = "AES";
//共通鍵暗号の鍵長
public static final String SEC_LEN = "128";
//ファイル暗号化の変換方式
public static final String TRANSFORM = SEC_ALG.concat("/ECB/PKCS5Padding");
//revocationのタイプ（active, lazy, proposal）
public static final String REVOKE_TYPE = "active";
//primary
public static final String PRIMARY = "./primary/";
//backup
public static final String BACKUP = "./backup/";
public static void main(String[] args) {
Properties prop = new Properties();
prop.setProperty("host", HOST);
prop.setProperty("backupHost", BACKUP_HOST);
prop.setProperty("primaryHost", PRIMARY_HOST);
prop.setProperty("serverList", SERVER_LIST);
prop.setProperty("connectList", CONNECT_LIST);
prop.setProperty("clientList", CLIENT_LIST);
prop.setProperty("migrationHost", MIGRATION_HOST);
prop.setProperty("port", PORT);
prop.setProperty("pubAlg", PUB_ALG);
prop.setProperty("pubLen", PUB_LEN);
prop.setProperty("secAlg", SEC_ALG);
prop.setProperty("secLen", SEC_LEN);
prop.setProperty("transform", TRANSFORM);
prop.setProperty("revokeType", REVOKE_TYPE);
prop.setProperty("primary", PRIMARY);
prop.setProperty("backup", BACKUP);
FileOutputStream output;
try {
output = new FileOutputStream("./encrypt_on_disk_server/EncOnDiskServer.properties");
prop.store(output, "EncryptOnDiskServer");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
【００５４】
エンクリプト・オン・ディスク方式のクライアントプログラム：
package encrypt_on_disk_client;
import java.io.*;
import java.security.*;
import java.nio.*;
import java.util.*;
import javax.crypto.*;
/**
* クライアント側メインクラス
* @author takayama
*
*/
public class EncryptOnDiskClient {
private Properties prop = new Properties();
private int port = -1;
private String pubAlg = "";
private int pubLen = -1;
private PrivateKey prikey = null;
private PublicKey pubkey = null;
private HashMap<String, PublicKey> pubkeyTable = new HashMap<String, PublicKey>();
private HashMap<String, SecretKey> filekeyTable = new HashMap<String, SecretKey>();
private String userName = "";
public EncryptOnDiskClient(String name) {
try {
userName = name;
//設定ファイルの読み込み
FileInputStream input = new FileInputStream("/exp/takayama/EncOnDiskClient.properties");
prop.load(input);
port = Integer.parseInt(prop.getProperty("port"));
pubAlg = prop.getProperty("pubAlg");
pubLen = Integer.parseInt(prop.getProperty("pubLen"));
//公開鍵の生成
KeyPairGenerator kpg = KeyPairGenerator.getInstance(pubAlg);
kpg.initialize(pubLen);
KeyPair keys = kpg.generateKeyPair();
prikey = keys.getPrivate();
pubkey = keys.getPublic();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.print("user name: ");
try {
String name = in.readLine();
new EncryptOnDiskClient(name).run();
} catch (IOException e) {
e.printStackTrace();
}
}
private void run() {
try {              
//コマンドの読み込み
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
Command command = null;
while(true){
System.out.print("command> ");
String[] cmd = in.readLine().split(" ");
if(cmd[0].startsWith("e") && cmd.length == 1){
return;
}else if(cmd[0].equals("setpubkey") && cmd.length == 1){
command = new SetPubkeyCommand(pubkeyTable, pubkey, userName, prop);
}else if(cmd[0].equals("put") && cmd.length == 3){
command = new PutCommand(cmd[1], cmd[2], cmd[2], pubkeyTable.get(cmd[1]), prop, userName, filekeyTable);
}else if(cmd[0].equals("get") && cmd.length == 3){
command = new GetCommand(cmd[1], userName, cmd[2], "", prikey, filekeyTable, prop, null, null, false);
}else if(cmd[0].equals("get") && cmd.length == 4){
if(filekeyTable.containsKey(cmd[3])){
command = new GetCommand(cmd[2], userName, cmd[3], cmd[1], prikey, filekeyTable, prop, null, null, false);
}else{
System.out.println("error: you don't have the secret key for " + cmd[3]);
continue;
}
}else if(cmd[0].equals("auth") && cmd.length == 4){
command = new AuthCommand(cmd[1], userName, cmd[2], cmd[3], prop);
}else if(cmd[0].equals("revoke") && cmd.length == 4){
command = new RevokeCommand(cmd[1], userName, cmd[2], cmd[3], prop);
}else if(cmd[0].equals("update2") && cmd.length == 5){
if(filekeyTable.containsKey(cmd[2])){
command = new UpdateCommand2(cmd[1], userName, cmd[2], cmd[3], cmd[4], filekeyTable, prop);
}else{
System.out.println("error: have not the secret key for " + cmd[2]);
continue;
}
}else if(cmd[0].equals("update") && cmd.length == 5){
if(filekeyTable.containsKey(cmd[2])){
command = new UpdateCommand(cmd[1], userName, cmd[2], cmd[3], cmd[4], filekeyTable, prop, null);
}else{
System.out.println("error: have not the secret key for " + cmd[2]);
continue;
}
}else if(cmd[0].equals("update3") && cmd.length == 4){
command = new UpdateCommand3(cmd[1], cmd[2], cmd[3], userName, filekeyTable, prop, pubkeyTable, prikey, null, null, false);
}else{
System.out.println("comamnd: not found.");
continue;
}
long time = System.currentTimeMillis();
command.run();
System.out.println("execute time: " + (System.currentTimeMillis() - time) + "ms");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 各コマンド実行クラスにより継承される，抽象クラス
* @author takayama
*
*/
public abstract class Command {
public abstract void run();
/**
* 大きなサイズのデータを送信するためのメソッド
* @param channel
* @param buf
* @param next
* @throws IOException
*/
public static void sendData(SocketChannel channel, ByteBuffer buf, boolean next) throws IOException{  
//要素数を送信
ByteBuffer count = ByteBuffer.allocate(8).putInt(buf.remaining());
count.flip();
channel.write(count);
//Ackを待つ
ByteBuffer ack = ByteBuffer.allocate(3);
channel.read(ack);
ack.clear();
//本体の送信
while(buf.remaining() > 0){
channel.write(buf);
}
//次もsendDataならAckを待つ
if(next){
channel.read(ack);
}
return;
}
/**
* 大きなデータを受信する為のメソッド
* @param channel
* @param next
* @return 受信したByteBuffer
* @throws IOException
*/
public static ByteBuffer receiveData(SocketChannel channel, boolean next) throws IOException{
ByteBuffer count = ByteBuffer.allocate(8);
Charset charset = Charset.forName("UTF-8");
//要素数の受信
while(channel.read(count) <= 0){};
count.flip();
int size = count.getInt();
//Ackを返す
channel.write(charset.encode("ack"));         
//本体の受信
ByteBuffer buf = ByteBuffer.allocate(size);
while(buf.remaining() > 0){
channel.read(buf);
}
buf.flip();
//次もreceiveDataを実行するならAckを返す
if(next){
channel.write(charset.encode("ack"));
}
return buf;
}
/**
* Stringデータを送信するメソッド
* @param channel
* @param buf
* @param next
* @throws IOException
*/
public static void sendShortData(SocketChannel channel, ByteBuffer buf, boolean next) throws IOException{
//データの送信
channel.write(buf);
//次も送信処理ならackを待つ
ByteBuffer ack = ByteBuffer.allocate(3);
if(next){
channel.read(ack);
}
return;
}
/**
* サイズの小さいデータ（文字列：256以下，共通鍵128，公開鍵162）を受信するメソッド
* @param channel
* @param next
* @return 受信したByteBuffer
* @throws IOException
*/
public static ByteBuffer receiveShortData(SocketChannel channel, boolean next) throws IOException{
ByteBuffer buf = ByteBuffer.allocate(256);
Charset charset = Charset.forName("UTF-8");
//本体の受信
channel.read(buf);
buf.flip();
//次もreceiveDataを実行するならAckを返す
if(next){
channel.write(charset.encode("ack"));
}
return buf;
}
/**
* コマンドを通信先に送信するメソッド
* @param cmd
* @param channel
* @throws IOException
*/
public static void commandSet(String cmd, SocketChannel channel) throws IOException {
Charset charset = Charset.forName("UTF-8");
channel.write(charset.encode(cmd));
//ackを待つ
ByteBuffer buf = ByteBuffer.allocate(3);
channel.read(buf);
}
/**
* サーバと接続し，userNameを送信するメソッド
* @param string
* @return SocketChannel
* @throws IOException 
*/
public static SocketChannel connectServer(String server, String userName, int port) throws IOException {
//接続
SocketChannel channel = SocketChannel.open(new InetSocketAddress(server, port));
//ユーザ名を送信
Charset charset = Charset.forName("UTF-8");
Command.sendShortData(channel, charset.encode(userName), true);
return channel;
}
}
/**
* getリクエストを実行するクラス
* @author takayama
*
*/
public class GetCommand extends Command implements Runnable{
private SocketChannel channel = null;
private String server = "";
private String fileName = "";
private String option = "";
private PrivateKey prikey = null;
private Map<String, SecretKey> filekeyTable = null;
private Properties prop = null;
private String userName = "";
private BufferedWriter logOutput = null;
private boolean isOtherDisk = false;
private long startTime = -1;
private String downFilePath = "";
private String pubAlg = "";
private String secAlg = "";
private int secLen = -1;
private String transform = "";
private BufferedWriter oLogOutput;
//コンストラクタ
public GetCommand(String server, String userName, String fileName, String option, PrivateKey prikey, Map<String, SecretKey> filekeyTable, Properties prop, BufferedWriter logOutput, BufferedWriter oLogOutput, boolean isOtherDisk) {
this.server = server;
this.userName = userName;
this.fileName = fileName;
this.option = option;
this.prikey = prikey;
this.filekeyTable = filekeyTable;
this.prop = prop;
this.logOutput = logOutput;
this.oLogOutput = oLogOutput;
this.isOtherDisk = isOtherDisk;
downFilePath = prop.getProperty("downFilePath");
pubAlg = prop.getProperty("pubAlg");
secAlg = prop.getProperty("secAlg");
secLen = Integer.parseInt(prop.getProperty("secLen"));
transform = prop.getProperty("transform");
}
public GetCommand(String server, String userName, String fileName, String option, PrivateKey prikey, Map<String, SecretKey> filekeyTable, Properties prop, BufferedWriter logOutput, BufferedWriter oLogOutput, boolean isOtherDisk, long startTime) {
this.server = server;
this.userName = userName;
this.fileName = fileName;
this.option = option;
this.prikey = prikey;
this.filekeyTable = filekeyTable;
this.prop = prop;
this.logOutput = logOutput;
this.oLogOutput = oLogOutput;
this.isOtherDisk = isOtherDisk;
this.startTime = startTime;
downFilePath = prop.getProperty("downFilePath");
pubAlg = prop.getProperty("pubAlg");
secAlg = prop.getProperty("secAlg");
secLen = Integer.parseInt(prop.getProperty("secLen"));
transform = prop.getProperty("transform");          
}
@Override
public void run() {
//開始時刻
System.out.println(startTime);
long start = -1;
if(startTime == -1){
start = System.currentTimeMillis();
}else{
start = startTime;
}
//SocketChannelの生成
try {
channel = connectServer(server, userName, Integer.parseInt(prop.getProperty("port")));
} catch (NumberFormatException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
//鍵保有の判定
SecretKey skey = null;
if(option.equals("-k")){
skey = filekeyTable.get(fileName);
}
//中間時刻
long check1 = System.currentTimeMillis();
long check2 = 0;
long check3 = 0;
long check4 = 0;
try {
//コマンド送信
commandSet("get", channel);
int i;
for(i=0; i < 1; i++){
//ファイル名の送信
Charset charset = Charset.forName("UTF-8");
sendShortData(channel, charset.encode(fileName), true);
//中間時刻
check2 = System.currentTimeMillis();
//共通鍵転送の有無により別処理
ByteBuffer encFileBuf = null;
if(skey == null){
//共通鍵の送信を要求
sendShortData(channel, charset.encode("true"), false);
//共通鍵とファイルを受信
ByteBuffer buf = receiveData(channel, false);
check3 = System.currentTimeMillis();
//エラーチェック
byte[] array = new byte[3];
buf.get(array);
ByteBuffer ebuf = ByteBuffer.wrap(array);
String error = charset.decode(ebuf).toString();
if(error.equals("401")){
System.out.println("error: haven't access right");
return;
}else if(error.equals("404")){
System.out.println("error: file not found");
return;
}else if(error.equals("mov")){
array = new byte[9];
buf.get(array);
ByteBuffer hostBuf = ByteBuffer.wrap(array);
String newHost = charset.decode(hostBuf).toString().split(":")[1];
new GetCommand(newHost, userName, fileName, option, prikey, filekeyTable, prop, logOutput, oLogOutput, true, start).run();
return;
}
buf.rewind();
check4 = System.currentTimeMillis();
//共通鍵の読み出し
byte[] encKey = new byte[secLen];
buf.get(encKey);
Cipher ucipher = Cipher.getInstance(pubAlg);
ucipher.init(Cipher.UNWRAP_MODE, prikey);
skey = (SecretKey)ucipher.unwrap(encKey, secAlg, Cipher.SECRET_KEY);
filekeyTable.put(fileName, skey);
//ファイル本体部分の読み出し
encFileBuf = ByteBuffer.allocate(buf.remaining());
encFileBuf.put(buf);
encFileBuf.flip();
}else{
//共通鍵の送信を拒否
sendShortData(channel, charset.encode("false"), false);
//ファイルを受信
encFileBuf = receiveData(channel, false);
check3 = System.currentTimeMillis();
//エラーチェック
byte[] array = new byte[3];
encFileBuf.get(array);
ByteBuffer ebuf = ByteBuffer.wrap(array);
String error = charset.decode(ebuf).toString();
if(error.equals("401")){
System.out.println("error: haven't access right");
return;
}else if(error.equals("404")){
System.out.println("error: file not found");
return;
}else if(error.equals("mov")){
array = new byte[9];
encFileBuf.get(array);
ByteBuffer hostBuf = ByteBuffer.wrap(array);
String newHost = charset.decode(hostBuf).toString().split(":")[1];
new Thread(new GetCommand(newHost, userName, fileName, option, prikey, filekeyTable, prop, logOutput, oLogOutput, true, start)).start();
return;
}
encFileBuf.rewind();
check4 = System.currentTimeMillis();
}
//復号
Cipher cipher = Cipher.getInstance(transform);
cipher.init(Cipher.DECRYPT_MODE, skey);
ByteBuffer fileBuf = ByteBuffer.allocate(cipher.getOutputSize(encFileBuf.capacity()));
cipher.doFinal(encFileBuf, fileBuf);
fileBuf.flip();
//ファイルの書き出し
FileChannel output = new FileOutputStream(downFilePath.concat(fileName)).getChannel();
output.write(fileBuf);
output.close();
}
//終了時刻
long end = System.currentTimeMillis();
long time = end-start;
System.out.println("time(GET): " + time + "ms/" + ((double)time/i));
//区間タイム
long time1 = check1 - start;
long time2 = check2 - check1;
long time3 = check3 - check2;
long time4 = check4 - check3;
long time5 = end - check4;
channel.close();
//ログの記録
if(logOutput != null){
synchronized (logOutput) {
String responsTime = new Long(time).toString();
if(isOtherDisk){
responsTime = responsTime.concat(":" + server);
}
logOutput.write(responsTime);
logOutput.newLine();
}
}
if(oLogOutput != null){
synchronized (oLogOutput) {
oLogOutput.newLine();
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (ShortBufferException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
}
}
/**
* putリクエストを実行するクラス
* @author takayama
*
*/
public class PutCommand extends Command {
private String upFilePath = "";
private String pubAlg = "";
private String secAlg = "";
private int secLen = -1;
private String transform = "";
private SocketChannel channel = null;
private String fileName = "";
private String newName = "";
private PublicKey serverPubkey = null;
private Map<String, SecretKey> filekeyTable;
public PutCommand(String server, String fileName, String newName, PublicKey serverPubkey, Properties prop, String userName, Map<String, SecretKey> filekeyTable) {
this.fileName = fileName;
this.newName = newName;
this.serverPubkey = serverPubkey;
this.filekeyTable = filekeyTable;
try {
channel = connectServer(server, userName, Integer.parseInt(prop.getProperty("port")));
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
upFilePath = prop.getProperty("upFilePath");
pubAlg = prop.getProperty("pubAlg");
secAlg = prop.getProperty("secAlg");
secLen = Integer.parseInt(prop.getProperty("secLen"));
transform = prop.getProperty("transform");
}
@Override
public void run() {
try {
//コマンドの送信
commandSet("put", channel);
//ファイル名送信
Charset charset = Charset.forName("UTF-8");
System.out.println(newName);
sendShortData(channel, charset.encode(newName), true);
//共通鍵の生成
KeyGenerator keygen = KeyGenerator.getInstance(secAlg);
keygen.init(secLen);
SecretKey skey = keygen.generateKey();
//共通鍵の保存
filekeyTable.put(newName, skey);
//ファイルの読み込み
FileChannel input = new FileInputStream(upFilePath.concat(fileName)).getChannel();
int psize = (int)input.size();
ByteBuffer pbuf = ByteBuffer.allocate(psize);
input.read(pbuf);
pbuf.flip();
input.close();
//ファイル暗号化
Cipher cipher = Cipher.getInstance(transform);
cipher.init(Cipher.ENCRYPT_MODE, skey);
int csize = cipher.getOutputSize(psize);
ByteBuffer cbuf = ByteBuffer.allocate(csize);
cipher.doFinal(pbuf, cbuf);
cbuf.flip();
//鍵暗号化
Cipher wcipher = Cipher.getInstance(pubAlg);
wcipher.init(Cipher.WRAP_MODE, serverPubkey);
ByteBuffer kbuf = ByteBuffer.wrap(wcipher.wrap(skey));
//送信
ByteBuffer buf = ByteBuffer.allocate(cbuf.capacity() + kbuf.capacity());
buf.put(kbuf).put(cbuf);
buf.flip();
sendData(channel, buf, true);
channel.close();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (ShortBufferException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
}
}
/**
* 公開鍵を共有するためのクラス
* @author takayama
*
*/
public class SetPubkeyCommand extends Command {
private HashMap<String, PublicKey> pubkeyTable = null;
private PublicKey pubkey = null;
private String userName = "";
private Properties prop = null;
public SetPubkeyCommand(HashMap<String, PublicKey> pubkeyTable, PublicKey pubkey, String userName, Properties prop) {
this.pubkeyTable  = pubkeyTable;
this.pubkey = pubkey;
this.userName = userName;
this.prop  = prop;
}
@Override
public void run() {
try {
List<String> servers = Arrays.asList(prop.getProperty("server").split(":"));
String pubAlg = prop.getProperty("pubAlg");
Iterator<String> it = servers.iterator();
SocketChannel channel = null;
HashMap<String, SocketChannel> socketTable = new HashMap<String, SocketChannel>();
//公開鍵の交換
while(it.hasNext()){
//SocketChannelの生成
String server = it.next();
System.out.println(server);
int port = Integer.parseInt(prop.getProperty("port"));
channel = connectServer(server, userName, port);
//コマンド送信
commandSet("setpubkey", channel);
//ユーザ公開鍵の送信
byte[] encKey = pubkey.getEncoded();
ByteBuffer buf = ByteBuffer.wrap(encKey);
Command.sendShortData(channel, buf, false);
//サーバ公開鍵の受信
buf = Command.receiveShortData(channel, false);
encKey = buf.array();
X509EncodedKeySpec eks = new X509EncodedKeySpec(encKey);
PublicKey serverPubkey = KeyFactory.getInstance(pubAlg).generatePublic(eks);
pubkeyTable.put(server, serverPubkey);
//SocketChannelを一時的に保存
socketTable.put(server, channel);
}
//ダミーのrevoked user用の公開鍵を登録
pubkeyTable.put("revoked", pubkey);
//pubkeyTableの送信
ByteArrayOutputStream baout = new ByteArrayOutputStream();
ObjectOutputStream obout = new ObjectOutputStream(baout);
obout.writeObject(pubkeyTable);
ByteBuffer buf = ByteBuffer.wrap(baout.toByteArray());
obout.close();
baout.close();
it = servers.iterator();
while(it.hasNext()){
channel = socketTable.get(it.next());
Command.sendData(channel, buf, true);
channel.close();
buf.rewind();
}
} catch (IOException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
/**
* サーバと接続し，userNameを送信するメソッド
* @param string
* @return SocketChannel
* @throws IOException 
*/
private SocketChannel connectServer(String server) throws IOException {
int port = Integer.parseInt(prop.getProperty("port"));
//接続
SocketChannel channel = SocketChannel.open(new InetSocketAddress(server, port));
//ユーザ名を送信
Charset charset = Charset.forName("UTF-8");
Command.sendShortData(channel, charset.encode(userName), true);
return channel;
}
}
/**
* アクセス権付与リクエストを実行するクラス
* @author takayama
*
*/
public class AuthCommand extends Command {
private SocketChannel channel = null;
private String fileName = "";
private String authName = "";
//コンストラクタ
public AuthCommand(String server, String userName, String fileName, String authName, Properties prop) {
try {
channel = connectServer(server, userName, Integer.parseInt(prop.getProperty("port")));
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
this.fileName = fileName;
this.authName = authName;
}
@Override
public void run() {
try {
Charset charset = Charset.forName("UTF-8");
//コマンドの送信
commandSet("auth", channel);
//ファイル名の送信
sendShortData(channel, charset.encode(fileName), true);
//対称ユーザ名の送信
sendShortData(channel, charset.encode(authName), false);
//結果の受信
ByteBuffer buf = receiveShortData(channel, false);
System.out.println(charset.decode(buf).toString() + ":[" + fileName + ", " + authName + "]");
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* アクセス権失効リクエストを実行するクラス
* @author takayama
*
*/
public class RevokeCommand extends Command implements Runnable{
private SocketChannel channel = null;
private String fileName = "";
private String revokedName = "";
public RevokeCommand(String server, String userName, String fileName, String revokedName, Properties prop) {
try {
channel = connectServer(server, userName, Integer.parseInt(prop.getProperty("port")));
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
this.fileName = fileName;
this.revokedName = revokedName;
}
@Override
public void run() {
try {
long time = System.currentTimeMillis();
//コマンド送信
commandSet("revoke", channel);
//ファイル名の送信
Charset charset = Charset.forName("UTF-8");
sendShortData(channel, charset.encode(fileName), true);
//対称ユーザ名の送信
sendShortData(channel, charset.encode(revokedName), false);
//結果の受信
ByteBuffer buf = receiveShortData(channel, false);
System.out.println(charset.decode(buf).toString());
System.out.println(System.currentTimeMillis() - time);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* ファイルの更新リクエストを実行するクラス
* @author takayama
*
*/
public class UpdateCommand3 extends Command implements Runnable{
private String fileName = "";
private String server = "";
private Map<String, SecretKey> filekeyTable = null;
private String userName = "";
private SocketChannel channel = null;
private Properties prop = null;
private HashMap<String, PublicKey> pubkeyTable = null;
private PrivateKey prikey = null;
private String size = "";
private long startTime = -1;
private BufferedWriter logOutput = null;
private boolean isOtherDisk = false;
private BufferedWriter oLogOutput;
public UpdateCommand3(String fileName, String server, String size, String userName, Map<String, SecretKey> filekeyTable, Properties prop, HashMap<String, PublicKey> pubkeyTable, PrivateKey prikey, BufferedWriter logOutput, BufferedWriter oLogOutput, boolean isOtherDisk, long startTime) {
this.fileName = fileName;
this.server = server;
this.userName  = userName;
this.filekeyTable = filekeyTable;
this.prop  = prop;
this.pubkeyTable = pubkeyTable;
this.prikey = prikey;
this.size  = size;
this.logOutput = logOutput;
this.oLogOutput = oLogOutput;
this.isOtherDisk = isOtherDisk;
this.startTime = startTime;
}
public UpdateCommand3(String fileName, String server, String size, String userName, Map<String, SecretKey> filekeyTable, Properties prop, HashMap<String, PublicKey> pubkeyTable, PrivateKey prikey, BufferedWriter logOutput, BufferedWriter oLogOutput, boolean isOtherDisk) {
this.fileName = fileName;
this.server = server;
this.userName  = userName;
this.filekeyTable = filekeyTable;
this.prop  = prop;
this.pubkeyTable = pubkeyTable;
this.prikey = prikey;
this.size  = size;
this.logOutput = logOutput;
this.oLogOutput = oLogOutput;
this.isOtherDisk = isOtherDisk;
}
@Override
public void run() {
try {
Charset charset = Charset.forName("UTF-8");
String downFilePath = prop.getProperty("downFilePath");
int secLen = Integer.parseInt(prop.getProperty("secLen"));
String secAlg = prop.getProperty("secAlg");
String pubAlg = prop.getProperty("pubAlg");
String transform = prop.getProperty("transform");
//開始時刻
System.out.println(startTime);
long start = -1;
if(startTime == -1){
start = System.currentTimeMillis();
}else{
start = startTime;
}
//ソケットチャネルの生成
channel = connectServer(server, userName, Integer.parseInt(prop.getProperty("port")));
//コマンド送信
commandSet("update3", channel);
//ファイル名送信
sendShortData(channel, charset.encode(fileName), false);
//鍵の受信
ByteBuffer buf = receiveData(channel, false);
if(buf.limit() < secLen){
String msg = charset.decode(buf).toString();
if(msg.equals("401")){
System.out.println("error: haven't access right");
return;
}else{
String newServer = msg.split(":")[1];
new UpdateCommand3(fileName, newServer, userName, size, filekeyTable, prop, pubkeyTable, prikey, logOutput, oLogOutput, true, start).run();
return;
}
}
byte[] encKey = new byte[secLen];
buf.get(encKey);
Cipher ucipher = Cipher.getInstance(pubAlg);
ucipher.init(Cipher.UNWRAP_MODE, prikey);
SecretKey skey = (SecretKey)ucipher.unwrap(encKey, secAlg, Cipher.SECRET_KEY);
//ファイルの読み込み
FileChannel input = new FileInputStream(downFilePath.concat(fileName)).getChannel();
int psize = (int)input.size();
ByteBuffer pbuf = ByteBuffer.allocate(psize);
input.read(pbuf);
pbuf.flip();
input.close();
//差分データ作成
byte[] diffData = null;
if(size.equals("min")){
diffData = new byte[16];
}else if(size.equals("mid")){
int i = (psize / 16) / 2 * 16;
diffData = new byte[i];
}else if(size.equals("max")){
diffData = new byte[psize];
}
pbuf.get(diffData);
ByteBuffer diffBuf = ByteBuffer.wrap(diffData);
//暗号化
Cipher cipher = Cipher.getInstance(transform);
cipher.init(Cipher.ENCRYPT_MODE, skey);
int csize = cipher.getOutputSize(diffBuf.capacity());
ByteBuffer cbuf = ByteBuffer.allocate(csize);
cipher.doFinal(diffBuf, cbuf);
cbuf.flip();
//送信
sendData(channel, cbuf, false);
//レスポンスを待つ
buf = receiveShortData(channel, false);
//終了時刻
long end = System.currentTimeMillis();
long time = end-start;
System.out.println("time(UPDATE): " + time + "ms");
//ログの記録
if(logOutput != null){
synchronized (logOutput) {
String responsTime = new Long(time).toString();
if(isOtherDisk){
responsTime = responsTime.concat(":" + server);
}
logOutput.write(responsTime);
logOutput.newLine();
}
}
if(oLogOutput != null){
synchronized (oLogOutput) {
oLogOutput.newLine();
}
}
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (ShortBufferException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
}
}
/**
* 設定ファイル生成クラス
* @author takayama
*
*/
public class EncOnClientProperties {
//このクライアントのホスト名
public static final String HOST_NAME = "adisk132";
//他クライアントリスト
public static final String CLIENT = "adisk133";
//全接続サーバリスト
public static final String SERVER = "adisk129:adisk130:adisk131";
//ポート番号
public static final String PORT = "50000";
//公開鍵暗号アルゴリズム
public static final String PUB_ALG = "RSA";
//公開鍵暗号の鍵長
public static final String PUB_LEN = "1024";
//共通鍵暗号アルゴリズム
public static final String SEC_ALG = "AES";
//共通鍵暗号の鍵長
public static final String SEC_LEN = "128";
//ファイル暗号化の変換方式
public static final String TRANSFORM = SEC_ALG.concat("/ECB/PKCS5Padding");
//uploadするファイルのディレクトリ
public static final String UPFILE_PATH = "./upload_files/";
//downloadするファイルのディレクトリ
public static final String DOWNFILE_PATH = "./download_files/";
public static void main(String[] args) {
Properties prop = new Properties();
prop.setProperty("hostName", HOST_NAME);
prop.setProperty("client", CLIENT);
prop.setProperty("server", SERVER);
prop.setProperty("port", PORT);
prop.setProperty("pubAlg", PUB_ALG);
prop.setProperty("pubLen", PUB_LEN);
prop.setProperty("secAlg", SEC_ALG);
prop.setProperty("secLen", SEC_LEN);
prop.setProperty("transform", TRANSFORM);
prop.setProperty("upFilePath", UPFILE_PATH);
prop.setProperty("downFilePath", DOWNFILE_PATH);
try {
FileOutputStream output = new FileOutputStream("./encrypt_on_disk_client/EncOnDiskClient.properties");
prop.store(output, "EncryptOnDiskClient");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
【００５５】
（ａ）コマンド
ｐｕｔ：ユーザが持つファイルを新たに生成した共通鍵で暗号化し、同時にその共通鍵を送信先サーバ（ディスク）の公開鍵で暗号化し、合わせてディスクへ送信するコマンドである。このコマンドによって、データを受け取ったディスクは、ファイルオブジェクトを生成し、暗号化ファイル本体と共にディスクへ書き込む動作を行う。
ｇｅｔ：ユーザからディスクにファイル名を送信し、それに対応する暗号化ファイルを受信するコマンドである。この時、対象ファイルの共通鍵をすでに所有しているかによって、共通鍵も受信するかを選択できる。データを受け取ったユーザは暗号化ファイルを鍵で復号し、自身のマシンのディスクへ書き込む。
ｕｐｄａｔｅ：ユーザからディスクにファイル名、およびそのファイルの変更があった部分のデータ（差分データ）を送信し、ファイルの更新を行うコマンドである。データを受け取ったディスクは、指定ファイルの指定された箇所に差分データを上書きし、ファイルの更新を完了する。
アクセス権付与：ファイルの所有者であるユーザからディスクにファイル名、およびアクセス権を与えるユーザ名を送信し、そのユーザにアクセス権を与えるコマンドである。データを受け取ったディスクは、対象ユーザの公開鍵でファイルの共通鍵を暗号化し、ファイルオブジェクト内のロックボックスへ追加することで、対象ユーザがファイルへアクセスできるようにする。
アクセス権限失効：ファイルの所有者であるユーザからディスクにファイル名、およびアクセス権を失効するユーザ名を送信し、そのユーザのアクセス権を取り消すコマンドである。データを受け取ったディスクは、ロックボックスから対象ユーザ用の鍵を削除し、指定されたアクセス権限失効方式に従って再暗号化処理を行う。
【００５６】
暗号を用いない方式のクライアント・サーバプログラム（２）およびエンクリプト・オン・ワイヤ方式のクライアント・サーバプログラム（３）は、下記の通りであり、ユーザが実行できるコマンドとその処理内容が以下のように異なる以外は、前記のエンクリプト・オン・ディスク方式のファイルサーバクライアントプログラム（１）と同様のプログラムである。
【００５７】
暗号を用いない方式のクライアントプログラム（２）
package raw_client_simulator;
import java.io.*;
import java.util.*;
import java.net.*;
import java.nio.*;
import encrypt_on_disk_client.Command;
/**
* クライアント側メインクラス
* @author takayama
*
*/
public class RawClientSimulator {
private Properties prop = new Properties();
private ArrayList<String> fileList = new ArrayList<String>();
public RawClientSimulator() {    
try {
FileInputStream input = new FileInputStream("./raw_client_simulator/raw_client.properties");
this.prop.load(input);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
new RawClientSimulator().run();
}
private void run() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while(true){
System.out.print("command> ");
String[] cmd = in.readLine().split(" ");
if(cmd[0].equals("put") && cmd.length == 3){
System.out.println("PutExecutor: start");
new PutExecutor(prop, cmd[1], cmd[2], fileList).run();
}else if(cmd[0].equals("get") && cmd.length == 3){
new GetExecutor(prop, cmd[1], cmd[2], fileList).run();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* putリクエストを実行するクラス
* @author takayama
*
*/
public class PutCommand extends Command{
private Properties prop = null;
private String fileName = "";
private String newFileName = "";
//コンストラクタ
public PutCommand(Properties prop, String fileName, String newFileName){
this.prop = prop;
this.fileName = fileName;
this.newFileName = newFileName;
}
@Override
public void run() {
try {
String server = prop.getProperty("host");
int port = Integer.parseInt(prop.getProperty("port"));
SocketChannel channel = SocketChannel.open(new InetSocketAddress(server, port));
commandSet("put", channel);
FileChannel input = new FileInputStream(prop.getProperty("upFilePath").concat(fileName)).getChannel();
Charset charset = Charset.forName("UTF-8");
ByteBuffer buf = ByteBuffer.allocate((int)input.size());
//ファイル名の送信
sendShortData(channel, charset.encode(newFileName), true);
//ファイルの送信
input.read(buf);
buf.flip();
sendData(channel, buf, true);
input.close();
channel.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}
}
/**
* 連続でputを実行するクラス
* @author takayama
*
*/
public class PutExecutor extends Command {
private Properties prop = null;
private String originalFileName = "";
private int quantity = 0;
private ArrayList<String> fileList;
public PutExecutor(Properties prop, String fileName, String quantity, ArrayList<String> fileList) {
this.prop = prop;
this.originalFileName = fileName;
this.quantity = Integer.parseInt(quantity);
this.fileList = fileList;
}
@Override
public void run() {
for(int i=0; i < quantity; i++){
String[] s = originalFileName.split("\\.");
String newFileName = s[0].concat(new Integer(i).toString()).concat("." + s[1]);
new PutCommand(prop, originalFileName, newFileName).run();
fileList.add(newFileName);
}
}
}
/**
* getリクエストを実行するクラス
* @author takayama
*
*/
public class GetCommand extends Command {
private String fileName = "";
private Properties prop = null;
public GetCommand(String fileName, Properties prop) {
this.fileName = fileName;
this.prop = prop;
}
@Override
public void run() {
try {
int port = Integer.parseInt(prop.getProperty("port"));
String host = prop.getProperty("host");
SocketChannel channel = SocketChannel.open(new InetSocketAddress(host, port));
//コマンド名の送信
commandSet("get", channel);
//ファイル名の送信
Charset charset = Charset.forName("UTF-8");
sendShortData(channel, charset.encode(fileName), false);
//ファイル本体の受信
ByteBuffer buf = receiveData(channel, false);
FileChannel output = new FileOutputStream(prop.getProperty("downFilePath").concat(fileName)).getChannel();
output.write(buf);
output.close();
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 連続でgetを実行するクラス
* @author takayama
*
*/
public class GetExecutor extends Command {
private Properties prop = null;
private int exeCount = 0;
private String outputName = "";
private ArrayList<String> fileList;
public GetExecutor(Properties prop, String exeCount, String fileSize, ArrayList<String> fileList) {
this.prop = prop;
this.exeCount = Integer.parseInt(exeCount);
this.fileList = fileList;
outputName = "./rawlog_".concat(fileSize).concat(".txt");
}
@Override
public void run() {
try {
Random rand = new Random();
BufferedWriter logOutput = new BufferedWriter(new FileWriter(outputName));
for(int i=0; i < exeCount; i++){
//ファイルの選択
String fileName = selectRandomFileName(rand);
long start = System.currentTimeMillis();
new GetCommand(fileName, prop).run();
String time = new Long(System.currentTimeMillis() - start).toString();
System.out.println(time);
logOutput.write(time);
logOutput.newLine();
}
logOutput.close();
} catch (IOException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}
private String selectRandomFileName(Random rand) {
return fileList.get(zipf(fileList.size(), rand, 0.3));
}
//Zipf分布に従い乱数生成
private int zipf(int size, Random rand, double theta) {
return (int)(size * Math.pow(rand.nextDouble(), 1.0/(1.0 - theta)));
}
}
【００５８】
暗号を用いない方式のサーバプログラム
package raw_server;
import java.io.*;
import java.net.*;
import java.nio.channels.*;
import java.util.*;
/**
* サーバのメインクラス
* @author takayama
*
*/
public class RawServer {
private Properties prop = new Properties();
//コンストラクタ
public RawServer(){
try {
FileInputStream input = new FileInputStream("./raw_server/raw_server.properties");
prop.load(input);
} catch (FileNotFoundException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (IOException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
new RawServer().run();
}
private void run() {
int port = Integer.parseInt(prop.getProperty("port"));
ServerSocketChannel serverChannel = null;
try {
serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress(port));
System.out.println("raw serverが起動しました(" + serverChannel.socket().getLocalSocketAddress() + ")");
while(true){
SocketChannel channel = serverChannel.accept();
SocketAddress remoteAddress = channel.socket().getRemoteSocketAddress();
System.out.println(remoteAddress + ":[接続されました]");
new Thread(new RawServerThread(channel, prop)).start();
}
} catch (IOException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}finally{
if(serverChannel != null && serverChannel.isOpen()){
try{
System.out.println("raw serverを停止します．");
serverChannel.close();
}catch(IOException e){}
}
}
}
}
/**
* 接続毎に生成され，指定されたコマンドを実行するスレッドクラス
* @author takayama
*
*/
public class RawServerThread implements Runnable {
private Properties prop = null;
private SocketChannel channel = null;
public RawServerThread(SocketChannel channel, Properties prop) {
this.channel = channel;
this.prop = prop;
}
public void run() {
try{
String path = prop.getProperty("path");
//コマンドの受信
ByteBuffer buf = ByteBuffer.allocate(256);
if(channel.read(buf) < 0){
return;
}
buf.flip();
Charset charset = Charset.forName("UTF-8");
String cmd = charset.decode(buf).toString();
//ackを返す
channel.write(charset.encode("ack"));
if(cmd.equals("put")){
new PutCommandHandler(channel, path).execute();
}else if(cmd.equals("get")){
new GetCommandHandler(channel, path).execute();
}
} catch (IOException e){
e.printStackTrace();
}finally{
SocketAddress remoteAddress = channel.socket().getRemoteSocketAddress();
System.out.println(remoteAddress + ":[切断しました]");
if(channel != null && channel.isOpen()){
try {
channel.close();
} catch (IOException e) {}
}
}
}
}
/**
* putコマンドに対応するクラス
* @author takayama
*
*/
public class PutCommandHandler extends CommandHandler {
SocketChannel channel = null;
String path = "";
public PutCommandHandler(SocketChannel channel, String path) {
this.channel = channel;
this.path = path;
}
@Override
public void execute() {
ByteBuffer buf = null;
Charset charset = Charset.forName("UTF-8");
try {
//ファイル名の受信
buf = receiveShortData(channel, true);
String fileName = charset.decode(buf).toString();
//ファイル本体の受信
buf = receiveData(channel, false);
FileChannel output = new FileOutputStream(path.concat(fileName)).getChannel();
output.write(buf);
output.close();
channel.write(charset.encode("ack"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* getコマンドに対応するクラス
* @author takayama
*
*/
public class GetCommandHandler extends CommandHandler {
SocketChannel channel = null;
String path = "";
public GetCommandHandler(SocketChannel channel, String path) {
this.channel = channel;
this.path = path;
}
@Override
public void execute() {
Charset charset = Charset.forName("UTF-8");
try {
ByteBuffer buf = receiveShortData(channel, false);
String fileName = charset.decode(buf).toString();
//ファイル本体の送信
FileChannel input = new FileInputStream(path.concat(fileName)).getChannel();
buf = ByteBuffer.allocate((int)input.size());
input.read(buf);
buf.flip();
sendData(channel, buf, false);
} catch (IOException e) {
e.printStackTrace();
}
}
}
【００５９】
ｐｕｔ：ユーザからファイルを暗号化せずにディスクに送信するコマンド．ディスクは受信したファイルをそのまま書き込むコマンドとする。
ｇｅｔ：ユーザからディスクにファイル名を送信し，ディスクは指定されたファイルをそのままユーザへ送信するコマンドとする。
【００６０】
エンクリプト・オン・ワイヤ方式のクライアント・サーバプログラム（３）
エンクリプト・オン・ワイヤ方式のクライアントプログラム
package on_wire_client_simulator;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.security.*;
import java.util.*;
import javax.crypto.*;
import encrypt_on_disk_client.Command;
import raw_client_simulator.PutExecutor;
/**
* クライアント側メインクラス
* @author takayama
*
*/
public class OnWireClientSimulator {
private Properties prop = new Properties();
private ArrayList<String> fileList = new ArrayList<String>();
private SecretKey sharedkey = null;
private PublicKey pubkey = null;
private PrivateKey prikey = null;
public OnWireClientSimulator() { 
try {
FileInputStream input = new FileInputStream("./on_wire_client_simulator/on_wire_client.properties");
this.prop.load(input);
//公開鍵の設定
String pubAlg = prop.getProperty("pubAlg");
int pubLen = Integer.parseInt(prop.getProperty("pubLen"));
KeyPairGenerator kpg = KeyPairGenerator.getInstance(pubAlg);
kpg.initialize(pubLen);
KeyPair keys = kpg.genKeyPair();
prikey = keys.getPrivate();
pubkey = keys.getPublic();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
new OnWireClientSimulator().run();
}
private void run() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while(true){
System.out.print("command> ");
String[] cmd = in.readLine().split(" ");
if(cmd[0].equals("put") && cmd.length == 3){
System.out.println("PutExecutor: start");
new PutExecutor(prop, cmd[1], cmd[2], fileList).run();
}else if(cmd[0].equals("get") && cmd.length == 3){
new GetExecutor(prop, cmd[1], cmd[2], fileList, prikey, sharedkey).run();
}else if(cmd[0].equals("setpubkey")){
setpubkey();
}else if(cmd[0].equals("sharekey")){
sharekey();
}
}
} catch (IOException e) {
e.printStackTrace();
}      
}
private void sharekey() {
SocketChannel channel;
try {
channel = SocketChannel.open(new InetSocketAddress(prop.getProperty("host"), Integer.parseInt(prop.getProperty("port"))));
Command.commandSet("sharekey", channel);
ByteBuffer buf = Command.receiveData(channel, false);
byte[] encKey = buf.array();
Cipher ucipher = Cipher.getInstance(prop.getProperty("pubAlg"));
ucipher.init(Cipher.UNWRAP_MODE, prikey);
sharedkey = (SecretKey) ucipher.unwrap(encKey, prop.getProperty("secAlg"), Cipher.SECRET_KEY);
System.out.println(sharedkey);
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}             
}
private void setpubkey() {
SocketChannel channel;
try {
channel = SocketChannel.open(new InetSocketAddress(prop.getProperty("host"), Integer.parseInt(prop.getProperty("port"))));
Command.commandSet("setpubkey", channel);
ByteBuffer buf = ByteBuffer.wrap(pubkey.getEncoded());
Command.sendData(channel, buf, false);
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* getリクエストを実行するクラス
* @author takayama
*
*/
public class GetCommand extends Command {
private String fileName;
private Properties prop;
private SecretKey sharedkey;
private PrivateKey prikey;
public GetCommand(String fileName, Properties prop, SecretKey sharedkey, PrivateKey prikey) {
this.fileName = fileName;
this.prop = prop;
this.sharedkey = sharedkey;
this.prikey = prikey;
}
@Override
public void run() {
try {
int port = Integer.parseInt(prop.getProperty("port"));
String host = prop.getProperty("host");
String pubAlg = prop.getProperty("pubAlg");
String secAlg = prop.getProperty("secAlg");
String transform = prop.getProperty("transform");
String downFilePath = prop.getProperty("downFilePath");
SocketChannel channel = SocketChannel.open(new InetSocketAddress(host, port));
//コマンド名の送信
commandSet("get", channel);   
//ファイル名の送信
Charset charset = Charset.forName("UTF-8");
sendShortData(channel, charset.encode(fileName), false);
//sharedKeyが存在しなければ共通鍵を受信
SecretKey skey = null;
if(sharedkey == null){
ByteBuffer buf = receiveData(channel, true);
Cipher ucipher = Cipher.getInstance(pubAlg);
ucipher.init(Cipher.UNWRAP_MODE, prikey);
skey = (SecretKey) ucipher.unwrap(buf.array(), secAlg, Cipher.SECRET_KEY);
}else{
skey = sharedkey;
}
//ファイル本体を受信
ByteBuffer buf = receiveData(channel, true);
//復号して格納
Cipher cipher = Cipher.getInstance(transform);
cipher.init(Cipher.DECRYPT_MODE, skey);
ByteBuffer pbuf = ByteBuffer.allocate(cipher.getOutputSize(buf.capacity()));
cipher.doFinal(buf, pbuf);
pbuf.flip();
FileChannel output = new FileOutputStream(downFilePath.concat(fileName)).getChannel();
output.write(pbuf);
output.close();
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (ShortBufferException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
}
}
/**
* 連続でgetを実行するクラス
* @author takayama
*
*/
public class GetExecutor extends Command {
private Properties prop = null;
private int exeCount = 0;
private String outputName = "";
private ArrayList<String> fileList;
private PrivateKey prikey;
private SecretKey sharedkey;
public GetExecutor(Properties prop, String exeCount, String fileSize, ArrayList<String> fileList, PrivateKey prikey, SecretKey sharedkey) {
this.prop = prop;
this.exeCount = Integer.parseInt(exeCount);
this.fileList = fileList;
this.prikey = prikey;
this.sharedkey = sharedkey;
if(sharedkey == null){
outputName = "./wirelog_".concat(fileSize).concat("notshare").concat(".txt");
}else{
outputName = "./wirelog_".concat(fileSize).concat("share").concat(".txt");
}
}
@Override
public void run() {
try {
Random rand = new Random();
BufferedWriter logOutput = new BufferedWriter(new FileWriter(outputName));
System.out.println(sharedkey);
for(int i=0; i < exeCount; i++){
//ファイルの選択
String fileName = selectRandomFileName(rand);
long start = System.currentTimeMillis();
new GetCommand(fileName, prop, sharedkey, prikey).run();
String time = new Long(System.currentTimeMillis() - start).toString();
System.out.println(time);
logOutput.write(time);
logOutput.newLine();
}
logOutput.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private String selectRandomFileName(Random rand) {
return fileList.get(zipf(fileList.size(), rand, 0.3));
}
//Zipf分布に従い乱数生成
private int zipf(int size, Random rand, double theta) {
return (int)(size * Math.pow(rand.nextDouble(), 1.0/(1.0 - theta)));
}
}
【００６１】
エンクリプト・オン・ワイヤ方式のクライアント・サーバプログラム
package raw_server;
import java.io.*;
import java.net.*;
import java.nio.channels.*;
import java.util.*;
/**
* サーバのメインクラス
* @author takayama
*
*/
public class RawServer {
private Properties prop = new Properties();
//コンストラクタ
public RawServer(){
try {
FileInputStream input = new FileInputStream("./raw_server/raw_server.properties");
prop.load(input);
} catch (FileNotFoundException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (IOException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
new RawServer().run();
}
private void run() {
int port = Integer.parseInt(prop.getProperty("port"));
ServerSocketChannel serverChannel = null;
try {
serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress(port));
System.out.println("raw serverが起動しました(" + serverChannel.socket().getLocalSocketAddress() + ")");
while(true){
SocketChannel channel = serverChannel.accept();
SocketAddress remoteAddress = channel.socket().getRemoteSocketAddress();
System.out.println(remoteAddress + ":[接続されました]");
new Thread(new RawServerThread(channel, prop)).start();
}
} catch (IOException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}finally{
if(serverChannel != null && serverChannel.isOpen()){
try{
System.out.println("raw serverを停止します．");
serverChannel.close();
}catch(IOException e){}
}
}
}
}
/**
* 接続毎に生成され，指定されたコマンドを実行するスレッドクラス
* @author takayama
*
*/
public class RawServerThread implements Runnable {
private Properties prop = null;
private SocketChannel channel = null;
public RawServerThread(SocketChannel channel, Properties prop) {
this.channel = channel;
this.prop = prop;
}
public void run() {
try{
String path = prop.getProperty("path");
//コマンドの受信
ByteBuffer buf = ByteBuffer.allocate(256);
if(channel.read(buf) < 0){
return;
}
buf.flip();
Charset charset = Charset.forName("UTF-8");
String cmd = charset.decode(buf).toString();
//ackを返す
channel.write(charset.encode("ack"));
if(cmd.equals("put")){
new PutCommandHandler(channel, path).execute();
}else if(cmd.equals("get")){
new GetCommandHandler(channel, path).execute();
}
} catch (IOException e){
e.printStackTrace();
}finally{
SocketAddress remoteAddress = channel.socket().getRemoteSocketAddress();
System.out.println(remoteAddress + ":[切断しました]");
if(channel != null && channel.isOpen()){
try {
channel.close();
} catch (IOException e) {}
}
}
}
}
/**
* putコマンドに対応するクラス
* @author takayama
*
*/
public class PutCommandHandler extends CommandHandler {
SocketChannel channel = null;
String path = "";
public PutCommandHandler(SocketChannel channel, String path) {
this.channel = channel;
this.path = path;
}
@Override
public void execute() {
ByteBuffer buf = null;
Charset charset = Charset.forName("UTF-8");
try {
//ファイル名の受信
buf = receiveShortData(channel, true);
String fileName = charset.decode(buf).toString();
//ファイル本体の受信
buf = receiveData(channel, false);
FileChannel output = new FileOutputStream(path.concat(fileName)).getChannel();
output.write(buf);
output.close();
channel.write(charset.encode("ack"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* getコマンドに対応するクラス
* @author takayama
*
*/
public class GetCommandHandler extends CommandHandler {
SocketChannel channel = null;
String path = "";
public GetCommandHandler(SocketChannel channel, String path) {
this.channel = channel;
this.path = path;
}
@Override
public void execute() {
Charset charset = Charset.forName("UTF-8");
try {
ByteBuffer buf = receiveShortData(channel, false);
String fileName = charset.decode(buf).toString();
//ファイル本体の送信
FileChannel input = new FileInputStream(path.concat(fileName)).getChannel();
buf = ByteBuffer.allocate((int)input.size());
input.read(buf);
buf.flip();
sendData(channel, buf, false);
} catch (IOException e) {
e.printStackTrace();
}
}
}
【００６２】
ｐｕｔ：ユーザが持つファイルを新たに生成した共通鍵で暗号化し、同時にその共通鍵を送信先サーバ（ディスク）の公開鍵で暗号化し、合わせてディスクへ送信するコマンドとする。これによって、サーバは暗号化ファイルを復号してディスクに書き込む。
ｇｅｔ：ユーザからディスクにファイル名を送信し、対応するファイルを受信するコマンドとする。サーバは、コマンドを受け取ると、予めユーザと共通鍵を共通している場合（sharekey参照）はその鍵、無い場合は新たに生成した共通鍵を用いてファイルを暗号化する。その後、暗号化ファイルと、鍵を共有していない場合はユーザの公開鍵で暗号化したファイル共通鍵をユーザに送信し、ユーザは暗号化ファイルを復号して自身のマシンのディスクに書き込む。
ｓｈａｒｅｋｅｙ：ユーザが新しい共通鍵を生成し、サーバの公開鍵で暗号化してサーバへ送信するコマンドである。サーバとユーザはこの鍵を保存しておき、ｇｅｔコマンド実行時に用いる。
【００６３】
一定のサイズのファイルを格納している環境で、これらのプログラムで、ｇｅｔの要求を送信してからファイルを受信し、暗号化されている場合は復号し、ディスクに書き込むまでの時間を応答時間として測定した。ここで暗号を用いる方式では、ファイルとともに鍵を転送する場合（Nonshared）と、予め共有された鍵があり鍵の転送を必要としない場合（shared）の２通りを行った。各方式毎にファイルサイズを変化させて実験を行った結果を図８に示す。
【００６４】
図８より、ファイルサイズが大きくなるほどエンクリプト・オン・ワイヤ方式に対してエンクリプト・オン・ディスク方式が有利になることがわかる。これは、ファイルサイズが大きいほどエンクリプト・オン・ディスク方式において予め暗号化しておくことによるコスト削減の効果が大きいことを示している。
【００６５】
実験方法
３台のディスクに対し、予め１台のオーナークライアントＰＣから１ＭＢのファイルを１ディスク当たり５００回ｐｕｔしておく。この３台のディスクそれぞれに対し、１台のクライアントＰＣによって５００ｍｓの間隔でｇｅｔリクエストを出し、その応答時間の変化を記録する。この間隔５００ｍｓは、一定間隔でｇｅｔリクエストを出してその応答時間を測定する実験を、様々な間隔について行い、平均応答時間が大きく変化しない範囲で最小のものである。この状態で、オーナークライアントＰＣによって最もアクセスされる確率が高いファイルについてアクセス権限失効を発生させた時に応答時間がどのように変化したかを観測した。また、同時にｖｍｓｔａｔコマンドによりＣＰＵ使用率を測定した。
【００６６】
実験１
1個のファイルに対してアクセス権限失効が発生した場合
アクティブ・レボケーション方式、図２（Ｂ）に示すデータの配置および置き換えの処理方式、および図２（Ａ）に示すデータの配置および置き換えの処理方式について、３０秒間の実験中に１個のファイルについてアクセス権限失効が発生した時の応答時間とＣＰＵ使用率の変化を図９〜１１に示す。実験は、各方式毎に１０回ずつ行い、応答時間は全てについてプロットし、ＣＰＵ利用率は平均を計算してプロットした。
【００６７】
応答時間の分布を比較すると、図９に示すアクティブ・レボケーション方式による場合では、アクセス権限の失効に伴い、対象ファイルのプライマリデータがあり、それにアクセスできなくなるディスクＡだけでなく、対象ファイルのバックアップデータがあるものの、それはアクセスの対象ではないディスクＢでも応答時間が遅くなる傾向があることがわかる。これは、アクセスされないファイルの再暗号化処理も応答時間に大きく影響してくることを示している．
【００６８】
これに対して、図１０および図１１では、どちらもディスクＢの応答時間はほとんど変化していない。したがって、ディスクＢにおける再暗号化処理を行わなくてもよいので、性能低下が大きく抑えられているといえる。
【００６９】
さらに、ディスクの間での通信を行わなくてよい。図２（Ｂ）に示す方式では、アクティブ・レボケーション方式よりも応答時間が増大しない傾向があることが分かる。これは、対象ファイルへのアクセス要求が来た場合、ディスクＢにおけるバックアップデータの新たなプライマリデータへの置き換え・昇格の処理が終了次第、そのアクセス要求先のユーザへの再アクセス要求を返すことによって、迅速な再アクセスが可能となる効果を示していると考えられる。
【００７０】
実験２
複数のファイルに対してアクセス権限が失効した場合
１つのディスクに格納されている複数のファイルに対して、同時に、アクセス権限失効が発生した場合を想定して、実験を行った。
ディスクに格納されているプライマリデータのうち、アクセスされる確率が上位である２４個のファイルに対してアクセス権限の失効が発生したときの応答時間とＣＰＵ使用率の変化を測定し、図１２〜１４に示した。図１２〜１４において、グラフの横軸は、revocation発生時を０とした相対時間を表す。
【００７１】
アクセス権限失効の対象となるファイルが１個の場合と同様に、アクティブ・レボケーション方式と比較すると、図１２に示すとおり、図２（Ｂ）および図２（Ｂ）に示す方式では、ディスクＢの応答時間の変化は非常に小さく、プライマリデータの再暗号化処理を実行しないことによる効果が確認できた。
【００７２】
また図１３および１３に示す結果を比較すると、図２（Ｂ）に示す方式では、図２（Ａ）に示す方式と比較して、通信を行わなければならない分、送信側と受信側のディスクに負担がかかっていることが読み取れる。しかし、図２（Ｂ）に示す方式では、ディスクＡにおいても、処理が遅くなった時点から再び安定する時点までの平均応答時間を比較すると、アクティブ・レボケーション方式におけるディスクＡの約７３パーセントにとどまっている。これは、新しいバックアップデータを送信するコストが、アクセス権限失効対象ファイルへのアクセスの新しい位置への回送による待機時間減少と、再暗号化後ディスクへの書き込みを行わないことによるコスト削減で打ち消されているためと考えられる。実際にアクセスの回送が発生し、応答時間が抑えられていることは、実験時のログから確認できた。
【図面の簡単な説明】
【００７３】
【図１】分散ネットワークストレージシステムを示す概念図である。
【図２】（Ａ）および（Ｂ）は、それぞれプライマリデータとバックアップデータの配置および置き換えの処理方式を説明する図である。
【図３】キーオブジェクトの構造を示す図である。
【図４】ファイルオブジェクトの構造を示す図である。
【図５】アクティブ・レボケーション方式において、アクセス権限失効が発生した場合のデータの処理および格納方法を示す図である。
【図６】図２（Ｂ）に示すデータの配置および置き換えの処理方式において、アクセス権限失効が発生した場合のデータの処理および格納方法を示す図である。
【図７】図２（Ａ）に示すデータの配置および置き換えの処理方式の方式において、アクセス権限失効が発生した場合のデータの処理および格納方法を示す図である。
【図８】予備実験の結果を示すグラフである。
【図９】実験１において、アクセス権限失効が発生した場合のアクティブ・レボケーション方式による処理結果を示すグラフである。
【図１０】実験１において、アクセス権限失効が発生した場合の図２（Ｂ）に示すデータの配置および置き換えの処理方式による処理結果を示すグラフである。
【図１１】実験１において、アクセス権限失効が発生した場合の図２（Ａ）に示すデータの配置および置き換えの処理方式による処理結果を示すグラフである。図である。
【図１２】実験２において、複数のファイルに対してアクセス権限が失効した場合のアクティブ・レボケーション方式による処理結果を示すグラフである。
【図１３】実験２において、複数のファイルに対してアクセス権限が失効した場合の図２（Ｂ）に示すデータの配置および置き換えの処理方式による処理結果を示すグラフである。
【図１４】実験２において、複数のファイルに対してアクセス権限が失効した場合の図２（Ａ）に示すデータの配置および置き換えの処理方式による処理結果を示すグラフである。
【符号の説明】
【００７４】
１      分散ネットワークストレージシステム
ＣＬ１，ＣＬ２，ＣＬ３    ユーザ
２Ａ，２Ｂ，２Ｃ      データ格納装置（ディスクノード）
Ａ１，Ｂ１，Ｃ１      プライマリ格納部
Ａ２，Ｂ２，Ｃ２      バックアップ格納部
【図１】
2008197998.tif 000005
【図２】
2008197998.tif 000006
【図３】
2008197998.tif 000007
【図４】
2008197998.tif 000008
【図５】
2008197998.tif 000009
【図６】
2008197998.tif 000010
【図７】
2008197998.tif 000011
【図８】
2008197998.tif 000012
【図９】
2008197998.tif 000013
【図１０】
2008197998.tif 000014
【図１１】
2008197998.tif 000015
【図１２】
2008197998.tif 000016
【図１３】
2008197998.tif 000017
【図１４】
2008197998.tif 000018