在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ 產品經理/ 如何使用安卓密鑰庫存儲密碼和其他敏感信息
5 個提示助你設計出精妙的 Apple Watch 應用軟件
如何使用安卓密鑰庫存儲密碼和其他敏感信息
從 HDFS 中使用分布式的 MAP REDUCE JOB 寫入 CASSANDRA
現(xiàn)代 Javascript 工具漫游指南
理解 Cassandra 壓縮儲存的作用
不懂 JavaScript?那你就不是一個 Web 開發(fā)者
如何開發(fā)一個簡單的 Android Wear 應用程序
Angular 與 React 的比拼
谷歌加入 OpenStack 基金會的 4 個理由
15個很有用的面向設計師的 UI 和 UX 設計工具及資源
DevTools 摘要: 處理條帶化數據時給條帶化數據的一個新家
為什么是 Node.js ? 什么時候使用 Node.js ?
玩轉 Dcoker:Hello World, 開發(fā)環(huán)境和你的應用
運行時的掛鉤 C 函數
在游戲開發(fā)中獲得成功
在 iOS 開發(fā)中使用 TWITTERKIT & DIGITS
使用 ionic 將數據保存到本地存儲中
20 個有用的 Angular.js 工具
如何成為一個超級軟件開發(fā)者
用 Go 語言來看 Android! 出發(fā), Android, 出發(fā)!

如何使用安卓密鑰庫存儲密碼和其他敏感信息

文章翻譯:趙潔
發(fā)表時間:2015 年 7 月 10 日
原文作者:OBARO OGBO
文章分類:云計算與安全

關于本文

本文主要介紹如何使用安卓密鑰庫來輕松地管理應用程序密鑰。有了密鑰庫,你就會發(fā)現(xiàn),無論是存儲密鑰、還是刪除密鑰,以及加密和解密用戶所提供的文本,都不再是難題。所以不妨來學習一下吧。

文章內容

幾個月前,Godfrey Nolan 寫了一篇超棒的文章,討論了安卓應用程序的開發(fā)者如何存儲用戶的密碼和敏感/私人信息。安卓密鑰庫提供一個安全系統(tǒng)等級證書存儲。有了密鑰庫,一個應用程序可以創(chuàng)建一個新的私人的/公共的密鑰對,并在保存到私人存儲文件夾之前就可以用于加密應用程序的秘密。在本文中,我們將要展示如何使用安卓密鑰庫來創(chuàng)建和刪除密鑰,以及如何使用這些密鑰來加密和解密用戶提供的文本。

準備

在我們編碼之前,了解一點關于安卓密鑰庫的事情以及其性能是很有幫助的。密鑰庫不是直接用來存儲應用程序的秘密的,比如說密碼,而是提供一個安全容器,應用程序用其來存儲私鑰,在某種程度上對于惡意(未經授權)的用戶和應用程序來說,檢索是相當困難的。

正如其名,一個應用程序可以在密鑰庫里存儲多樣的密鑰,但是只能查看和問詢它自己的密鑰。理想上,有了密鑰庫,應用程序可以生成或獲取一個存儲在密鑰庫的私人或公共的密鑰對。公鑰可用于加密應用程序的秘密,在其被存儲在應用程序指定的文件夾之前。而私鑰是在需要的時候,解密相同的信息。

盡管安卓密鑰庫供應商在 API 等級 18(安卓 4.3)中引進,密鑰庫本身是在 API 1 的時候可被使用,由 VPN 和 WiFi 系統(tǒng)限制使用。

密鑰庫本身是使用用戶自身的鎖屏 pin 或者密碼來加密的,因此,當裝置屏幕鎖上的話,密鑰庫是不能使用的。謹記如果你有一個后臺服務,可能需要訪問你的應用程序的秘密。

布局

我們實例化應用程序的主要布局是一個 ListView,由應用程序所創(chuàng)建的所有密鑰(實際上是密鑰別名或名稱)所組成的項目。保存為 layout/activity_main.xml。

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/listView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true"
    tools:context="com.sample.foo.simplekeystoreapp.MainActivity">
