* The fixes need to be applied via {@link #apply()} before any use of Java
* Cryptography Architecture primitives. A good place to invoke them is in the
* application's {@code onCreate}.
*/
-final class PRNGFixes {
- private static final int VERSION_CODE_JELLY_BEAN = 16;
- private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
+public final class PRNGFixes {
+ private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x000110, "PRNGFixes");
private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL = getBuildFingerprintAndDeviceSerial();
/**
* Hidden constructor to prevent instantiation.
*/
- private PRNGFixes() { }
+ private PRNGFixes() {
+ }
/**
* Applies all fixes.
@@ -63,27 +64,22 @@ final class PRNGFixes {
* @throws SecurityException if the fix is needed but could not be applied.
*/
private static void applyOpenSSLFix() throws SecurityException {
- if (Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN
- || Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
- // No need to apply the fix
- return;
- }
try {
// Mix in the device- and invocation-specific seed.
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
- .getMethod("RAND_seed", byte[].class)
- .invoke(null, generateSeed());
+ .getMethod("RAND_seed", byte[].class)
+ .invoke(null, generateSeed());
// Mix output of Linux PRNG into OpenSSL's PRNG
int bytesRead = (Integer) Class.forName(
- "org.apache.harmony.xnet.provider.jsse.NativeCrypto")
- .getMethod("RAND_load_file", String.class, long.class)
- .invoke(null, "/dev/urandom", 1024);
+ "org.apache.harmony.xnet.provider.jsse.NativeCrypto")
+ .getMethod("RAND_load_file", String.class, long.class)
+ .invoke(null, "/dev/urandom", 1024);
if (bytesRead != 1024) {
throw new IOException(
- "Unexpected number of bytes read from Linux PRNG: "
- + bytesRead);
+ "Unexpected number of bytes read from Linux PRNG: "
+ + bytesRead);
}
} catch (Exception e) {
throw new SecurityException("Failed to seed OpenSSL PRNG", e);
@@ -98,20 +94,16 @@ final class PRNGFixes {
* @throws SecurityException if the fix is needed but could not be applied.
*/
private static void installLinuxPRNGSecureRandom()
- throws SecurityException {
- if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
- // No need to apply the fix
- return;
- }
+ throws SecurityException {
// Install a Linux PRNG-based SecureRandom implementation as the
// default, if not yet installed.
Provider[] secureRandomProviders =
- Security.getProviders("SecureRandom.SHA1PRNG");
+ Security.getProviders("SecureRandom.SHA1PRNG");
if (secureRandomProviders == null
- || secureRandomProviders.length < 1
- || !LinuxPRNGSecureRandomProvider.class.equals(
- secureRandomProviders[0].getClass())) {
+ || secureRandomProviders.length < 1
+ || !LinuxPRNGSecureRandomProvider.class.equals(
+ secureRandomProviders[0].getClass())) {
Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
}
@@ -120,10 +112,10 @@ final class PRNGFixes {
// by the Linux PRNG-based SecureRandom implementation.
SecureRandom rng1 = new SecureRandom();
if (!LinuxPRNGSecureRandomProvider.class.equals(
- rng1.getProvider().getClass())) {
+ rng1.getProvider().getClass())) {
throw new SecurityException(
- "new SecureRandom() backed by wrong Provider: "
- + rng1.getProvider().getClass());
+ "new SecureRandom() backed by wrong Provider: "
+ + rng1.getProvider().getClass());
}
SecureRandom rng2;
@@ -133,26 +125,29 @@ final class PRNGFixes {
throw new SecurityException("SHA1PRNG not available", e);
}
if (!LinuxPRNGSecureRandomProvider.class.equals(
- rng2.getProvider().getClass())) {
+ rng2.getProvider().getClass())) {
throw new SecurityException(
- "SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
- + " Provider: " + rng2.getProvider().getClass());
+ "SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
+ + " Provider: " + rng2.getProvider().getClass());
}
}
/**
* Generates a device- and invocation-specific seed to be mixed into the
* Linux PRNG.
+ *
+ * @return byte[]
*/
private static byte[] generateSeed() {
try {
ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
DataOutputStream seedBufferOut =
- new DataOutputStream(seedBuffer);
+ new DataOutputStream(seedBuffer);
seedBufferOut.writeLong(System.currentTimeMillis());
seedBufferOut.writeLong(System.nanoTime());
- seedBufferOut.writeInt(Process.myPid());
- seedBufferOut.writeInt(Process.myUid());
+
+ seedBufferOut.writeInt(ProcessManager.getPid());
+ seedBufferOut.writeInt(ProcessManager.getUid());
seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
seedBufferOut.close();
return seedBuffer.toByteArray();
@@ -170,7 +165,7 @@ final class PRNGFixes {
// We're using the Reflection API because Build.SERIAL is only available
// since API Level 9 (Gingerbread, Android 2.3).
try {
- return (String) Build.class.getField("SERIAL").get(null);
+ return (String) System.class.getField("SERIAL").get(null);
} catch (Exception ignored) {
return null;
}
@@ -178,10 +173,6 @@ final class PRNGFixes {
private static byte[] getBuildFingerprintAndDeviceSerial() {
StringBuilder result = new StringBuilder();
- String fingerprint = Build.FINGERPRINT;
- if (fingerprint != null) {
- result.append(fingerprint);
- }
String serial = getDeviceSerialNumber();
if (serial != null) {
result.append(serial);
@@ -210,7 +201,6 @@ final class PRNGFixes {
* serialized (on LOCK) to ensure that multiple threads do not get
* duplicated PRNG output.
*/
-
private static final File URANDOM_FILE = new File("/dev/urandom");
private static final Object LOCK = new Object();
@@ -250,8 +240,8 @@ final class PRNGFixes {
} catch (IOException e) {
// On a small fraction of devices /dev/urandom is not writable.
// Log and ignore.
- Log.w(PRNGFixes.class.getSimpleName(),
- "Failed to mix seed into " + URANDOM_FILE);
+ HiLog.error(LABEL, PRNGFixes.class.getSimpleName() +
+ "Failed to mix seed into " + URANDOM_FILE);
} finally {
seeded = true;
}
@@ -274,7 +264,7 @@ final class PRNGFixes {
}
} catch (IOException e) {
throw new SecurityException(
- "Failed to read from " + URANDOM_FILE, e);
+ "Failed to read from " + URANDOM_FILE, e);
}
}
@@ -294,10 +284,10 @@ final class PRNGFixes {
// output being pulled into this process prematurely.
try {
urandomIn = new DataInputStream(
- new FileInputStream(URANDOM_FILE));
+ new FileInputStream(URANDOM_FILE));
} catch (IOException e) {
throw new SecurityException("Failed to open "
- + URANDOM_FILE + " for reading", e);
+ + URANDOM_FILE + " for reading", e);
}
}
return urandomIn;
@@ -322,9 +312,9 @@ final class PRNGFixes {
LinuxPRNGSecureRandomProvider() {
super("LinuxPRNG",
- 1.0,
- "A Linux-specific random number provider that uses"
- + " /dev/urandom");
+ 1.0,
+ "A Linux-specific random number provider that uses"
+ + " /dev/urandom");
// Although /dev/urandom is not a SHA-1 PRNG, some apps
// explicitly request a SHA1PRNG SecureRandom and we thus need to
// prevent them from getting the default implementation whose output
diff --git a/securestoragelibrary/src/main/java/de/adorsys/ohos/securestoragelibrary/PreferenceUtils.java b/securestoragelibrary/src/main/java/de/adorsys/ohos/securestoragelibrary/PreferenceUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..36613d7c7f1311a433d4814170de47b080b4e524
--- /dev/null
+++ b/securestoragelibrary/src/main/java/de/adorsys/ohos/securestoragelibrary/PreferenceUtils.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain an copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package de.adorsys.ohos.securestoragelibrary;
+
+import ohos.app.Context;
+import ohos.data.DatabaseHelper;
+import ohos.data.preferences.Preferences;
+
+/**
+ * Preference程序退出后就无法读取存储的值,原因是context不一致
+ *
+ * @author wp
+ * @since 2021-05-19
+ */
+public class PreferenceUtils {
+ private static final String PREFERENCE_FILE_NAME = "fileName";
+
+ private static PreferenceUtils instance = null;
+
+ private PreferenceUtils() {
+ }
+
+ /**
+ * 静态锁对象
+ *
+ * @return Utils
+ */
+ public static synchronized PreferenceUtils getInstance() {
+ if (instance == null) {
+ instance = new PreferenceUtils();
+ }
+ return instance;
+ }
+
+ /**
+ * 存放key和value
+ *
+ * @param context
+ * @param key
+ * @param value
+ */
+ public static void putString(Context context, String key, String value) {
+ DatabaseHelper databaseHelper = new DatabaseHelper(context);
+ Preferences preferences = databaseHelper.getPreferences(PREFERENCE_FILE_NAME);
+ preferences.putString(key, value);
+ preferences.flushSync();
+ }
+
+ /**
+ * 根据key获取value
+ *
+ * @param context
+ * @param key
+ * @return String获取的String默认值为:null
+ */
+ public static String getString(Context context, String key) {
+ DatabaseHelper databaseHelper = new DatabaseHelper(context);
+ Preferences preferences = databaseHelper.getPreferences(PREFERENCE_FILE_NAME);
+ String value = preferences.getString(key, null);
+ return value;
+ }
+
+ /**
+ * 根据key获取value
+ *
+ * @param context
+ * @param key
+ */
+ public static void deleteString(Context context, String key) {
+ DatabaseHelper databaseHelper = new DatabaseHelper(context);
+ Preferences preferences = databaseHelper.getPreferences(PREFERENCE_FILE_NAME);
+ preferences.delete(key);
+ }
+}
diff --git a/securestoragelibrary/src/main/java/de/adorsys/ohos/securestoragelibrary/RsaUtils.java b/securestoragelibrary/src/main/java/de/adorsys/ohos/securestoragelibrary/RsaUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..8ad4e89e9d1e49bc234d5a67172cf58bf6caa1a0
--- /dev/null
+++ b/securestoragelibrary/src/main/java/de/adorsys/ohos/securestoragelibrary/RsaUtils.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain an copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package de.adorsys.ohos.securestoragelibrary;
+
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+
+import javax.crypto.Cipher;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+
+/**
+ * RSAUtils RSA非对称加密密钥对生成工具类,
+ *
+ * @author wp
+ * @since 2021-05-13
+ */
+public class RsaUtils {
+ private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x000110, "RSAUtils");
+ /** 算法名称 */
+ private static final String ALGORITHM = "RSA";
+
+ /** 密钥长度 */
+ private static final int KEY_SIZE = 2048;
+
+ private static RsaUtils instance = null;
+
+ private RsaUtils() {
+ }
+
+ /**
+ * 静态锁对象
+ *
+ * @return Utils
+ */
+ public static synchronized RsaUtils getInstance() {
+ if (instance == null) {
+ instance = new RsaUtils();
+ }
+ return instance;
+ }
+
+ /**
+ * 随机生成密钥对(包含公钥和私钥)
+ *
+ * @return KeyPair
+ * @throws Exception
+ */
+ public static KeyPair generateKeyPair() throws Exception {
+ // 获取指定算法的密钥对生成器
+ KeyPairGenerator gen = KeyPairGenerator.getInstance(ALGORITHM);
+
+ // 初始化密钥对生成器(指定密钥长度, 使用默认的安全随机数源)
+ gen.initialize(KEY_SIZE);
+
+ // 随机生成一对密钥(包含公钥和私钥)
+ return gen.generateKeyPair();
+ }
+
+ /**
+ * 将 公钥/私钥 编码后以 Base64 的格式保存到指定文件
+ *
+ * @param key
+ * @param keyFile
+ * @throws IOException
+ */
+ public static void saveKeyForEncodedBase64(Key key, File keyFile) {
+ try {
+ // 获取密钥编码后的格式
+ byte[] encBytes = key.getEncoded();
+
+ // 转换为 Base64 文本
+ String encBase64 = Base64.getEncoder().encodeToString(encBytes);
+ IoUtils.writeFile(encBase64, keyFile);
+ } catch (FileNotFoundException e) {
+ HiLog.error(LABEL, e.getMessage());
+ } catch (IOException e) {
+ HiLog.error(LABEL, e.getMessage());
+ }
+ }
+
+ /**
+ * 根据公钥的 Base64 文本创建公钥对象
+ *
+ * @param pubKeyBase64
+ * @return PublicKey
+ * @throws Exception
+ */
+ public static PublicKey getPublicKey(String pubKeyBase64) throws Exception {
+ // 把 公钥的Base64文本 转换为已编码的 公钥bytes
+ byte[] encPubKey = Base64.getDecoder().decode(pubKeyBase64);
+
+ // 创建 已编码的公钥规格
+ X509EncodedKeySpec encPubKeySpec = new X509EncodedKeySpec(encPubKey);
+
+ // 获取指定算法的密钥工厂, 根据 已编码的公钥规格, 生成公钥对象
+ return KeyFactory.getInstance(ALGORITHM).generatePublic(encPubKeySpec);
+ }
+
+ /**
+ * 根据私钥的 Base64 文本创建私钥对象
+ *
+ * @param priKeyBase64
+ * @return PrivateKey
+ * @throws Exception
+ */
+ public static PrivateKey getPrivateKey(String priKeyBase64) throws Exception {
+ // 把 私钥的Base64文本 转换为已编码的 私钥bytes
+ byte[] encPriKey = Base64.getDecoder().decode(priKeyBase64);
+
+ // 创建 已编码的私钥规格
+ PKCS8EncodedKeySpec encPriKeySpec = new PKCS8EncodedKeySpec(encPriKey);
+
+ // 获取指定算法的密钥工厂, 根据 已编码的私钥规格, 生成私钥对象
+ return KeyFactory.getInstance(ALGORITHM).generatePrivate(encPriKeySpec);
+ }
+
+ /**
+ * 公钥加密数据
+ *
+ * @param plainData
+ * @param pubKey
+ * @return byte[]
+ * @throws Exception
+ */
+ public static byte[] encrypt(byte[] plainData, PublicKey pubKey) throws Exception {
+ // 获取指定算法的密码器
+ Cipher cipher = Cipher.getInstance(ALGORITHM);
+
+ // 初始化密码器(公钥加密模型)
+ cipher.init(Cipher.ENCRYPT_MODE, pubKey);
+
+ // 加密数据, 返回加密后的密文
+ return cipher.doFinal(plainData);
+ }
+
+ /**
+ * 私钥解密数据
+ *
+ * @param cipherData
+ * @param priKey
+ * @return byte[]
+ * @throws Exception
+ */
+ public static byte[] decrypt(byte[] cipherData, PrivateKey priKey) throws Exception {
+ // 获取指定算法的密码器
+ Cipher cipher = Cipher.getInstance(ALGORITHM);
+
+ // 初始化密码器(私钥解密模型)
+ cipher.init(Cipher.DECRYPT_MODE, priKey);
+
+ // 解密数据, 返回解密后的明文
+ return cipher.doFinal(cipherData);
+ }
+}
diff --git a/securestoragelibrary/src/main/java/de/adorsys/android/securestoragelibrary/SecurePreferences.java b/securestoragelibrary/src/main/java/de/adorsys/ohos/securestoragelibrary/SecurePreferences.java
similarity index 61%
rename from securestoragelibrary/src/main/java/de/adorsys/android/securestoragelibrary/SecurePreferences.java
rename to securestoragelibrary/src/main/java/de/adorsys/ohos/securestoragelibrary/SecurePreferences.java
index 58b355023ed546a565b8632c819baa5760989f39..f28037647f918dade67196231ec1e4cc5eb7472f 100644
--- a/securestoragelibrary/src/main/java/de/adorsys/android/securestoragelibrary/SecurePreferences.java
+++ b/securestoragelibrary/src/main/java/de/adorsys/ohos/securestoragelibrary/SecurePreferences.java
@@ -14,30 +14,29 @@
* limitations under the License.
*/
-package de.adorsys.android.securestoragelibrary;
+package de.adorsys.ohos.securestoragelibrary;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.text.TextUtils;
+import ohos.app.Context;
+import ohos.data.DatabaseHelper;
+import ohos.data.preferences.Preferences;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
import java.util.HashSet;
import java.util.Set;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
+import static de.adorsys.ohos.securestoragelibrary.SecureStorageException.ExceptionType.CRYPTO_EXCEPTION;
-import static android.content.Context.MODE_PRIVATE;
-import static de.adorsys.android.securestoragelibrary.SecureStorageException.ExceptionType.CRYPTO_EXCEPTION;
/**
* Handles every use case for the developer using Secure Storage.
* Encryption, Decryption, Storage, Removal etc.
*/
public final class SecurePreferences {
- private static final String KEY_SHARED_PREFERENCES_NAME = "SecurePreferences";
+ private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x000110, "SecurePreferences");
private static final String KEY_SET_COUNT_POSTFIX = "_count";
+ private static final String PREFERENCES_NAME = "my_sharePreferences";
- // hidden constructor to disable initialization
private SecurePreferences() {
}
@@ -47,20 +46,21 @@ public final class SecurePreferences {
* @param context Context is used internally
* @param key Key used to identify the stored value in SecureStorage
* @param value Plain String value that will be encrypted and stored in the SecureStorage
+ * @throws SecureStorageException
*/
- public static void setValue(@NonNull Context context,
- @NonNull String key,
- @NonNull String value) throws SecureStorageException {
+ public static void setValue(Context context, String key, String value) throws SecureStorageException {
Context applicationContext = context.getApplicationContext();
- if (!KeystoreTool.keyPairExists()) {
+
+ if (!KeystoreTool.keyPairExists(context)) {
KeystoreTool.generateKeyPair(applicationContext);
}
String transformedValue = KeystoreTool.encryptMessage(applicationContext, value);
- if (TextUtils.isEmpty(transformedValue)) {
- throw new SecureStorageException(context.getString(R.string.message_problem_encryption), null, CRYPTO_EXCEPTION);
+ if (transformedValue.isEmpty()) {
+ HiLog.error(LABEL, "KeystoreTool.keyPairExists : " + KeystoreTool.keyPairExists(context));
+ throw new SecureStorageException("Problem during Encryption", null, CRYPTO_EXCEPTION);
} else {
- setSecureValue(applicationContext, key, transformedValue);
+ setSecureValue(context, key, transformedValue);
}
}
@@ -70,10 +70,9 @@ public final class SecurePreferences {
* @param context Context is used internally
* @param key Key used to identify the stored value in SecureStorage
* @param value Plain boolean value that will be encrypted and stored in the SecureStorage
+ * @throws SecureStorageException
*/
- public static void setValue(@NonNull Context context,
- @NonNull String key,
- boolean value) throws SecureStorageException {
+ public static void setValue(Context context, String key, boolean value) throws SecureStorageException {
setValue(context, key, String.valueOf(value));
}
@@ -83,10 +82,9 @@ public final class SecurePreferences {
* @param context Context is used internally
* @param key Key used to identify the stored value in SecureStorage
* @param value Plain float value that will be encrypted and stored in the SecureStorage
+ * @throws SecureStorageException
*/
- public static void setValue(@NonNull Context context,
- @NonNull String key,
- float value) throws SecureStorageException {
+ public static void setValue(Context context, String key, float value) throws SecureStorageException {
setValue(context, key, String.valueOf(value));
}
@@ -96,10 +94,9 @@ public final class SecurePreferences {
* @param context Context is used internally
* @param key Key used to identify the stored value in SecureStorage
* @param value Plain long value that will be encrypted and stored in the SecureStorage
+ * @throws SecureStorageException
*/
- public static void setValue(@NonNull Context context,
- @NonNull String key,
- long value) throws SecureStorageException {
+ public static void setValue(Context context, String key, long value) throws SecureStorageException {
setValue(context, key, String.valueOf(value));
}
@@ -109,10 +106,9 @@ public final class SecurePreferences {
* @param context Context is used internally
* @param key Key used to identify the stored value in SecureStorage
* @param value Plain int value that will be encrypted and stored in the SecureStorage
+ * @throws SecureStorageException
*/
- public static void setValue(@NonNull Context context,
- @NonNull String key,
- int value) throws SecureStorageException {
+ public static void setValue(Context context, String key, int value) throws SecureStorageException {
setValue(context, key, String.valueOf(value));
}
@@ -122,12 +118,10 @@ public final class SecurePreferences {
* @param context Context is used internally
* @param key Key used to identify the stored value in SecureStorage
* @param value Plain Set(type: String) value that will be encrypted and stored in the SecureStorage
+ * @throws SecureStorageException
*/
- public static void setValue(@NonNull Context context,
- @NonNull String key,
- @NonNull Set