Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.
C-LIS CO., LTD.
自己紹介
Android Studio本
3
2014年11月21日発売
技術評論社刊
Android Studio 0.8.6
http://amzn.to/1HYRp32
4
5
https://github.com/keiji/the-androidstudio-book
コミックマーケット87

Android Studioセットアップガイド
6
https://anharu.keiji.io
はじめての
Androidアプリ開発レッスン
7
改訂版が出ます(2016年1月上旬予定)
改訂版の原稿を送ったのが11月16日
9
11月20日
サンプルコードのプロジェクトを

Android 1.5用に調整したのが11月23日
11月24日
11
Android Studio 2.0 Preview
続く
Realmの暗号化と

Android System
2015/11/25 Realm Meetup #9
今日の内容
データの保護
15
 ユーザーや悪意のある開発者からデータを保護したい。
 安全にデータを保管するための方法として

「javax.cryptoによるデータベースファイルの保護」

「SQLCipherを使った保護」

「Realmの暗号化機能...
鍵の保護
16
 暗号化したデータの復号鍵をどこに保存するかは大きな
課題である。
 Android 6.0で大幅に強化された「Android Keystore
System」を使って、データを安全に保護する方策を検討
する。
 
データの保護を検討する
https://github.com/keiji/realm_meetup_9/releases/tag/realm_meetup_9
{

"characters": [

{

"name": "Uduki Shimamura",

"age": "17",

"megane": false

},

{

"name": "Rin Shibuya",

"age": "1...
アプリの動作
https://github.com/keiji/realm_meetup_9/releases/tag/realm_meetup_9
{

"characters": [

{

"name": "Uduki Shimamura...
検索条件
{

"characters": [

{

"name": "Uduki Shimamura",

"age": "17",

"megane": false

},

{

"name": "Rin Shibuya",

"age...
実行画面
21
javax.crypto
22
•Androidに組み込まれている暗号化フレームワーク
•AES 256-bitに対応している
暗号化したデータベースファイルを、
利用時に限定して復号する
@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);



setContentView(R.l...
private static final String TRANSFORMATION = "AES/CBC/PKCS7Padding";

private static final byte[] KEY = "thisismypa55w0rdt...
テーブル構造
25
private static final String CREATE_TABLE_USERS = "CREATE TABLE characters ( " +

"_id INTEGER PRIMARY KEY," +

"...
問題点
26
復号してデータベースにアクセスしている間、

暗号化されていないデータがファイルシステム上に存在する
SQLCipher
27
•Zetetic社が開発、提供する暗号化機能付きSQLite
•AES 256-bitで暗号化
•透過的にアクセス可能
暗号化したままデータを読み書きする
公式ではバイナリを配布せず
28
SQLCipherは、基本的に有償版を推しているイメージ。
オープンソース版のCommunity Editionは個々でビルドする
dependencies {

compile fileTree(dir: 'libs', include: ['*.jar'])

testCompile 'junit:junit:4.12'

compile 'com.android.su...
private static final String DB_PASSWORD = "pa55w0rd";

@Override

protected void onCreate(Bundle savedInstanceState) {

su...
テーブル構造
31
private static final String CREATE_TABLE_USERS = "CREATE TABLE characters ( " +

"_id INTEGER PRIMARY KEY," +

"...
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[
DexPathList[
[zip file "/data/app/io.keiji.realmsample2-2/bas...
問題点
33
SQLCipherのSQLiteDatabaseクラスは、

net.sqlcipher.databaseパッケージに所属している。
SQLiteDatabaseとの継承関係もないため、

一般的なORMとの相性は良くないと思われる
Realm
34
•Realm社が開発、提供するNoSQLデータベース
•AES 256-bitで暗号化
•透過的にアクセス可能
読み書き中もデータは暗号化されたまま
データが抜き出されても現実的な時間では解読が困難
public class RealmAdapter {



private static final byte[] KEY
= "thisismypa55w0rdthisismypa55w0rdthisismypa55w0rdthisismy...
RealmResults<Character> realmResult = mRealm

.where(Character.class)

.equalTo("megane", true)

.findAll();

mListView = ...
遭遇した課題
createObjectFromJson
38
{

"characters": [

{

"name": "Uduki Shimamura",

"age": "17",

"megane": false

},

{

"name": "...
createObjectFromJson
39
public class JsonLoaderRealmJson extends AsyncTaskLoader<LoaderResult> {

@Override

public Loader...
想定していないデータの存在
40
{

"name": "Kirari Moroboshi",

"age": "17",

"megane": false

},

{

"name": "Nana Abe",

"age": "永遠の17歳...
課題の解決 - JPP
41
JsonPullParser

https://github.com/vvakame/JsonPullParser

を導入して、Jsonのパース処理をRealmから切り離した。
参考: RealmとJSONライブ...
課題の解決
42
@JsonModel

public class Characters4Jpp {



@JsonKey

private List<Character> characters;



public List<Charact...
converterを指定
43
@JsonModel

public class Character extends RealmObject {



@JsonKey

private long id;



@JsonKey

privat...
converter
44
public class AgeTokenConverter extends TokenConverter<Integer> {



static AgeTokenConverter converter = null...
鍵の保護を検討する
鍵をどこに置く?
46
private static final String DB_PASSWORD = "pa55w0rd";
private static final byte[] KEY
= "thisismypa55w0rdthisi...
Android Keystore System
47
 Android端末のセキュリティ・ハードウェア(Secure Element,
Trusted Execution Environment)内に鍵情報を格納する。
 Android 4.3...
Android Keystore System
48
 javax.cryptoと密接に連携している。
 任意の情報を保存できるわけではない(=Realmの暗号化キーを直
接保存できない)。
http://developer.android.c...
Android Keystore System
49
Realm暗号キー
64bytes
Android
Keystore
System
鍵情報
AES
暗号キー
64bytes
暗号化済
ユーザー認証
Storage
http://devel...
realm_with_ask.RealmActivity
50
static final String PROVIDER_NAME = "AndroidKeyStore";

static final String KEY_ALIAS = "a...
realm_with_ask.RealmActivity
51
void authorize() {

KeyguardManager km = (KeyguardManager) getSystemService(KEYGUARD_SERVI...
realm_with_ask.RealmActivity
52
realm_with_ask.RealmActivity
53
void readyRealm() {

try {

Cipher cipher = Cipher.getInstance(TRANSFORMATION);



byte[] ...
realm_with_ask.RealmActivity
54
byte[] encryptRealmKey(byte[] passkey, Cipher cipher) {



try {

SecretKey key = (SecretK...
realm_with_ask.RealmActivity
55
byte[] decryptRealmKey(byte[] encrypted, Cipher cipher) {

byte[] initializationVector = n...
Fingerprint API
56
 Android 6.0(Marshmallow)で追加。
 Android Keystore Systemをパターンロック・パスワードに
加えて指紋スキャナーで認証する。
http://developer...
RealmFingerprintActivity
57
private final AuthenticationCallback mAuthenticationCallback
= new FingerprintManager.Authenti...
RealmFingerprintActivity
58
@Override
void authorize() {

FingerprintManager fingerprintManager
= (FingerprintManager) get...
RealmFingerprintActivity
59
今日の内容 - まとめ
今日の内容 - まとめ
61
 安全にデータを保管するための方法として
「javax.cryptoによるデータベースファイルの保護」
「SQLCipherを使った保護」

「Realmの暗号化機能」
 の3パターンを、それぞれ比較した。
今日の内容 - まとめ
62
 「javax.cryptoによるデータベースファイルの保護」で
は、SQLDatabaseのファイルを必要なときだけ復号し
た。
 しかし、読み書きの最中はファイルシステム上に復号し
たデータファイルが存在するた...
今日の内容 - まとめ
63
 「SQLCipherを使った保護」では、SQLDatabaseのファ
イルを暗号化した上で全体を復号することなく、透過的に
読み書きができた。
 しかし、Android 6.0で正常な動作が確認できなかった。
ま...
今日の内容 - まとめ
64
 Realmを使うと、データベースを暗号で保護した状態
で、透過的に扱うことができた。
 JSONからモデルに変換する機能は、異常値が含まれる
場合などは対応しきれないケースは、JsonPullParserの
co...
今日の内容 - まとめ
65
 「Android Keystore System」を使うことで、機密
データの復号鍵をセキュリティ・ハードウェアに保護され
た領域に保存して、安全に格納することができる。
 ただし、Realmはjavax.cry...
C-LIS CO., LTD.
各製品名・ブランド名、会社名などは、一般に各社の商標または登録商標です。本資料中では、©、®、™を割愛しています。
本資料は、有限会社シーリスの著作物です。掲載されているイラストは、特に記載がない場合は根雪れいの...
http://techbooster.github.io/c89/
Copyright TechBooster
C89 - TechBooster 3日目東シ58a
Copyright TechBooster
C89 - TechBooster 3日目東シ58a
Copyright TechBooster
Próxima SlideShare
Cargando en…5
×

Realmの暗号化とAndroid System

6.614 visualizaciones

Publicado el

 ユーザーや悪意のある開発者からデータを保護したい。
 安全にデータを保管するための方法として
「javax.cryptoによるデータベースファイルの保護」
「SQLCipherを使った保護」
「Realmの暗号化機能」
 の、三つについて、それぞれ利用して比較する。

 暗号化したデータの復号鍵をどこに保存するかは大きな課題である。
 Android 6.0で大幅に強化された「Android Keystore System」を使って、データを安全に保護する方策を検討する。

Publicado en: Software
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Responder 
    ¿Estás seguro?    No
    Tu mensaje aparecerá aquí

Realmの暗号化とAndroid System

  1. 1. C-LIS CO., LTD.
  2. 2. 自己紹介
  3. 3. Android Studio本 3 2014年11月21日発売 技術評論社刊 Android Studio 0.8.6 http://amzn.to/1HYRp32
  4. 4. 4
  5. 5. 5 https://github.com/keiji/the-androidstudio-book コミックマーケット87
 Android Studioセットアップガイド
  6. 6. 6 https://anharu.keiji.io はじめての Androidアプリ開発レッスン
  7. 7. 7 改訂版が出ます(2016年1月上旬予定)
  8. 8. 改訂版の原稿を送ったのが11月16日
  9. 9. 9 11月20日
  10. 10. サンプルコードのプロジェクトを
 Android 1.5用に調整したのが11月23日
  11. 11. 11月24日 11 Android Studio 2.0 Preview
  12. 12. 続く
  13. 13. Realmの暗号化と
 Android System 2015/11/25 Realm Meetup #9
  14. 14. 今日の内容
  15. 15. データの保護 15  ユーザーや悪意のある開発者からデータを保護したい。  安全にデータを保管するための方法として
 「javax.cryptoによるデータベースファイルの保護」
 「SQLCipherを使った保護」
 「Realmの暗号化機能」
  の、三つについて、それぞれ利用して比較する。
  16. 16. 鍵の保護 16  暗号化したデータの復号鍵をどこに保存するかは大きな 課題である。  Android 6.0で大幅に強化された「Android Keystore System」を使って、データを安全に保護する方策を検討 する。  
  17. 17. データの保護を検討する https://github.com/keiji/realm_meetup_9/releases/tag/realm_meetup_9
  18. 18. {
 "characters": [
 {
 "name": "Uduki Shimamura",
 "age": "17",
 "megane": false
 },
 {
 "name": "Rin Shibuya",
 "age": "15",
 "megane": false
 },
 {
 "name": "Haruna Kamijo",
 "age": "18",
 "megane": true
 }, サンプルアプリ 18 https://github.com/keiji/realm_meetup_9/releases/tag/realm_meetup_9
  19. 19. アプリの動作 https://github.com/keiji/realm_meetup_9/releases/tag/realm_meetup_9 {
 "characters": [
 {
 "name": "Uduki Shimamura",
 "age": "17",
 "megane": false
 },
 {
 "name": "Rin Shibuya",
 "age": "15",
 "megane": false
 },
 {
 "name": "Mio Honda",
 "age": "15",
 "megane": false
 },
 {
 "name": "Hina Araki",
 "age": "20",
 "megane": false
 },
 characters.json Database ユーザー 検索 & 一覧表示 JSONパース 保存 19
  20. 20. 検索条件 {
 "characters": [
 {
 "name": "Uduki Shimamura",
 "age": "17",
 "megane": false
 },
 {
 "name": "Rin Shibuya",
 "age": "15",
 "megane": false
 },
 {
 "name": "Haruna Kamijo",
 "age": "18",
 "megane": true
 }, これがtrue https://github.com/keiji/realm_meetup_9/releases/tag/realm_meetup_9 20
  21. 21. 実行画面 21
  22. 22. javax.crypto 22 •Androidに組み込まれている暗号化フレームワーク •AES 256-bitに対応している 暗号化したデータベースファイルを、 利用時に限定して復号する
  23. 23. @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 setContentView(R.layout.user_list);
 mListView = (ListView) findViewById(R.id.listview);
 
 getSupportLoaderManager().restartLoader(LOADER_ID_DECRYPT, null, mCipherLoaderCallback);
 }
 
 @Override
 protected void onDestroy() {
 super.onDestroy();
 
 mDb.close();
 
 new EncryptTask(getDatabasePath(DbHelper.DB_FILE_NAME)).execute();
 } sqlite.SQLiteActivity 23
  24. 24. private static final String TRANSFORMATION = "AES/CBC/PKCS7Padding";
 private static final byte[] KEY = "thisismypa55w0rdthisismypa55w0rd".getBytes();
 sqlite.SQLiteActivity 24 SecretKeySpec key = new SecretKeySpec(KEY, "AES");
 
 Cipher cipher = Cipher.getInstance(TRANSFORMATION);
 cipher.init(Cipher.ENCRYPT_MODE, key);
 
 byte[] buffer = new byte[cipher.getBlockSize()];
 int len;
 
 while ((len = is.read(buffer)) != -1) {
 byte[] encrypted = cipher.update(buffer, 0, len);
 os.write(encrypted);
 }
 os.write(cipher.doFinal());
 
 rawDbFile.deleteOnExit(); 32byte必要
  25. 25. テーブル構造 25 private static final String CREATE_TABLE_USERS = "CREATE TABLE characters ( " +
 "_id INTEGER PRIMARY KEY," +
 "name TEXT," +
 "age INTEGER," +
 "megane INTEGER" +
 " )";
 public static Character read(Cursor cursor) {
 Character user = new Character();
 user.name = cursor.getString(cursor.getColumnIndex("name"));
 user.age = cursor.getInt(cursor.getColumnIndex("age"));
 user.megane = cursor.getInt(cursor.getColumnIndex("megane")) == 1;
 return user;
 }
 
 public static Cursor findAllMeganeCursor(SQLiteDatabase db) {
 return db.query("characters", new String[]{"_id", "name", "age", "megane"},
 "megane = ?", new String[]{Integer.toString(1)}, null, null, null);
 }
 sqlite.DbHelper.java sqlite.entity.Character.java
  26. 26. 問題点 26 復号してデータベースにアクセスしている間、
 暗号化されていないデータがファイルシステム上に存在する
  27. 27. SQLCipher 27 •Zetetic社が開発、提供する暗号化機能付きSQLite •AES 256-bitで暗号化 •透過的にアクセス可能 暗号化したままデータを読み書きする
  28. 28. 公式ではバイナリを配布せず 28 SQLCipherは、基本的に有償版を推しているイメージ。 オープンソース版のCommunity Editionは個々でビルドする
  29. 29. dependencies {
 compile fileTree(dir: 'libs', include: ['*.jar'])
 testCompile 'junit:junit:4.12'
 compile 'com.android.support:appcompat-v7:23.1.1'
 compile 'net.zetetic:android-database-sqlcipher:3.3.1-2'
 
 }
 BintrayでAndroid版を入手 app/build.gradle 29
  30. 30. private static final String DB_PASSWORD = "pa55w0rd";
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 SQLiteDatabase.loadLibs(this);
 
 setContentView(R.layout.user_list);
 mListView = (ListView) findViewById(R.id.listview);
 
 if (!getDatabasePath(DbHelper.DB_FILE_NAME).exists()) {
 mDb = new DbHelper(this).getWritableDatabase(DB_PASSWORD);
 getSupportLoaderManager().restartLoader(LOADER_ID, null, mLoaderCallback);
 sqlcipher.SQLCipherActivity 30
  31. 31. テーブル構造 31 private static final String CREATE_TABLE_USERS = "CREATE TABLE characters ( " +
 "_id INTEGER PRIMARY KEY," +
 "name TEXT," +
 "age INTEGER," +
 "megane INTEGER" +
 " )";
 public static Character read(Cursor cursor) {
 Character user = new Character();
 user.name = cursor.getString(cursor.getColumnIndex("name"));
 user.age = cursor.getInt(cursor.getColumnIndex("age"));
 user.megane = cursor.getInt(cursor.getColumnIndex("megane")) == 1;
 return user;
 }
 
 public static Cursor findAllMeganeCursor(SQLiteDatabase db) {
 return db.query("characters", new String[]{"_id", "name", "age", "megane"},
 "megane = ?", new String[]{Integer.toString(1)}, null, null, null);
 }
 sqlcipher.DbHelper.java sqlcipher.entity.Character.java
  32. 32. java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[ DexPathList[ [zip file "/data/app/io.keiji.realmsample2-2/base.apk"], nativeLibraryDirectories=[/data/app/io.keiji.realmsample2-2/lib/arm64, /data/app/io.keiji.realmsample2-2/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]] couldn't find "libstlport_shared.so" at java.lang.Runtime.loadLibrary(Runtime.java:367) at java.lang.System.loadLibrary(System.java:1076) at net.sqlcipher.database.SQLiteDatabase.loadLibs(SQLiteDatabase.java:173) at net.sqlcipher.database.SQLiteDatabase.loadLibs(SQLiteDatabase.java:169) at io.keiji.realmsample2.sqlcipher.SQLCipherActivity.onCreate(SQLCipherActivity.java:83) at android.app.Activity.performCreate(Activity.java:6237) Android 6.0(Marshmallow) 32
  33. 33. 問題点 33 SQLCipherのSQLiteDatabaseクラスは、
 net.sqlcipher.databaseパッケージに所属している。 SQLiteDatabaseとの継承関係もないため、
 一般的なORMとの相性は良くないと思われる
  34. 34. Realm 34 •Realm社が開発、提供するNoSQLデータベース •AES 256-bitで暗号化 •透過的にアクセス可能 読み書き中もデータは暗号化されたまま データが抜き出されても現実的な時間では解読が困難
  35. 35. public class RealmAdapter {
 
 private static final byte[] KEY = "thisismypa55w0rdthisismypa55w0rdthisismypa55w0rdthisismypa55w0rd".getBytes();
 
 public RealmConfiguration getRealmConfiguration(Context context) {
 return new RealmConfiguration.Builder(context)
 .encryptionKey(KEY)
 .build();
 }
 }
 64byte必要 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 setContentView(R.layout.user_list);
 
 mRealmAdapter = new RealmAdapter();
 
 mRealm = Realm.getInstance(mRealmAdapter.getRealmConfiguration(this));
 
 realm.RealmActivity 35
  36. 36. RealmResults<Character> realmResult = mRealm
 .where(Character.class)
 .equalTo("megane", true)
 .findAll();
 mListView = (ListView) findViewById(R.id.listview);
 mAdapter = new Adapter(this, realmResult, true);
 mListView.setAdapter(mAdapter);
 情報を検索して表示 public class Adapter extends RealmBaseAdapter<Character> {
 
 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
 Character character = getItem(position);
 
 ViewHolder holder = null;
 if (convertView == null) {
 convertView = View.inflate(RealmActivity.this, R.layout.user_list_row, null);
 holder = new ViewHolder((TextView) convertView.findViewById(R.id.label));
 convertView.setTag(holder);
 }
 36
  37. 37. 遭遇した課題
  38. 38. createObjectFromJson 38 {
 "characters": [
 {
 "name": "Uduki Shimamura",
 "age": "17",
 "megane": false
 },
 {
 "name": "Rin Shibuya",
 "age": "15",
 "megane": false
 },
 {
 "name": "Haruna Kamijo",
 "age": "18",
 "megane": true
 }, public class Character extends RealmObject {
 
 private long id;
 
 private String name;
 
 private Integer age;
 
 private boolean megane = true;
 public class Characters extends RealmObject {
 
 private RealmList<Character> characters;

  39. 39. createObjectFromJson 39 public class JsonLoaderRealmJson extends AsyncTaskLoader<LoaderResult> {
 @Override
 public LoaderResult loadInBackground() {
 
 LoaderResult result;
 
 Realm realm = Realm.getInstance(realmAdapter.getRealmConfiguration(getContext()));
 
 try {
 
 realm.beginTransaction();
 realm.createObjectFromJson(Characters.class, getContext().getAssets().open(JSON_FILE_NAME));
 realm.commitTransaction();
 
 result = new LoaderResult(null);
 
 } catch (IOException e) {

  40. 40. 想定していないデータの存在 40 {
 "name": "Kirari Moroboshi",
 "age": "17",
 "megane": false
 },
 {
 "name": "Nana Abe",
 "age": "永遠の17歳",
 "megane": false
 },
 {
 "name": "Akiha Ikebukuro",
 "age": "14",
 "megane": true
 }
 ]
 public class Character extends RealmObject {
 
 private long id;
 
 private String name;
 
 private Integer age;
 
 private boolean megane = true;
 public class Characters extends RealmObject {
 
 private RealmList<Character> characters;

  41. 41. 課題の解決 - JPP 41 JsonPullParser
 https://github.com/vvakame/JsonPullParser
 を導入して、Jsonのパース処理をRealmから切り離した。 参考: RealmとJSONライブラリ by zaki50 https://speakerdeck.com/zaki50/realmtojsonraiburari
  42. 42. 課題の解決 42 @JsonModel
 public class Characters4Jpp {
 
 @JsonKey
 private List<Character> characters;
 
 public List<Character> getCharacters() {
 return characters;
 }
 
 public void setCharacters(List<Character> characters) {
 this.characters = characters;
 }
 }

  43. 43. converterを指定 43 @JsonModel
 public class Character extends RealmObject {
 
 @JsonKey
 private long id;
 
 @JsonKey
 private String name;
 
 @JsonKey(converter = AgeTokenConverter.class)
 private Integer age;
 
 @JsonKey
 private boolean megane = true;

  44. 44. converter 44 public class AgeTokenConverter extends TokenConverter<Integer> {
 
 static AgeTokenConverter converter = null;
 
 public static AgeTokenConverter getInstance() {
 if (converter == null) converter = new AgeTokenConverter();
 
 return converter;
 }
 
 @Override
 public Integer parse(JsonPullParser parser, OnJsonObjectAddListener listener) throws IOException, JsonFormatException {
 Integer value = null;
 if (parser.getEventType() == JsonPullParser.State.VALUE_STRING) {
 String str = parser.getValueString();
 try {
 value = !TextUtils.isEmpty(str) ? Integer.parseInt(str) : null;
 } catch (NumberFormatException e) {
 }
 }
 
 return value;

  45. 45. 鍵の保護を検討する
  46. 46. 鍵をどこに置く? 46 private static final String DB_PASSWORD = "pa55w0rd"; private static final byte[] KEY = "thisismypa55w0rdthisismypa55w0rdthisismypa55w0rdthisismypa55w0rd".getBytes(); private static final byte[] KEY = "thisismypa55w0rdthisismypa55w0rd".getBytes();

  47. 47. Android Keystore System 47  Android端末のセキュリティ・ハードウェア(Secure Element, Trusted Execution Environment)内に鍵情報を格納する。  Android 4.3から導入されていたがAndroid 6.0(Marshmallow)で AES共通鍵に対応するなど機能が強化された。 http://developer.android.com/intl/ja/training/articles/keystore.html
  48. 48. Android Keystore System 48  javax.cryptoと密接に連携している。  任意の情報を保存できるわけではない(=Realmの暗号化キーを直 接保存できない)。 http://developer.android.com/intl/ja/training/articles/keystore.html
  49. 49. Android Keystore System 49 Realm暗号キー 64bytes Android Keystore System 鍵情報 AES 暗号キー 64bytes 暗号化済 ユーザー認証 Storage http://developer.android.com/intl/ja/training/articles/keystore.html
  50. 50. realm_with_ask.RealmActivity 50 static final String PROVIDER_NAME = "AndroidKeyStore";
 static final String KEY_ALIAS = "auth_key"; private static void generateKey() {
 try {
 KeyGenerator keyGenerator = KeyGenerator.getInstance(
 KeyProperties.KEY_ALGORITHM_AES,
 PROVIDER_NAME);
 
 keyGenerator.init(new KeyGenParameterSpec.Builder(
 KEY_ALIAS,
 KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
 .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
 .setUserAuthenticationRequired(true)
 .setUserAuthenticationValidityDurationSeconds(
 AUTH_VALID_DURATION_IN_SECOND)
 .build());
 keyGenerator.generateKey();
 
 } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException

  51. 51. realm_with_ask.RealmActivity 51 void authorize() {
 KeyguardManager km = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
 Intent intent = km.createConfirmDeviceCredentialIntent("Android Keystore System",
 "Android Keystore Systemに保存した を使ってRealmのデータベースにアクセスします");
 
 startActivityForResult(intent, REQUEST_CREDENTIAL);
 }

  52. 52. realm_with_ask.RealmActivity 52
  53. 53. realm_with_ask.RealmActivity 53 void readyRealm() {
 try {
 Cipher cipher = Cipher.getInstance(TRANSFORMATION);
 
 byte[] encryptedKey = null;
 
 File file = new File(getFilesDir(), KEY_FILE_NAME); // 暗号化された 情報の読み込み → encryptedKey
 
 RealmAdapter.setKey(decryptRealmKey(encryptedKey, cipher));

  54. 54. realm_with_ask.RealmActivity 54 byte[] encryptRealmKey(byte[] passkey, Cipher cipher) {
 
 try {
 SecretKey key = (SecretKey) mKeyStore.getKey(KEY_ALIAS, null);
 cipher.init(Cipher.ENCRYPT_MODE, key);
 
 byte[] encrypted = cipher.doFinal(passkey);
 byte[] initializationVector = cipher.getIV();
 
 byte[] result = new byte[initializationVector.length + encrypted.length];
 System.arraycopy(initializationVector, 0, result, 0, initializationVector.length);
 System.arraycopy(encrypted, 0, result, initializationVector.length, encrypted.length);
 
 return result; Initialization Vector 16 bytes encryption key 64 bytes
  55. 55. realm_with_ask.RealmActivity 55 byte[] decryptRealmKey(byte[] encrypted, Cipher cipher) {
 byte[] initializationVector = new byte[16];
 byte[] passKey = new byte[encrypted.length - initializationVector.length];
 
 System.arraycopy(encrypted, 0, initializationVector, 0, initializationVector.length);
 System.arraycopy(encrypted, initializationVector.length, passKey, 0, passKey.length);
 
 try {
 SecretKey key = (SecretKey) mKeyStore.getKey(KEY_ALIAS, null);
 
 IvParameterSpec ivSpec = new IvParameterSpec(initializationVector);
 cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
 
 return cipher.doFinal(passKey);

  56. 56. Fingerprint API 56  Android 6.0(Marshmallow)で追加。  Android Keystore Systemをパターンロック・パスワードに 加えて指紋スキャナーで認証する。 http://developer.android.com/intl/ja/about/versions/marshmallow/android-6.0.html#fingerprint-authentication
  57. 57. RealmFingerprintActivity 57 private final AuthenticationCallback mAuthenticationCallback = new FingerprintManager.AuthenticationCallback() {
 @Override
 public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
 super.onAuthenticationSucceeded(result);
 readyRealm();
 }
 };

  58. 58. RealmFingerprintActivity 58 @Override void authorize() {
 FingerprintManager fingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE);
 
 try {
 SecretKey key = (SecretKey) mKeyStore.getKey(KEY_ALIAS, null);
 
 Cipher cipher = Cipher.getInstance(TRANSFORMATION);
 cipher.init(Cipher.ENCRYPT_MODE, key);
 
 FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher);
 
 Toast.makeText(this, "指紋スキャナーに指を当てて下さい...", Toast.LENGTH_LONG)
 .show();
 
 //noinspection ResourceType
 fingerprintManager.authenticate(cryptoObject, null, 0, mAuthenticationCallback, null);
  59. 59. RealmFingerprintActivity 59
  60. 60. 今日の内容 - まとめ
  61. 61. 今日の内容 - まとめ 61  安全にデータを保管するための方法として 「javax.cryptoによるデータベースファイルの保護」 「SQLCipherを使った保護」
 「Realmの暗号化機能」  の3パターンを、それぞれ比較した。
  62. 62. 今日の内容 - まとめ 62  「javax.cryptoによるデータベースファイルの保護」で は、SQLDatabaseのファイルを必要なときだけ復号し た。  しかし、読み書きの最中はファイルシステム上に復号し たデータファイルが存在するため、安全とは言えなかった。
  63. 63. 今日の内容 - まとめ 63  「SQLCipherを使った保護」では、SQLDatabaseのファ イルを暗号化した上で全体を復号することなく、透過的に 読み書きができた。  しかし、Android 6.0で正常な動作が確認できなかった。 また、そのままではActiveAndroidなどORMを適用しに くいという課題もあった。
  64. 64. 今日の内容 - まとめ 64  Realmを使うと、データベースを暗号で保護した状態 で、透過的に扱うことができた。  JSONからモデルに変換する機能は、異常値が含まれる 場合などは対応しきれないケースは、JsonPullParserの converterを使って解決した。  
  65. 65. 今日の内容 - まとめ 65  「Android Keystore System」を使うことで、機密 データの復号鍵をセキュリティ・ハードウェアに保護され た領域に保存して、安全に格納することができる。  ただし、Realmはjavax.crypto.Cipherを直接扱えない ため、暗号化に用いるキーをAndroid Keystore System を用いて暗号化。キーをファイルシステムに保存した。
  66. 66. C-LIS CO., LTD. 各製品名・ブランド名、会社名などは、一般に各社の商標または登録商標です。本資料中では、©、®、™を割愛しています。 本資料は、有限会社シーリスの著作物です。掲載されているイラストは、特に記載がない場合は根雪れいの著作物です。 本資料の全部、または一部について、著作者から文書による許諾を得ずに複製することは禁じられています。 Material Icons are reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 4.0 Attribution license.
  67. 67. http://techbooster.github.io/c89/ Copyright TechBooster
  68. 68. C89 - TechBooster 3日目東シ58a Copyright TechBooster
  69. 69. C89 - TechBooster 3日目東シ58a Copyright TechBooster

×