Android: AndroidKeyStore 对数据进行签名和验证

采用的是RSA加密方式进行签名和验证,同时把密钥放在AndroidKeyStore中,增加安全系数。

效果如下:

这里写图片描述

  • 生成RSA秘钥工具类:KeyStoneUtils
package tsou.com.encryption.androidkeystoresign;

import android.content.Context;
import android.os.Build;
import android.security.KeyPairGeneratorSpec;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.support.annotation.RequiresApi;
import android.util.Base64;
import android.util.Log;

import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Calendar;
import java.util.GregorianCalendar;

import javax.security.auth.x500.X500Principal;

/**
 * Created by Administrator on 2017/10/10 0010.
 */

public class KeyStoneUtils {
    /**
     * 自己给你的别名,方便在keystore中查找秘钥
     */
    public static final String SAMPLE_ALIAS = "xiaoGuoKey";

    /**
     * 自己给你的别名  就是SAMPLE_ALIAS
     */
    private static String mAlias = null;

    public static void setAlias(String alias) {
        mAlias = alias;
    }

    /**
     * 创建一个公共和私人密钥,并将其存储使用Android密钥存储库中,因此,只有
     * 这个应用程序将能够访问键。
     *
     * @param context
     * @throws InvalidAlgorithmParameterException
     * @throws NoSuchProviderException
     * @throws NoSuchAlgorithmException
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public static void createKeys(Context context) throws InvalidAlgorithmParameterException,
            NoSuchProviderException, NoSuchAlgorithmException {
        setAlias(SAMPLE_ALIAS);
        //创建一个开始和结束时间,有效范围内的密钥对才会生成。
        Calendar start = new GregorianCalendar();
        Calendar end = new GregorianCalendar();
        end.add(Calendar.YEAR, 1);//往后加一年
        AlgorithmParameterSpec spec;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            //使用别名来检索的key 。这是一个key 的key !
            spec = new KeyPairGeneratorSpec.Builder(context)
                    //使用别名来检索的关键。这是一个关键的关键!
                    .setAlias(mAlias)
                    // 用于生成自签名证书的主题 X500Principal 接受 RFC 1779/2253的专有名词
                    .setSubject(new X500Principal("CN=" + mAlias))
                    //用于自签名证书的序列号生成的一对。
                    .setSerialNumber(BigInteger.valueOf(1337))
                    // 签名在有效日期范围内
                    .setStartDate(start.getTime())
                    .setEndDate(end.getTime())
                    .build();
        } else {
            //Android 6.0(或者以上)使用KeyGenparameterSpec.Builder 方式来创建,
            // 允许你自定义允许的的关键属性和限制
//            String AES_MODE_CBC = KeyProperties.KEY_ALGORITHM_AES + "/" +
//                    KeyProperties.BLOCK_MODE_CBC + "/" +
//                    KeyProperties.ENCRYPTION_PADDING_PKCS7;
            spec = new KeyGenParameterSpec.Builder(mAlias, KeyProperties.PURPOSE_SIGN)
                    .setCertificateSubject(new X500Principal("CN=" + mAlias))
                    .setDigests(KeyProperties.DIGEST_SHA256)
                    .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
                    .setBlockModes(KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CTR,
                            KeyProperties.BLOCK_MODE_CBC, KeyProperties.BLOCK_MODE_ECB)
//                    .setBlockModes(KeyProperties.BLOCK_MODE_GCM/CTR/CBC/ECB)
                    .setCertificateSerialNumber(BigInteger.valueOf(1337))
                    .setCertificateNotBefore(start.getTime())
                    .setCertificateNotAfter(end.getTime())
                    .build();
        }
        KeyPairGenerator kpGenerator = KeyPairGenerator
                .getInstance(SecurityConstants.TYPE_RSA,
                        SecurityConstants.KEYSTORE_PROVIDER_ANDROID_KEYSTORE);
        kpGenerator.initialize(spec);
        KeyPair kp = kpGenerator.generateKeyPair();
        Log.d("huangxiaoguo", "公共密钥: " + kp.getPublic().toString());
        Log.d("huangxiaoguo", "私钥: " + kp.getPrivate().toString());

    }

    /**
     * 签名
     *
     * @param inputStr
     * @return
     * @throws KeyStoreException
     * @throws CertificateException
     * @throws NoSuchAlgorithmException
     * @throws IOException
     * @throws UnrecoverableEntryException
     * @throws InvalidKeyException
     * @throws SignatureException
     */
    public static String signData(String inputStr) throws KeyStoreException, CertificateException,
            NoSuchAlgorithmException, IOException, UnrecoverableEntryException,
            InvalidKeyException, SignatureException {
        byte[] data = inputStr.getBytes();
        //AndroidKeyStore
        KeyStore ks = KeyStore.getInstance(SecurityConstants.KEYSTORE_PROVIDER_ANDROID_KEYSTORE);
        // 如果你没有InputStream加载,你仍然需要
        //称之为“负载”,或者它会崩溃
        ks.load(null);
        if (mAlias == null) {
            setAlias(SAMPLE_ALIAS);
        }
        //从Android加载密钥对密钥存储库中
        KeyStore.Entry entry = ks.getEntry(mAlias, null);
         /* *
         *进行判断处理钥匙是不是存储的当前别名下 不存在要遍历别名列表Keystore.aliases()
         */
        if (entry == null) {
            Log.w("huangxiaoguo", "No key found under alias: " + mAlias);
            Log.w("huangxiaoguo", "Exiting signData()...");
            return null;
        }
        if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
            Log.w("huangxiaoguo", "Not an instance of a PrivateKeyEntry");
            Log.w("huangxiaoguo", "Exiting signData()...");
            return null;
        }
        // 开始签名
        Signature s = Signature.getInstance(SecurityConstants.SIGNATURE_SHA256withRSA);
        //初始化使用指定的私钥签名
        s.initSign(((KeyStore.PrivateKeyEntry) entry).getPrivateKey());
        // 签名并存储结果作为Base64编码的字符串。
        s.update(data);
        byte[] signature = s.sign();
        String result = Base64.encodeToString(signature, Base64.DEFAULT);
        return result;
    }

    /**
     * 校验签名的字符串
     *
     * @param input
     * @param signatureStr
     * @return
     * @throws KeyStoreException
     * @throws CertificateException
     * @throws NoSuchAlgorithmException
     * @throws IOException
     * @throws UnrecoverableEntryException
     * @throws InvalidKeyException
     * @throws SignatureException
     */
    public static boolean verifyData(String input, String signatureStr) throws KeyStoreException,
            CertificateException, NoSuchAlgorithmException, IOException,
            UnrecoverableEntryException, InvalidKeyException, SignatureException {

        //要验证的数据
        byte[] data = input.getBytes();
        //签名
        byte[] signature;


        //判断签名是否存在
        if (signatureStr == null) {
            Log.w("huangxiaoguo", "Invalid signature.");
            Log.w("huangxiaoguo", "Exiting verifyData()...");
            return false;
        }

        try {

            //Base64解码字符串
            signature = Base64.decode(signatureStr, Base64.DEFAULT);
        } catch (IllegalArgumentException e) {
            return false;
        }
        KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
        ks.load(null);

        // Load the key pair from the Android Key Store
        if (mAlias == null) {
            setAlias(SAMPLE_ALIAS);
        }
        //从Android加载密钥对密钥存储库中
        KeyStore.Entry entry = ks.getEntry(mAlias, null);
        if (entry == null) {
            Log.w("huangxiaoguo", "No key found under alias: " + mAlias);
            Log.w("huangxiaoguo", "Exiting verifyData()...");
            return false;
        }
        if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
            Log.w("huangxiaoguo", "Not an instance of a PrivateKeyEntry");
            return false;
        }
        Signature s = Signature.getInstance(SecurityConstants.SIGNATURE_SHA256withRSA);
        // 开始校验签名
        s.initVerify(((KeyStore.PrivateKeyEntry) entry).getCertificate());
        s.update(data);
        boolean valid = s.verify(signature);
        return valid;//true 签名一致

    }

    /**
     * 判断是否创建过秘钥
     *
     * @return
     * @throws KeyStoreException
     * @throws CertificateException
     * @throws NoSuchAlgorithmException
     * @throws IOException
     * @throws UnrecoverableEntryException
     */
    public static boolean isHaveKeyStore() {
        try {
            KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
            ks.load(null);
            if (mAlias == null) {
                setAlias(SAMPLE_ALIAS);
            }
            // Load the key pair from the Android Key Store
            //从Android加载密钥对密钥存储库中
            KeyStore.Entry entry = ks.getEntry(mAlias, null);
            if (entry == null) {
                return false;
            }
        } catch (KeyStoreException e) {
            e.printStackTrace();
            return false;
        } catch (CertificateException e) {
            e.printStackTrace();
            return false;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } catch (UnrecoverableEntryException e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }
}

