package com.stm.asset.utils;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.security.KeyPairGeneratorSpec;
import android.util.Base64;

import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Calendar;
import java.util.GregorianCalendar;

import javax.crypto.Cipher;
import javax.security.auth.x500.X500Principal;

/**
 * 类名称：RSAForShareUtil
 * 类描述：加密工具类，对SharedPreferences数据做RSA加密（加密会产生耗时，建议非必要数据不要加密）
 * 创建人：story
 * 创建时间：2021-08-07 09:34
 */
public class RSAForShareUtil {

    private static final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";//加密填充方式
    private static String mAlias = "stmKey";

    /**
     * 判断是否创建过秘钥
     */
    private static boolean isHaveKeyStore() {
        try {
            KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
            ks.load(null);
            //从Android加载密钥对密钥存储库中
            PrivateKey privateKey = (PrivateKey) ks.getKey(mAlias, null);
            if (privateKey == null) {
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 创建一个公共和私人密钥,并将其存储使用Android密钥存储库中,因此,只有这个应用程序将能够访问键。
     * @param context context
     */
    private static KeyPair generateRSAKeyPair(Context context) throws InvalidAlgorithmParameterException, NoSuchProviderException, NoSuchAlgorithmException {
        //创建一个开始和结束时间,有效范围内的密钥对才会生成。
        Calendar start = new GregorianCalendar();
        Calendar end = new GregorianCalendar();
        end.add(Calendar.YEAR, 5);
        //使用别名来检索的key
        AlgorithmParameterSpec 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();
        KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
        kpGenerator.initialize(spec);
        return kpGenerator.generateKeyPair();
    }

    /**
     * 获得本地AndroidKeyStore中的公钥
     */
    private static PublicKey getLocalPublicKey() {
        try {
            KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
            ks.load(null);
            //从Android加载密钥对密钥存储库中
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                return ks.getCertificate(mAlias).getPublicKey();
            } else {
                KeyStore.Entry entry = ks.getEntry(mAlias, null);
                if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
                    return null;
                } else {
                    return ((KeyStore.PrivateKeyEntry) entry).getCertificate().getPublicKey();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 加密数据
     * @param data data
     */
    private static String encryptData(Context context, String data) {
        if (data != null) {
            try {
                PublicKey publicKey;
                if (isHaveKeyStore()) {
                    publicKey = getLocalPublicKey();
                } else {
                    KeyPair keyPair = generateRSAKeyPair(context);
                    publicKey = keyPair.getPublic();
                }
                if (publicKey != null) {
                    // 得到公钥
                    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey.getEncoded());
                    KeyFactory kf = KeyFactory.getInstance("RSA");
                    PublicKey keyPublic = kf.generatePublic(keySpec);
                    // 加密数据
                    Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING);
                    cp.init(Cipher.ENCRYPT_MODE, keyPublic);
                    byte[] encryptBytes = cp.doFinal(data.getBytes());
                    return Base64.encodeToString(encryptBytes, Base64.DEFAULT);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return data;
    }

    /**
     * 用私钥解密数据
     * @param encodeData encodeData
     */
    private static String decryptData(String encodeData) {
        if (encodeData != null) {
            try {
                byte[] encodeBytes = Base64.decode(encodeData, Base64.DEFAULT);
                KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
                ks.load(null);
                //从Android加载密钥对密钥存储库中
                PrivateKey keyPrivate = null;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                    keyPrivate = (PrivateKey) ks.getKey(mAlias, null);
                } else {
                    KeyStore.Entry entry = ks.getEntry(mAlias, null);
                    if (entry instanceof KeyStore.PrivateKeyEntry) {
                        keyPrivate = ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();
                    }
                }
                if (keyPrivate != null) {
                    // 解密数据
                    Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING);
                    cp.init(Cipher.DECRYPT_MODE, keyPrivate);
                    byte[] arr = cp.doFinal(encodeBytes);
                    return new String(arr);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return encodeData;
    }

    /**
     * @param key key
     * @param isTrue isTrue
     */
    public static void setBoolean(Context context, String name, String key, boolean isTrue) {
        SharedPreferences preferences = context.getSharedPreferences(name, Context.MODE_PRIVATE);
        preferences.edit().putString(key, RSAForShareUtil.encryptData(context, isTrue ? "1" : "0")).apply();
    }

    public static boolean getBoolean(Context context, String name, String key, boolean def) {
        SharedPreferences preferences = context.getSharedPreferences(name, Context.MODE_PRIVATE);
        String str = preferences.getString(key, "0");
        if (!"0".equals(str)) {
            String i = RSAForShareUtil.decryptData(str);
            if ("1".equals(i)) {
                return true;
            } else if ("0".equals(i)) {
                return false;
            }
        }
        return def;
    }

    public static void setInt(Context context, String name, String key, int data) {
        SharedPreferences preferences = context.getSharedPreferences(name, Context.MODE_PRIVATE);
        preferences.edit().putString(key, RSAForShareUtil.encryptData(context, String.valueOf(data))).apply();
    }

    public static int getInt(Context context, String name, String key, int def) {
        SharedPreferences preferences = context.getSharedPreferences(name, Context.MODE_PRIVATE);
        String str = preferences.getString(key, "0");
        if (!"0".equals(str)) {
            String deStr = RSAForShareUtil.decryptData(str);
            if (deStr != null) {
                return Integer.parseInt(deStr);
            }
        }
        return def;
    }

    public static void setString(Context context, String name, String key, String data) {
        SharedPreferences preferences = context.getSharedPreferences(name, Context.MODE_PRIVATE);
        preferences.edit().putString(key, RSAForShareUtil.encryptData(context, data)).apply();
    }

    public static String getString(Context context, String name, String key, String def) {
        SharedPreferences preferences = context.getSharedPreferences(name, Context.MODE_PRIVATE);
        String str = preferences.getString(key, "0");
        if (!"0".equals(str)) {
            return RSAForShareUtil.decryptData(str);
        }
        return def;
    }

    public static void remove(Context context, String name, String key) {
        SharedPreferences preferences = context.getSharedPreferences(name, Context.MODE_PRIVATE);
        preferences.edit().remove(key).apply();
    }

}
