web-dev-qa-db-fra.com

Android In App Billing: sécurisation de la clé publique de l'application

À partir de l'exemple d'application Android In App Billing version 3 (TrivialDrive) fourni avec sdk

MainActivity.Java

/* base64EncodedPublicKey should be YOUR APPLICATION'S PUBLIC KEY
 * (that you got from the Google Play developer console). This is not your
 * developer public key, it's the *app-specific* public key.
 *
 * Instead of just storing the entire literal string here embedded in the
 * program,  construct the key at runtime from pieces or
 * use bit manipulation (for example, XOR with some other string) to hide
 * the actual key.  The key itself is not secret information, but we don't
 * want to make it easy for an attacker to replace the public key with one
 * of their own and then fake messages from the server.
 */
String base64EncodedPublicKey = "CONSTRUCT_YOUR_KEY_AND_PLACE_IT_HERE";

Eh bien, je ne suis pas sûr de comprendre cette mesure de sécurité. Je sais comment obtenir la clé publique de l'application (qui est déjà codée en base 64) à partir de la console développeur de Google Play. 

Ce que je ne comprends pas, c'est cette partie

 /* Instead of just storing the entire literal string here embedded in the
 * program,  construct the key at runtime from pieces or
 * use bit manipulation (for example, XOR with some other string) to hide
 * the actual key
 */

Autant que je sache, cette clé publique est une chaîne constante, fournie par Google lors du processus de téléchargement de l'application. 

Comment pouvons-nous créer la même clé par programme en utilisant n'importe quel processus de manipulation de bits? Est-ce que quelqu'un l'a déjà fait? Y at-il un exemple de code sur la façon de procéder?

60
Krishnabhadra

Quelque chose comme ça:

String Base64EncodedPublicKey key = "Ak3jfkd" + GetMiddleBit() + "D349824";

ou 

String Base64EncodedPublicKey key = 
         DecrementEachletter("Bl4kgle") + GetMiddleBit() + ReverseString("D349824");

ou tout ce qui ne met pas la clé en texte brut en base64 dans une seule chaîne. Probablement aussi quelque chose qui ne stocke pas la clé dans base64 serait une bonne idée aussi, puisque les fragments de texte bruts en base64 sont assez faciles à repérer.

Ce n'est pas un BON moyen de protéger la clé. Mais il protège contre une attaque triviale dans laquelle une personne cherche simplement dans les chaînes littérales de votre APK recherchant quelque chose qui ressemble à une clé publique encodée en base64. Au moins, vous faites en sorte que les # $ # $ ers fonctionnent un peu.

On peut supposer que des personnes perverses peuvent faire de mauvaises choses si elles identifient votre clé publique. Google semble le penser, apparemment. Je peux deviner ce que cette étape fait, mais je ne suis pas sûr de vouloir vraiment spéculer à ce sujet dans un forum ouvert et donner à quiconque des idées. Vous voulez le faire cependant.

Le résumé de base de l'intrigue serait qu'il est plus difficile pour quelqu'un d'écrire une application qui dé-LVL programme par programme une application.

On suppose que toute personne qui le fait gagne sa vie en exploitant 20 ou 30 000 applications Android et en les republiant. Les chances sont, je suppose, qu'ils ne vont pas prendre les dix minutes supplémentaires pour ajouter votre application à la liste des 20 000 applications Android qui ont déjà été cassées par un programme, s'ils doivent réellement faire un peu de travail manuel. Sauf si vous avez une application de premier niveau. Et puis la bataille est potentiellement sans fin, et probablement finalement futile.

Fractionner la clé en morceaux consécutifs (comme proposé dans une autre réponse) n'est probablement pas suffisant. Parce que la clé finira par former des chaînes consécutives dans les tables de constantes de chaînes de l'APK. Trop facile de trouver cela avec un programme.

43
Robin Davies

Une alternative consiste à effectuer des transformations de base sur la clé.

// Replace this with your encoded key.
String base64EncodedPublicKey = "";

// Get byte sequence to play with.
byte[] bytes = base64EncodedPublicKey.getBytes();

// Swap upper and lower case letters.
for (int i = 0; i < bytes.length; i++) {
    if(bytes[i] >= 'A' && bytes[i] <= 'Z')
        bytes[i] = (byte)( 'a' + (bytes[i] - 'A'));
    else if(bytes[i] >= 'a' && bytes[i] <= 'z')
        bytes[i] = (byte)( 'A' + (bytes[i] - 'a'));
}

// Assign back to string.
base64EncodedPublicKey = new String( bytes );

