Android 采用keyStore的方式非对称加密,配合Rxjava2更好用

加密的代码如下

public class RxSecureStorage {

  private static final String AndroidKeyStore = "AndroidKeyStore";
  private Context context;
  private String alias;
  private KeyStore keyStore;
  private RxSharedPreferences sharedPreferences;

  public RxSecureStorage(Context context, String alias) {
    this.context = context.getApplicationContext();
    this.alias = alias;
    SharedPreferences prefs =
        context.getSharedPreferences(
            String.format("%s-%s", context.getPackageName(), alias), Context.MODE_PRIVATE);
    this.sharedPreferences = RxSharedPreferences.create(prefs);
  }

  public static RxSecureStorage create(Context context, String alias) {
    return new RxSecureStorage(context, alias);
  }

  private void initIfNecessary() throws Exception {
    if (keyStore != null) {
      return;
    }
    try {
      keyStore = KeyStore.getInstance(AndroidKeyStore);
      keyStore.load(null);
      if (!keyStore.containsAlias(alias)) {
        // Generate a key pair for encryption
        Calendar start = Calendar.getInstance();
        Calendar end = Calendar.getInstance();
        end.add(Calendar.YEAR, 30);
        KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context.getApplicationContext())
                .setAlias(alias)
                .setSubject(new X500Principal("CN=" + alias))
                .setSerialNumber(BigInteger.TEN)
                .setStartDate(start.getTime())
                .setEndDate(end.getTime())
                .build();
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", AndroidKeyStore);
        kpg.initialize(spec);
        kpg.generateKeyPair();
      }
    } catch (Exception e) {
      throw new Exception("Failed to initialize this RxSecureStorage instance.", e);
    }
  }

  public Single<byte[]> encrypt(final byte[] data) {
    return Single.fromCallable(
            new Callable<byte[]>() {
              @Override
              public byte[] call() throws Exception {
                initIfNecessary();

                ByteArrayOutputStream outputStream = null;
                CipherOutputStream cipherOutputStream = null;
                try {
                  KeyStore.PrivateKeyEntry privateKeyEntry =
                      (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
                  PublicKey publicKey = privateKeyEntry.getCertificate().getPublicKey();

                  Cipher input = getCipher();
                  input.init(Cipher.ENCRYPT_MODE, publicKey);

                  outputStream = new ByteArrayOutputStream();
                  cipherOutputStream = new CipherOutputStream(outputStream, input);
                  cipherOutputStream.write(data);
                  closeQuietely(cipherOutputStream);

                  return outputStream.toByteArray();
                } catch (Exception e) {
                  throw new Exception("Failed to encrypt data with alias" + alias, e);
                } finally {
                  closeQuietely(cipherOutputStream);
                  closeQuietely(outputStream);
                }
              }
            })
        .observeOn(Schedulers.computation());
  }

  public Single<String> encryptString(String text) {
    byte[] textBytes;
    try {
      textBytes = text.getBytes("UTF-8");
    } catch (UnsupportedEncodingException e) {
      return Single.error(new Exception("Failed convert text to bytes.", e));
    }
    return encrypt(textBytes)
        .map(
            new Function<byte[], String>() {
              @Override
              public String apply(byte[] encryptedData) throws Exception {
                return Base64.encodeToString(encryptedData, Base64.DEFAULT);
              }
            })
        .observeOn(Schedulers.computation());
  }

  public Single<byte[]> decrypt(final byte[] encryptedData) {
    return Single.fromCallable(
            new Callable<byte[]>() {
              @Override
              public byte[] call() throws Exception {
                initIfNecessary();

                CipherInputStream cipherInputStream = null;
                ByteArrayOutputStream bos = null;
                try {
                  KeyStore.PrivateKeyEntry privateKeyEntry =
                      (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null);
                  PrivateKey privateKey = privateKeyEntry.getPrivateKey();

                  Cipher output = getCipher();
                  output.init(Cipher.DECRYPT_MODE, privateKey);

                  cipherInputStream =
                      new CipherInputStream(new ByteArrayInputStream(encryptedData), output);
                  bos = new ByteArrayOutputStream();

                  byte[] buffer = new byte[512];
                  int read;
                  while ((read = cipherInputStream.read(buffer)) != -1) {
                    bos.write(buffer, 0, read);
                  }

                  return bos.toByteArray();
                } catch (Exception e) {
                  throw new Exception("Failed to decrypt data with " + alias, e);
                } finally {
                  closeQuietely(cipherInputStream);
                  closeQuietely(bos);
                }
              }
            })
        .observeOn(Schedulers.computation());
  }

  public Single<String> decryptString(String encryptedText) {
    byte[] textBytes = new byte[0];
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.FROYO) {
      textBytes = Base64.decode(encryptedText, Base64.DEFAULT);
    }
    return decrypt(textBytes)
        .map(
            new Function<byte[], String>() {
              @Override
              public String apply(byte[] encryptedData) throws Exception {
                return new String(encryptedData, 0, encryptedData.length);
              }
            })
        .observeOn(Schedulers.computation());
  }

  public Observable<byte[]> getBytes(String name) {
    return sharedPreferences
        .getString(name)
        .asObservable()
        .map(
            new Function<String, byte[]>() {
              @Override
              public byte[] apply(String base64Value) throws Exception {
                byte[] encryptedValue = Base64.decode(base64Value, Base64.DEFAULT);
                return decrypt(encryptedValue).blockingGet();
              }
            });
  }

  public Single<Boolean> putBytes(final String name, @Nullable byte[] value) {
    if (value == null) {
      sharedPreferences.getString(name).delete();
      return Single.just(false);
    }
    return encrypt(value)
        .map(
            new Function<byte[], Boolean>() {
              @Override
              public Boolean apply(byte[] encryptedData) throws Exception {
                String encryptedString = Base64.encodeToString(encryptedData, Base64.DEFAULT);
                sharedPreferences.getString(name).set(encryptedString);
                return true;
              }
            });
  }

  public Observable<String> getString(String name) {
    return sharedPreferences
        .getString(name)
        .asObservable()
        .map(
            new Function<String, String>() {
              @Override
              public String apply(String encryptedValue) throws Exception {
                if (encryptedValue == null || encryptedValue.trim().isEmpty()) {
                  return null;
                }
                return decryptString(encryptedValue).blockingGet();
              }
            });
  }

  public Single<Boolean> putString(final String name, @Nullable String value) {
    if (value == null) {
      sharedPreferences.getString(name).delete();
      return Single.just(false);
    }
    return encryptString(value)
        .map(
            new Function<String, Boolean>() {
              @Override
              public Boolean apply(String encryptedValue) throws Exception {
                sharedPreferences.getString(name).set(encryptedValue);
                return true;
              }
            });
  }

  public void dispose() {
    this.context = null;
    this.alias = null;
    this.keyStore = null;
    this.sharedPreferences = null;
  }

  private static Cipher getCipher() {
    try {
      if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // below android m
        // error in android 6: InvalidKeyException: Need RSA private or public key
        return Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
      } else {
        // error in android 5: NoSuchProviderException: Provider not available: AndroidKeyStoreBCWorkaround
        return Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidKeyStoreBCWorkaround");
      }
    } catch (Exception exception) {
      throw new RuntimeException("getCipher: Failed to get an instance of Cipher", exception);
    }
  }

  private static void closeQuietely(Closeable c) {
    try {
      c.close();
    } catch (Throwable ignored) {
    }
  }
}

使用
1,初始化

RxSecureStorage secureStorage =
    RxSecureStorage.create(this, "alias_name")

2,加密

secureStorage
    .encryptString("string to encrypt")
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(result -> {
        // Use the resulting string
    },
    error -> {
        // Handle error
    });

3,解密

secureStorage
    .decryptString("9yIfhiwf3eDENxI1XG/XWYZOPc5RH6B9ez9y7I7BtEsig==")
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(result -> {
        // Use the resulting string
    },
    error -> {
        // Handle error
    });

4,保存一些隐私数据

secureStorage.putString("key", "hello, world!").subscribe();

获取隐私数据

secureStorage
    .getString("key")
    .subscribe(
        latest -> {
          // preference was changed, here's the latest decryped value
        });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值