package tsou.com.encryption.androidkeystoresign;


public class SecurityConstants {
    public static final String KEYSTORE_PROVIDER_ANDROID_KEYSTORE = "AndroidKeyStore";

    public static final String TYPE_RSA = "RSA";
    public static final String TYPE_DSA = "DSA";
    public static final String TYPE_BKS = "BKS";

    public static final String SIGNATURE_SHA256withRSA = "SHA256withRSA";
    public static final String SIGNATURE_SHA512withRSA = "SHA512withRSA";
}
  • 执行操作
package tsou.com.encryption.activity.AndroidKeyStore;

import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;

import tsou.com.encryption.R;
import tsou.com.encryption.aescbc.Base64Encoder;
import tsou.com.encryption.androidkeystoresign.KeyStoneUtils;

/**
 * Android AndroidKeyStore 对数据进行签名和验证
 */
public class AndroidKeyStoreActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText encryptionContext;
    private Button encryption;
    private TextView tvEncryption;
    private Button decode;
    private TextView tvDecode;
    private Activity mActivity;
    private Context mContext;
    private Button encryptionNotFixed;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_or);
        mActivity = this;
        mContext = this;
        encryptionContext = (EditText) findViewById(R.id.et_encryption_context);
        encryption = (Button) findViewById(R.id.btn_encryption);
        encryptionNotFixed = (Button) findViewById(R.id.btn_encryption_not_fixed);
        tvEncryption = (TextView) findViewById(R.id.tv_encryption);
        decode = (Button) findViewById(R.id.btn_decode);
        tvDecode = (TextView) findViewById(R.id.tv_decode);
        encryption.setText("创建Key");
        encryptionNotFixed.setText("签名");
        decode.setText("验证");
        initListener();
    }

    private void initListener() {
        encryption.setOnClickListener(this);
        encryptionNotFixed.setOnClickListener(this);
        decode.setOnClickListener(this);
    }


    private String mSignatureStr = null;


    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            /**
             * 在应用安装后第一次运行时,生成一个随机密钥,并存入 KeyStore
             当你想存储一个数据,便从 KeyStore 中取出之前生成的随机密钥,对你的数据进行加密,
             加密完成后,已完成加密的数据可以随意存储在任意地方,比如 SharePreferences,
             此时即使它被他人读取到,也无法解密出你的原数据,因为他人取不到你的密钥
             当你需要拿到你的原数据,只需要从 SharePreferences 中读取你加密后的数据,
             并从 KeyStore 取出加密密钥,使用加密密钥对 “加密后的数据” 进行解密即可
             */
            case R.id.btn_encryption://创建Key,在项目中放在application或启动页中
                if (KeyStoneUtils.isHaveKeyStore()) {//是否有秘钥
                    Toast.makeText(mContext, "已经创建过秘钥", Toast.LENGTH_SHORT).show();
                    return;
                }
                try {
                    KeyStoneUtils.createKeys(mContext);
                    Toast.makeText(mContext, "秘钥创建成功", Toast.LENGTH_SHORT).show();
                } catch (InvalidAlgorithmParameterException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "不支持RSA", Toast.LENGTH_SHORT).show();
                } catch (NoSuchProviderException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "没有提供:AndroidKeyStore", Toast.LENGTH_SHORT).show();
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "无效的算法参数异常", Toast.LENGTH_SHORT).show();
                }
                break;

            case R.id.btn_encryption_not_fixed://签名
                String encryptionString = encryptionContext.getText().toString().trim();
                if (TextUtils.isEmpty(encryptionString)) {
                    Toast.makeText(mContext, "请输入签名内容", Toast.LENGTH_SHORT).show();
                    return;
                }
                if (!KeyStoneUtils.isHaveKeyStore()) {//是否有秘钥
                    Toast.makeText(mContext, "没有生成秘钥对,请先创建key", Toast.LENGTH_SHORT).show();
                    return;
                }

                try {
                    mSignatureStr = KeyStoneUtils.signData(encryptionString);
                    tvEncryption.setText(Base64Encoder.encode(mSignatureStr));
                } catch (KeyStoreException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "密钥存储库没有初始化,可能没有生成秘钥对", Toast.LENGTH_SHORT).show();
                } catch (CertificateException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "加载证书时发生错误", Toast.LENGTH_SHORT).show();
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "不支持RSA", Toast.LENGTH_SHORT).show();
                } catch (IOException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "IO Exception", Toast.LENGTH_SHORT).show();
                } catch (UnrecoverableEntryException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "密钥对没有恢复", Toast.LENGTH_SHORT).show();
                } catch (InvalidKeyException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "无效的key", Toast.LENGTH_SHORT).show();
                } catch (SignatureException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "无效的签名", Toast.LENGTH_SHORT).show();
                }
                break;

            case R.id.btn_decode://验证

                String encryptionverifyString = encryptionContext.getText().toString().trim();

                if (TextUtils.isEmpty(encryptionverifyString)) {
                    Toast.makeText(mContext, "请输入验证内容", Toast.LENGTH_SHORT).show();
                    return;
                }
                if (TextUtils.isEmpty(mSignatureStr)) {
                    Toast.makeText(mContext, "请先签名", Toast.LENGTH_SHORT).show();
                    return;
                }
                try {
                    boolean b = KeyStoneUtils.verifyData(encryptionverifyString, mSignatureStr);
                    if (b) {
                        tvDecode.setText("签名一致");
                    } else {
                        tvDecode.setText("签名不一致");
                    }
                } catch (KeyStoreException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "密钥存储库没有初始化,可能没有生成秘钥对", Toast.LENGTH_SHORT).show();
                } catch (CertificateException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "加载证书时发生错误", Toast.LENGTH_SHORT).show();
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "不支持RSA", Toast.LENGTH_SHORT).show();
                } catch (IOException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "IO Exception", Toast.LENGTH_SHORT).show();
                } catch (UnrecoverableEntryException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "密钥对没有恢复", Toast.LENGTH_SHORT).show();
                } catch (InvalidKeyException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "无效的key", Toast.LENGTH_SHORT).show();
                } catch (SignatureException e) {
                    e.printStackTrace();
                    Toast.makeText(mContext, "无效的签名", Toast.LENGTH_SHORT).show();
                }

                break;
        }
    }
}

更多加密方式DEMO请查看:https://gitee.com/huangxiaoguo/androidGeZhongJiaMiZongJie

更多加密方式博文请查看:http://blog.csdn.net/huangxiaoguo1/article/details/78043354

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值