Lors de la création d'une messagerie simple Android qui consiste à crypter/décrypter des messages et à les envoyer via Internet, j'ai décidé d'utiliser le cryptage par clé publique/privée RSA. La question est de savoir comment stocker la clé privée, donc que même si le téléphone est enraciné de manière malveillante, la clé resterait en sécurité? Pour autant que je sache, KeyStore est utilisé pour les certificats et ne peut pas être utilisé pour cela. Dois-je chiffrer la clé privée sous forme de fichier texte avec AES? J'ai très peu d'expérience avec sécurité, alors n'hésitez pas à corriger mes idées, et donnez votre avis!
Sincères amitiés.
Je pense que KeyStore pourrait convenir à votre utilisation. Il est capable de stocker des clés RSA et de les crypter en utilisant AES donc même avec un accès root, elles ne peuvent pas être extraites sans le mot de passe ou le bruteforcing.
Il y a un bon article ici sur l'utilisation de KeyStore: http://nelenkov.blogspot.fr/2012/05/storing-application-secrets-in-androids.html
Vous pouvez conserver votre clé publique/privée RSA en utilisant SharedPreference sur Android. Afin de protéger vos clés lorsque le téléphone est enraciné de manière malveillante, vous pouvez effectuer les étapes suivantes:
1: Lorsque vous souhaitez chiffrer des données, générez une paire de clés.
2: Demander à l'utilisateur un mot de passe.
3: Utilisez ce mot de passe pour générer une clé symétrique pour crypter votre clé privée.
4: Vous pouvez crypter vos données à l'aide de la clé publique et décrypter à l'aide de la clé privée.
5: Vous pouvez conserver une session pour le mot de passe demandé à l'étape 2. Au cours de cette session, vous pouvez utiliser la clé symétrique (générée à partir du mot de passe) pour crypter/décrypter la clé privée.
L'extrait de code suivant montre comment stocker et récupérer la clé publique
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;
}
L'extrait de code suivant montre comment stocker et récupérer la clé privée.
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;
}
Aucun des fichiers de clés (P12, JKS, AKS) du système de fichiers ne peut être suffisamment sécurisé pour conserver les clés privées RSA. Seules les SmartCard ou les jetons sécurisés peuvent fournir une sécurité de haut niveau. Lisez ce livre: "Android Security Internals". Dans ce livre, vous trouverez une bonne description de Android Sécurité et fournisseurs JCA.
Oui, vous pouvez utiliser KeyStore pour conserver votre clé privée RSA dans Android Studio, et la récupérer pour signature si nécessaire. L'idée de base est que vous utilisez "AndroidKeystore" comme fournisseur lors de la génération des clés. Ce gars: https://stackoverflow.com/questions/49410575/keystore-operation-failed-with-rsa-sign-and-verify# = avait le point important de vous assurer que vous définissez le remplissage de la signature. Cela m'a fait fonctionner comme suit:
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();
}
}
Plus tard, vous pouvez utiliser cette clé privée pour signer un message comme suit:
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.";
}
}