</ListView>

列表上的每一個項目包含一個 TextView,代表了密鑰的別名,一個刪除密鑰的按鈕,以及加密和解密文本的按鈕。這是我們項目中的 layout/list_item.xml。

http://wiki.jikexueyuan.com/project/wiki-journal-201507-1/images/aa_keystore_list_item-300x88.jpg" alt="image" />

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/cardBackground"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    card_view:cardCornerRadius="4dp"
    android:layout_margin="5dp">

    <TextView
        android:id="@+id/keyAlias"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:textSize="30dp"/>

    <Button
        android:id="@+id/deleteButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/keyAlias"
        android:layout_alignParentLeft="true"
        android:layout_centerHorizontal="true"
        android:text="@string/delete"
        style="@style/Base.Widget.AppCompat.Button.Borderless" />

    <Button
        android:id="@+id/encryptButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/keyAlias"
        android:layout_alignRight="@+id/keyAlias"
        android:text="@string/encrypt"
        style="@style/Base.Widget.AppCompat.Button.Borderless"/>

    <Button
        android:id="@+id/decryptButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/keyAlias"
        android:layout_toLeftOf="@+id/encryptButton"
        android:text="@string/decrypt"
        style="@style/Base.Widget.AppCompat.Button.Borderless"/>
</RelativeLayout>

列表標題

使用此方法將列表標題添加到 ListView。

View listHeader = View.inflate(this, R.layout.activity_main_header, null);
listView.addHeaderView(listHeader);

http://wiki.jikexueyuan.com/project/wiki-journal-201507-1/images/aa_keystore_layout-300x533.jpg" alt="image" />

在上面的圖片中,ListView 當前是空的,所以只有列表標題是可以看到的。列表標題相當明確,在頂端是一個 EditText,當創(chuàng)建一個密鑰時需要一個字符串作為別名。生成新的密鑰的按鈕就在這個的下方。按鈕后是三個 EditTexts,一個是需要輸入的字符串被加密,另一個展示了加密的結果,然后第三個展示了解密字符串(一個成功的解密)。此文件保存在 layout/activity_main_header.xml。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin">

    <EditText
        android:id="@+id/aliasText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:hint="@string/key_alias"/>

    <Button
        android:id="@+id/generateKeyPair"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/aliasText"
        android:layout_centerHorizontal="true"
        android:layout_alignParentRight="true"
        android:text="@string/generate"
        android:onClick="createNewKeys" />

    <EditText
        android:id="@+id/startText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/generateKeyPair"
        android:layout_centerHorizontal="true"
        android:hint="@string/initial_text"/>

    <EditText
        android:id="@+id/encryptedText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/startText"
        android:layout_centerHorizontal="true"
        android:editable="false"
        android:textIsSelectable="true"
        android:hint="@string/final_text"/>

    <EditText
        android:id="@+id/decryptedText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/encryptedText"
        android:layout_centerHorizontal="true"
        android:editable="false"
        android:textIsSelectable="true"
        android:hint="@string/decrypt_result"/>
</RelativeLayout>

主要活動

關于任何活動,我們開始都使用 onCreate() 方法。我們做的第一件事就是引用 AndroidKeyStore,然后對其初始化,使用:

Keystore.getInstance("AndroidKeyStore");
keystore.load(null)

然后我們調用 refreshKeys() 方法(接下來討論)來列出我們應用程序存儲在密鑰庫所有的密鑰。這個保證了在密鑰庫中的任何的密鑰在 ListView 初始化之后立即顯示。

列出密鑰庫所有的密鑰

http://wiki.jikexueyuan.com/project/wiki-journal-201507-1/images/aa_keystore_keys-300x533.jpg" alt="image" />

