存储 RSA 私钥 Android
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20102570/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Storing RSA Private Key Android
提问by user32981
During the creation of simple messaging android application that is to encrypt/decrypt messages and send them through internet, I decided to use RSA public/private key encryption. Question is how to store private key, so that even if phone is maliciously rooted, the key would stay safe? As far as I understood, KeyStore is used for certificates, and cannot be used for this? Should I encrypt private key as text file with AES? I have very little experience with security, so please feel free to correct my ideas, and give your opinion!
在创建用于加密/解密消息并通过互联网发送的简单消息传递 android 应用程序期间,我决定使用 RSA 公钥/私钥加密。问题是如何存储私钥,这样即使手机被恶意root,密钥也能保持安全?据我了解,KeyStore是用来做证书的,不能用来做这个?我应该使用 AES 将私钥加密为文本文件吗?我对安全的经验很少,所以请随时纠正我的想法,并提出您的意见!
Kind Regards.
亲切的问候。
回答by gabrielwong
I think KeyStore could be suitable for your use. It is able to store RSA keys and encrypts them using AES so even with root access, they cannot be extracted without the password or bruteforcing.
我认为 KeyStore 可能适合您使用。它能够存储 RSA 密钥并使用 AES 对其进行加密,因此即使具有 root 访问权限,也无法在没有密码或暴力破解的情况下提取它们。
There's a good post here about using KeyStore: http://nelenkov.blogspot.fr/2012/05/storing-application-secrets-in-androids.html
这里有一篇关于使用 KeyStore 的好文章:http: //nelenkov.blogspot.fr/2012/05/storing-application-secrets-in-androids.html
回答by Vikas
You can persist your RSA public/private key using SharedPreference on android. In order to keep your keys safe when the phone is maliciously rooted, you can do the following steps:
您可以在 android 上使用 SharedPreference 保留您的 RSA 公钥/私钥。为了在手机被恶意植根时确保您的密钥安全,您可以执行以下步骤:
1: When you want to ecrypt any data generate a key pair.
2: Prompt the user for a password.
3: Use that password to generate a symmetric key to encrypt your private key.
4: You can encrypt your data using the public key and decrypt using private key.
5: You can keep a session for the password prompted in step 2. During that session, you can use the symmetric key(generated from password) to encrypt/decrypt the private key.
1:当你想对任何数据进行加密时,生成一个密钥对。
2:提示用户输入密码。
3:使用该密码生成对称密钥来加密您的私钥。
4:您可以使用公钥加密您的数据并使用私钥解密。
5:您可以为步骤2中提示的密码保持会话。在该会话期间,您可以使用对称密钥(从密码生成)来加密/解密私钥。
The following code snippet shows to how to store & fetch the public key
以下代码片段显示了如何存储和获取公钥
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;
}
The following code snippet shows how to store& fetch the private key.
以下代码片段显示了如何存储和获取私钥。
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;
}
回答by Makko
None of the keystore(P12, JKS, AKS) in file system cannot be enough secure for holding RSA private keys. Only SmartCard or secure tokens can provide high level security. Read this book: "Android Security Internals". In this book you will find good description of Android Security and JCA providers.
文件系统中的密钥库(P12、JKS、AKS)都不足以安全地保存 RSA 私钥。只有智能卡或安全令牌可以提供高级别的安全性。阅读本书:“Android 安全内幕”。在本书中,您将找到对 Android 安全和 JCA 提供程序的良好描述。
回答by Adam Winter
Yes, you can use KeyStore to keep your RSA PrivateKey in Android Studio, and retrieve it for signing as needed. The basic idea is that you use "AndroidKeystore" as the provider when generating the keys. This guy: https://stackoverflow.com/questions/49410575/keystore-operation-failed-with-rsa-sign-and-verify#=had the important point of making sure you set the signature padding. That got it working for me, as follows:
是的,您可以使用 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();
}
}
Later, you can use that private key to sign a message as follows:
稍后,您可以使用该私钥对消息进行签名,如下所示:
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.";
}
}