L'idée serait donc de mettre votre clé d'origine en tant que base64EncodedPublicKey et d'exécuter le code ci-dessus, elle permuterait les lettres majuscules et minuscules et remettrait le résultat en base64EncodedPublicKey. Vous pouvez ensuite copier le résultat du débogueur et le coller dans le code en tant que valeur base64EncodedPublicKey d'origine. À ce stade, votre clé sera transformée (majuscules et minuscules commutées) et, au moment de l'exécution, elle sera corrigée dans le bon boîtier et continuera à fonctionner.

Ce qui précède est évidemment un transcodage de base, mais vous pouvez être plus créatif, inverser l’ordre de A à Z, échanger des nombres pairs et impairs, échanger des voyelles pour des nombres pairs. Le problème ici est que si je mets du code dans l’extrait ci-dessus qui fait un tas de transcodes plus intéressants, puis que tout le monde copie et colle cela dans leurs projets, un pirate pourra facilement voir et utiliser le transcodage ce post)! Il ne vous reste donc plus qu'à vous proposer quelques transformations.

J'ai fait exprès de faire en sorte que ce qui précède fonctionne dans les deux sens (donc si vous l'exécutez deux fois, vous retrouverez votre valeur d'origine) car cela facilite l'exécution de l'algorithme sur votre clé d'origine. Je pense que c'est assez chouette, on dirait que la vraie clé est en texte clair, un cracker occasionnel peut essayer de changer cela, puis être confus quand cela ne fonctionne pas.

13
Steven Craft

Vous pouvez le diviser en morceaux comme celui-ci

String piece1 = "SDFGJKGB4UIH234WE/FRT23RSDF/3DFUISDFVWE";
String piece2 = "SDFGJKGB4UIHUISDFVWE";
String piece3 = "BDYASGBDNAWGRET24IYE23das4saGBENWKD";
String piece4 = "432423SDF23R/+SDDS";

mHelper = new IabHelper(this, piece1 + piece2 + piece3 + piece4);

Tout type de manipulations fera l'affaire.

Vous ne pouvez pas cacher parfaitement la clé publique à l'attaquant, vous devez simplement manipuler la chaîne pour dérouter un peu

Vous pouvez ajouter des chaînes et les supprimer si nécessaire ou les scinder en morceaux.

5
NaviRamyle

Ce que j'ai fait était de transformer la clé en tableau de caractères, de la diviser en deux, puis de la reconstruire au besoin, comme suit:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_shop);

    char[] base64KeyByteArray = ArrayUtils.addAll(getPublicKeyChunk1(), getPublicKeyChunk2());

    Log.d(TAG, String.valueOf(base64KeyByteArray));
}

private char[] getPublicKeyChunk1() {
    return new char[]{82, 73, 67, 66, 73, 106, 65, 78, 66, 103, 107, 113, 104, 107,
            105, 71, 57, 119, 79, 66, 65, 81, 69, 70, 65, 65, 79, 67, 65, 81, 56, 65, 77, 73,
            73, 66, 67, 103, 75, 67, 65, 81, 69, 65, 121, 55, 81, 76, 122, 67, 105, 80, 65,
            110, 105, 101, 72, 66, 53, 57};
}

private char[] getPublicKeyChunk2() {
    return new char[]{82, 43, 68, 47, 79, 121, 122, 110, 85, 67, 118, 89, 108, 120, 43, 49,
            80, 100, 67, 108, 55, 90, 57, 103, 119, 57, 87, 78, 79, 111, 53, 101, 80, 71,
            117, 74, 104, 82, 87, 97, 100};
}
2
Adrian

Suivez 3 étapes simples pour sécuriser la clé API/clé secrète

Nous pouvons utiliser Gradle pour sécuriser la clé API ou la clé secrète. Vérifiez ma réponse .

0
SANAT

S'appuyant sur La réponse de Steven Craft avec l'aide que j'ai eu de gidds

Voici un code plus propre. De plus, il remplace les chiffres (0-9) par des caractères ascii (37-46) et inversement. Écrit en Kotlin.

 val string = "Hello World 012345679 %&()"
 fun String.swapCase() = map {
        when {
            it.isUpperCase() -> it.toLowerCase()
            it.isLowerCase() -> it.toUpperCase()
            it.isDigit() -> (37 + (it.toInt() - 48)).toChar()
            it.isDefined() -> if (it.toInt() >= 37 && it.toInt() <= 46) (48 + (it.toInt() - 37)).toChar() else it
            else -> it
        }
    }.joinToString("")
    println(string.swapCase()) // hELLO wORLD %&'()*+,. 0134

Utilisez ceci -> https://Edge-developer.github.io/BillingGenerator/ pour générer tous ceux qui se trouvent dans une mouche

0
Micklo_Nerd