要獲取可在密鑰庫應用程序中所有密鑰的列舉,只需調用 aliases() 方法。在我們下方的 refreshKeys() 方法中,我們獲取密鑰庫別名,然后把返回的字符串放置到一個 ArrayList(由我們 ListView 的適配器所使用)中。

    private void refreshKeys() {
        keyAliases = new ArrayList<>();
        try {
            Enumeration<String> aliases = keyStore.aliases();
            while (aliases.hasMoreElements()) {
                keyAliases.add(aliases.nextElement());
            }
        }
        catch(Exception e) {}

        if(listAdapter != null)
            listAdapter.notifyDataSetChanged();
    }

在密鑰庫中添加一個新密鑰

http://wiki.jikexueyuan.com/project/wiki-journal-201507-1/images/aa_keystore_new_key-300x533.jpg" alt="image" />

每一個由應用程序創(chuàng)建的密鑰必須有一個獨特的別名,可以是任何字符串。我們使用一個 KeyPairGeneratorSpec 對象來創(chuàng)建我們所需密鑰的規(guī)格。你可以設置密鑰(setStartDate() 和 setEndDate())的有效期,設置別名和其他的主體(自簽密鑰)。該主體必須是一個 X500Principal 對象,解析到一個字符串的格式“CN=通用名稱,O=組織,C=國家”。

要生成一個私人或公共的密鑰對,我們需要一個 KeyPairGenerator 對象。我們獲取 KeyPairGenerator 集合的實例來使用 “AndroidKeyStore” 的 RSA 算法。調用 generateKeyPair() 創(chuàng)建新的密鑰對(私鑰和相應的公鑰),并將其添加到密鑰庫中。

      public void createNewKeys(View view) {
        String alias = aliasText.getText().toString();
        try {
            // Create new key if needed
            if (!keyStore.containsAlias(alias)) {
                Calendar start = Calendar.getInstance();
                Calendar end = Calendar.getInstance();
                end.add(Calendar.YEAR, 1);
                KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(this)
                        .setAlias(alias)
                        .setSubject(new X500Principal("CN=Sample Name, O=Android Authority"))
                        .setSerialNumber(BigInteger.ONE)
                        .setStartDate(start.getTime())
                        .setEndDate(end.getTime())
                        .build();
                KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
                generator.initialize(spec);

                KeyPair keyPair = generator.generateKeyPair();
            }
        } catch (Exception e) {
            Toast.makeText(this, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show();
            Log.e(TAG, Log.getStackTraceString(e));
        }
        refreshKeys();
    }

從密鑰庫中刪除密鑰

http://wiki.jikexueyuan.com/project/wiki-journal-201507-1/images/aa_keystore_delete-300x533.jpg" alt="image" />

從密鑰庫中刪除一個密鑰相當簡單。有密鑰別名做準備,調用 keystore.deleteEntry(keyAlias)。沒有辦法重新存儲一個刪除了的密鑰,所以在刪除之前要確定。

      public void deleteKey(final String alias) {
        AlertDialog alertDialog =new AlertDialog.Builder(this)
                .setTitle("Delete Key")
                .setMessage("Do you want to delete the key \"" + alias + "\" from the keystore?")
                .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        try {
                            keyStore.deleteEntry(alias);
                            refreshKeys();
                        } catch (KeyStoreException e) {
                            Toast.makeText(MainActivity.this,
                                    "Exception " + e.getMessage() + " occured",
                                    Toast.LENGTH_LONG).show();
                            Log.e(TAG, Log.getStackTraceString(e));
                        }
                        dialog.dismiss();
                    }
                })
                .setNegativeButton("No", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                })
                .create();
        alertDialog.show();
    }

加密文本塊

http://wiki.jikexueyuan.com/project/wiki-journal-201507-1/images/aa_keystore_encrypt-300x533.jpg" alt="image" />

