シンプルなメッセージングの作成中にAndroidメッセージを暗号化/復号化してインターネット経由で送信するアプリケーションですが、RSA公開鍵/秘密鍵暗号化を使用することにしました。秘密鍵の保存方法は電話が悪意を持ってルート化された場合でも、キーは安全に保たれますか?私が理解している限り、KeyStoreは証明書に使用され、これには使用できません?秘密キーをAESでテキストファイルとして暗号化する必要がありますか?セキュリティですので、お気軽に私の考えを修正して、ご意見をお寄せください!
敬具。
KeyStoreはあなたの使用に適していると思います。 RSAキーを格納し、AESを使用して暗号化することができるため、ルートアクセスがあっても、パスワードやブルートフォーシングがなければ抽出できません。
KeyStoreの使用に関する良い投稿があります: http://nelenkov.blogspot.fr/2012/05/storing-application-secrets-in-androids.html
AndroidのSharedPreferenceを使用して、RSA公開/秘密鍵を永続化できます。電話が悪意を持ってルート化されたときにキーを安全に保つために、次の手順を実行できます。
1:データを暗号化する場合は、鍵ペアを生成します。
2:ユーザーにパスワードを要求します。
3:そのパスワードを使用して対称鍵を生成し、秘密鍵を暗号化します。
4:公開鍵を使用してデータを暗号化し、秘密鍵を使用して復号化できます。
5:ステップ2でプロンプトされたパスワードのセッションを維持できます。そのセッション中に、対称キー(パスワードから生成)を使用して秘密キーを暗号化/復号化できます。
次のコードスニペットは、公開鍵を保存およびフェッチする方法を示しています
public void setPublicKey(PublicKey publicKey, String key, Context context) {
byte[] pubKey = publicKey.getEncoded();
String pubKeyString = Base64.encodeBytes(pubKey);
this.setString(key, pubKeyString, context);
}
public PublicKey getPublicKey(String key,Context context) {
PublicKey pKey = null;
try {
String pubString = this.getString(key, context);
if(pubString!=null) {
byte[] binCpk = Base64.decode(pubString);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(binCpk);
pKey = keyFactory.generatePublic(publicKeySpec);
}
}catch(Exception e){
}
return pKey;
}
次のコードスニペットは、秘密鍵を保存およびフェッチする方法を示しています。
public void setPrivateKey(PrivateKey privateKey, String key, Context context) {
byte[] priKey = privateKey.getEncoded();
String priKeyString = Base64.encodeBytes(priKey);
this.setString(key, priKeyString, context);
}
public PrivateKey getPrivateKey(String key, Context context) {
PrivateKey privateKey = null;
try {
String privateString = this.getString(key, context);
if(privateString!=null){
byte[] binCpk = Base64.decode(privateString);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(binCpk);
privateKey = keyFactory.generatePrivate(privateKeySpec);
}
}
catch(Exception e){
}
return privateKey;
}
ファイルシステムのキーストア(P12、JKS、AKS)のいずれも、RSA秘密鍵を保持するのに十分な安全性を確保できません。高レベルのセキュリティを提供できるのは、スマートカードまたは安全なトークンのみです。この本「Android Security Internals」を読んでください。この本では、AndroidセキュリティおよびJCAプロバイダーの説明がわかります。
はい。KeyStoreを使用してRSA PrivateKeyをAndroid Studioに保持し、必要に応じて署名用に取得できます。基本的な考え方は、キーを生成するときにプロバイダーとして「AndroidKeystore」を使用することです。この男: https://stackoverflow.com/questions/49410575/keystore-operation-failed-with-rsa-sign-and-verify#= 署名のパディングを確実に設定するという重要なポイントがありました。
public void storeKeyAsymmetric(){ //Generate the keys (public and private together) using KeyStore
KeyPairGenerator kpGenerator = null;
try {
kpGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (NoSuchProviderException e) {
e.printStackTrace();
}
try {
kpGenerator.initialize(new KeyGenParameterSpec.Builder("aliasOfYourChoice", KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA512, KeyProperties.DIGEST_SHA256)
.setKeySize(2048)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1, KeyProperties.ENCRYPTION_PADDING_RSA_OAEP, KeyProperties.ENCRYPTION_PADDING_NONE)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1, KeyProperties.SIGNATURE_PADDING_RSA_PSS)
.build());
keyPairAsymmetric = kpGenerator.generateKeyPair();
devicePublic = keyPairAsymmetric.getPublic();
byte[] encoding = devicePublic.getEncoded();
strDevicePublicPEM = Crypto.writePEM(encoding);
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
後で、その秘密鍵を使用して次のようにメッセージに署名できます。
public static String verifiedDeviceSignature(String dataToSign){
boolean verified = false;
String signature = null;
MessageDigest digest = null;
try {
digest = MessageDigest.getInstance("SHA-512");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
digest.update(dataToSign.getBytes(StandardCharsets.UTF_8));
byte[] hash = digest.digest();
try {
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
//******This is a PrivateKeyEntry
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry("aliasOfYourChoice", null); //null if you don't have key locked up with password
PrivateKey privateKey = privateKeyEntry.getPrivateKey();
Signature s = Signature.getInstance("SHA512withRSA");
s.initSign(privateKey);
s.update(dataToSign.getBytes(StandardCharsets.UTF_8)); //TODO: Change this to hash
byte[] sig = s.sign();
PublicKey publicKey = ks.getCertificate("aliasOfYourChoice").getPublicKey();
Signature v = Signature.getInstance("SHA512withRSA");
v.initVerify(publicKey);
v.update(dataToSign.getBytes(StandardCharsets.UTF_8)); //TODO: Change this to hash
verified = v.verify(sig);
String strSig = new String(Base64.encode(sig, 2));
signature = strSig;
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
}
if(verified){
Log.d("***verifiedDeviceSignature*: ", "Signature Verified");
//TODO: URL encode
return signature;
}else {
return "Not verified.";
}
}