あるSEのつぶやき・改

ITやシステム開発などの技術に関する話題を、取り上げたりしています。

React Nativeで暗号キーなどの重要情報を安全な場所に保存する

はじめに

スマートフォンのアプリで暗号キーなどの重要情報を安全に保存するニーズは高いですね。

それは、アプリで普通に保存してしまうと、他のアプリや攻撃者に重要情報を取得される危険性があるからです。

この危険性を防ぐために、iOS では KeyChain、Android では KeyStore という、重要情報を安全に保存する仕組みがあります。

この KeyChain と KeyStore に、 React Native から重要情報を保存する方法をご紹介します。

使用するライブラリ

iOS では KeyChain、Android では KeyStore に暗号キーなどの重要情報を保存するために、 rn-secure-storageというライブラリを使用します。rn-secure-storageは任意のキーを任意の数だけ保存できるため使い勝手がよいものになっています。

ちなみに、react-native-keychainというライブラリは、ユーザーIDとパスワードの固定の組み合わせを一組しか保存できません。react-native-keychainの使用方法については、以下の記事を参考にしてください。

www.aruse.net

なお、rn-secure-storageは、iOS ではバージョンに関係なく KeyChain に保存するので問題はありません。

ですが、Android では API のバージョンが 23 (Android 6.0)以下だと KeyStore ではなくsecure-preferencesというライブラリを使用するので注意が必要です。API のバージョンが 24(Android 7.0)以降では KeyStore を使用するので安全です。

また、ログに OS 情報を出力するために、react-native-device-infoというライブラリも使用します。

プロジェクトの作成

React Native のプロジェクトを作成します。今回は TypeScript を使用するので、以下のようにコマンドを実行します。

$ npx react-native init KeySecure --template react-native-template-typescript

インストール

下記コマンドを実行して、必要なライブラリをインストールします。

$ yarn add rn-secure-storage
$ yarn add react-native-device-info
$ cd ios && pod install && cd ..

App.tsx の編集

App.tsxを以下のように編集します。

内容は見てのとおりですが、key と value の組み合わせを保存、取得、存在チェック、削除しています。なお、保存時に存在している key で保存すると上書きされます。

import React, {useEffect} from 'react';
import {SafeAreaView, Text} from 'react-native';
import DeviceInfo from 'react-native-device-info';
import RNSecureStorage, {ACCESSIBLE} from 'rn-secure-storage';

const App = () => {
  useEffect(() => {
    const storeKeys = async () => {
      // Retrieve the systemname
      const systemName = DeviceInfo.getSystemName();
      console.log(systemName);

      // Set key/value
      console.log('Set key/value.');

      try {
        // {accessible: ACCESSIBLE.WHEN_UNLOCKED} -> This for IOS
        const res1 = await RNSecureStorage.set('key1', 'This is value 1.', {
          accessible: ACCESSIBLE.WHEN_UNLOCKED,
        });
        console.log('key1:' + res1);

        const res2 = await RNSecureStorage.set('key2', 'This is value 2.', {
          accessible: ACCESSIBLE.WHEN_UNLOCKED,
        });
        console.log('key2:' + res2);
      } catch (error) {
        console.log(error);
      }

      // Get key/value
      console.log('Get key/value.');

      try {
        const value1 = await RNSecureStorage.get('key1');
        console.log('key1=' + value1);

        const value2 = await RNSecureStorage.get('key2');
        console.log('key2=' + value2);
      } catch (error) {
        console.log(error);
      }

      // Exists check
      console.log('Exists check.');

      try {
        // res -> is can be True or False
        const res1 = await RNSecureStorage.exists('key1');
        console.log(res1 ? 'key1:exists' : 'key1:not exists');

        const res2 = await RNSecureStorage.exists('key2');
        console.log(res2 ? 'key2:exists' : 'key2:not exists');

        // Nothing
        const res3 = await RNSecureStorage.exists('key3');
        console.log(res3 ? 'key3:exists' : 'key3:not exists');
      } catch (error) {
        console.log(error);
      }

      // Remove key/value
      console.log('Remove key/value.');

      try {
        const res1 = await RNSecureStorage.remove('key1');
        console.log('key1:' + res1);

        const res2 = await RNSecureStorage.remove('key2');
        console.log('key2:' + res2);
      } catch (error) {
        console.log(error);
      }
    };

    storeKeys();
  }, []);

  return (
    <>
      <SafeAreaView>
        <Text>Hello</Text>
      </SafeAreaView>
    </>
  );
};

export default App;

実行

iOS で動作させるために、下記コマンドを実行します。

$ react-native run-ios

ログが正しく出力されることが分かります。

f:id:fnyablog:20200320184648p:plain:w640

なお、iOS でログが出力されない場合は、下記記事を参考にしてください。

www.aruse.net

Android で動作させるために、下記コマンドを実行します。

$ react-native run-android

こちらも、ログが正しく出力されることが分かります。

f:id:fnyablog:20200320184854p:plain:w640

おわりに

React Native で暗号キーなどの重要情報に安全な場所に保存する方法を見てきました。

日本語情報は相変わらずないですが、ライブラリを使用すれば比較的簡単に KeyChain や KeyStore に保存できることが分かったかと思います。

参考サイト

blog.jscrambler.com