加密一個文本塊由密鑰對的公鑰執(zhí)行。我們檢索公鑰,請求一個密碼,使用我們更喜歡的加密或解密轉換(“RSA/ECB/PKCS1Padding”),然后初始化密碼,使用檢索到的公鑰來執(zhí)行加密(Cipher.ENCRYPT_MODE)。密碼操作(和返回)一個字節(jié) []。我們將密碼包含在 CipherOutputStream 中,和 ByteArrayOutputStream 一起來處理加密復雜性。加密進程的結果就是轉化成一個顯示為 Base64 的字符串。

    public void encryptString(String alias) {
        try {
            KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
            RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();

            // Encrypt the text
            String initialText = startText.getText().toString();
            if(initialText.isEmpty()) {
                Toast.makeText(this, "Enter text in the 'Initial Text' widget", Toast.LENGTH_LONG).show();
                return;
            }

            Cipher input = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
            input.init(Cipher.ENCRYPT_MODE, publicKey);

            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            CipherOutputStream cipherOutputStream = new CipherOutputStream(
                    outputStream, input);
            cipherOutputStream.write(initialText.getBytes("UTF-8"));
            cipherOutputStream.close();

            byte [] vals = outputStream.toByteArray();
            encryptedText.setText(Base64.encodeToString(vals, Base64.DEFAULT));
        } catch (Exception e) {
            Toast.makeText(this, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show();
            Log.e(TAG, Log.getStackTraceString(e));
        }
    }

解密

解密基本上是加密的逆過程。解密是通過使用密鑰對的私鑰完成的。然后我們使用和加密相同的轉化算法來初始化一個密碼,但是設置 Cipher.DECRYPT_MODE。Base64 字符串解碼為一個字節(jié)[],然后放置在 ByteArrayInputStream 中。然后我們使用一個 CipherInputStream 來解密數據為一個字節(jié)[]。然后顯示為一個字符串。

    public void decryptString(String alias) {
        try {
            KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
            RSAPrivateKey privateKey = (RSAPrivateKey) privateKeyEntry.getPrivateKey();

            Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
            output.init(Cipher.DECRYPT_MODE, privateKey);

            String cipherText = encryptedText.getText().toString();
            CipherInputStream cipherInputStream = new CipherInputStream(
                    new ByteArrayInputStream(Base64.decode(cipherText, Base64.DEFAULT)), output);
            ArrayList<Byte> values = new ArrayList<>();
            int nextByte;
            while ((nextByte = cipherInputStream.read()) != -1) {
                values.add((byte)nextByte);
            }

            byte[] bytes = new byte[values.size()];
            for(int i = 0; i < bytes.length; i++) {
                bytes[i] = values.get(i).byteValue();
            }

            String finalText = new String(bytes, 0, bytes.length, "UTF-8");
            decryptedText.setText(finalText);

        } catch (Exception e) {
            Toast.makeText(this, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show();
            Log.e(TAG, Log.getStackTraceString(e));
        }
    }

安卓開發(fā)者簡報

你想知道更多嗎?訂閱我們的安卓開發(fā)者簡報。僅在下方輸入你的電子郵件地址,就可以每周一次在你的收件箱里獲取所有頂級的開發(fā)者信息、提示和鏈接。

PS. 永遠不會有垃圾郵件。你的郵箱地址只用于安卓開發(fā)周報。

綜述

安卓密鑰庫使創(chuàng)建和管理應用程序密鑰變得輕而易舉,并為應用程序提供了一個相對安全的庫來存儲加密密鑰。當然公鑰也可以發(fā)送到你的服務器上,服務器的公鑰可以發(fā)送到你的應用程序上,來確保應用程序和服務器之前的安全通信。按往常來說,完整的源代碼在 github 上可供你使用。想要補充、改正或討論,請在下方留下評論,我們非常期待聽到你的聲音。

更多IT技術干貨: wiki.jikexueyuan.com
加入極客星球翻譯團隊: http://wiki.jikexueyuan.com/project/wiki-editors-guidelines/translators.html

版權聲明:
本譯文僅用于學習和交流目的。非商業(yè)轉載請注明譯者、出處,并保留文章在極客學院的完整鏈接
商業(yè)合作請聯(lián)系 wiki@jikexueyuan.com
原文地址:http://www.androidauthority.com/use-android-keystore-store-passwords-sensitive-information-623779/