Bonjour,
Est-ce que quelqu'un sait comment faire pour que mon application java ne puisse être exécutée que sur une seule machine ? J'aimerais créer des licenses avec date d'expiration pour les utilisateurs de mon logiciel.
Merci d'avance.
Version imprimable
Bonjour,
Est-ce que quelqu'un sait comment faire pour que mon application java ne puisse être exécutée que sur une seule machine ? J'aimerais créer des licenses avec date d'expiration pour les utilisateurs de mon logiciel.
Merci d'avance.
Une méthode simple, c'est de stocker toutes ces informations dans une chaine de caractère en clair (date d'expiration, utilisateur, nombre de licences, etc). Ton application vérifie ces données un peu partout dans ton code. Ensuite, il te faut une paire de clé publique/privées (cryptographie). Java supporte très bien la cryptographie, autant t'en servir. Tu utilise ta clé privée que tu ne distribue jamais pour encoder cette chaine de caractères (une chaine différente pas client donc). Tu obtiens ainsi un code binaire crypté, que tu transfère à tes clients. Ton application, elle, contient la clé publique, elle l'utilise pour décoder le fichier et obtenir ca chaine de données, qu'elle utilise partout dans son code.
L'utilisation de clés publiques/privée garanti qu'un utilisateur ne pourra pas générer de fausse licence, elle n'empechera jamais de tripatouiller ton code pour faire sauter la protection.
Edit: regarde peut etre aussi du coté de truelicence:
https://truelicense.dev.java.net/
Merci ça a l'air intéressant, je vais y jeter un coup d'oeil.
Bonjour tchize, j'ai lu un peu partout sur le net divers articles sur la cryptographie, mais étant assez novice en la matière, pourriez-vous m'expliquer par où je dois commencer ? Avez-vous un bon tuto en ce qui concerne la génération de licenses pour mon application ?
Merci d'avance.
intro à la cryptographie:
http://ram-0000.developpez.com/tutoriels/cryptographie/
intro à la crypto sous java:
http://nyal.developpez.com/tutoriel/java/bouncycastle/
http://jmdoudoux.developpez.com/cour...p-securite.php
Bonsoir,
J'ai lu tous les liens que tu as postés et cela m'a permis de comprendre un peu mieux.
Pour mon problème de génération de license, dois-je donc commencer par la génération d'une pair de clés privée/publique ?
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 // Générer une paire de clef RSA public static KeyPair generateKeyPair() throws CryptoException { try { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); SecureRandom rand = SecureRandom.getInstance("SHA1PRNG"); keyPairGen.initialize(2048, rand); KeyPair keyPair = keyPairGen.generateKeyPair(); return keyPair; } catch (NoSuchAlgorithmException ex) { throw new CryptoException("Probleme lors de la création de la pair de clef : "+ex.getMessage()); } catch (InvalidParameterException ex) { throw new CryptoException("Probleme lors de la création de la pair de clef : "+ex.getMessage()); } }
Code:
1
2
3
4
5
6
7 // Crypter avec la clef publique public static byte[] cryptWithRSAPublicKey(byte[] plainText,RSAPublicKey clefPublique) { BigInteger msgEnBigInteger = new BigInteger(plainText); BigInteger msgCrypte = msgEnBigInteger.modPow(clefPublique.getPublicExponent(), clefPublique.getModulus()); return msgCrypte.toByteArray(); }
Et après ?Code:
1
2
3
4
5
6
7 // Décrypter avec la clef privé public static byte[] decryptWithRSAPrivateKey(byte[] cipherText,RSAPrivateKey clefPrivee) { BigInteger msgADecrypteEnBigInt = new BigInteger(cipherText); BigInteger msgDecrypteEnBigInt = msgADecrypteEnBigInt.modPow(clefPrivee.getPrivateExponent(), clefPrivee.getModulus()); return msgDecrypteEnBigInt.toByteArray(); }
Dois-je maintenant créer un certificat ? et un keystore pour stocker mes clés ? Je suis un peu confus :?
si tu part sur les clés publiques / privées, tu va devoir faire ceci
de ton coté (et en dehors du code de ton application)
créer une paire clé publique / clé privée
ces deux clés peuvent se résumer à deux byte[]
stocker un de ces byte[] chez toi, ce sera la clé privée
stocker l'autre byte[] dans ton application
tu va devoir te faire ensuite un petit code qui utilise ton byte[] privé pour générer des fichiers de licences (donc chez toi)
et tu va devoir du coté application décoder ces fichiers de licence en utilisant le byte[] de la clé pubique.
Le keystore c'est un moyen simple de stocker la clé (publique par exemple). Trop simple dans ton cas. Si l'utilisateur veut faire ses propres ficheir de licence il aura qu'à "cracker" ton application en injectant simplement dans le key store sa propre clé publique, beaucoup d'efforts pour pas grand chose finalement. Par contre tu peux éventuellement l'utilisé coté privé pour protéger ta clé privée avec un mot de passe.
Donc tu va devoir jouer au plus fin pour planquer la clé quelque part dans ton code et éparpiller un peu partout el code de vérification. Bref faire exactement le contraire de ce qu'on préconise en programmant habituellement :D. Les obfuscateurs pourront aussi, à la fin, t'aider à cacher un peu plus ton code ^^
Merci beaucoup pour tes explications très fructueuses.
J'ai réussi à créer une clé privée et une clé publique.
J'ai sauvé la clé privée dans un keystore.
Pour tester, j'ai crypté un String que j'ai nommé "license" à l'aide de ma clé privée comme ceci.
Ca a l'air correct vu que j'ai un code bizarre comme résultat.Code:byte[] licCrypt = cryptWithRSAPrivateKey("licence", privKey);
Ensuite, je crée un fichier avec cette license cryptée à l'aide de la clé privée comme ceci:
Ca va jusque là ?Code:
1
2
3
4 FileOutputStream fos = new FileOutputStream("License.lic"); fos.write(licCrypt); fos.close();
Si j'ai bien compris, je dois donner ce fichier au client, et le décoder dans mon application à l'aide de ma clé publique ?
Je dois donc stocker dans mon appli le String "license" et la clé publique, et le résultat du décodage du fichier de license à l'aide de cette clé publique doit être égal à mon String "license", c'est bien ça ?
dans l'idée c'est ça oui, sauf qu'on se content pas du String "licence" d'habitude, on stocke quelque chose d'un peux plus évolué, genre
Et dans ton code tu parse la string pour extraire ces information. A la moindre erreur tu présume que le fichier est corrompu ou invalide:)Code:
1
2
3
4
5 "Ceci est un fichier de licence Application: nom de l'application Version: versions de l'application pour laquelle c'est valable Expiration: éventuellement date de fin de licence Utilisateur: nom du client"
Ca permet d'individualiser les clés pour chaque client:)
En fait le String "license" c'était juste un exemple évidemment :D
Sinon, comment générer une license propre à une machine ? Parce que je n'aimerais pas que l'application soit utilisée par quelqu'un d'autre qui l'aurait copiée avec la license. Quel serait le meilleur moyen pour limiter la license à une machine ?
là ca se complique :) Et je connais pas beaucoup d'outils commerciaux qui fassent ce genre de vérification
J'utilise System.getProperty("user.name") et je créé un fichier dans System.getProperty("user.home") avec le nom de l'appli.
Ainsi le programme ne fonctionne que sur cette machine et une copie du programme avec ou sans cette signature ne fonctionne pas sur d'autre machine.
C'est une protection suffisante contre la copie courante d'appli étant entendu que rien ne résiste au hackage.
Salut tchize_,
J'ai trouvé le moyen d'avoir le numéro de série du disque dur.
En résumé, j'ai fait un petit utilitaire qui me permet de créer un fichier de license pour le client.
Je complète des jTextField avec le num et le nom de l'entreprise du client, le nom et la version de mon application, la date de début et de fin de la license.
Je concatène le tout avec le SN du HDD du client et j'obtiens un String que je chiffre avec la clé privée extraite de mon KeyStore.
Le résultat de ce chiffrement est un code que je stocke dans un fichier "licence.lic" qui se trouvera dans le répertoire de l'application sur le pc du client.
Jusque là tout va bien ???
Comment faire après ?
Comment stocker la clé publique dans l'application du client ?
Dois-je créer un keystore chez le client avec uniquement la clé publique, puis mon application extraira cette clé de ce keystore afin de déchiffrer le contenu du fichier "license.lic" ???
Dois-je donc alors stocker mon String concaténé "en clair" dans mon application pour la comparaison ?
Salut william,
Je connais un peu System.getProperty, mais pourrais-tu m'expliquer ton procédé un peu plus en détails ? Ca a l'air intéressant.
Comment stocker la clé publique dans l'application du client ?
L'imagination est reine dans ce domaine là, à toi de trouver un moyen tordu pour la stocket pas trop visiblementDois-je créer un keystore chez le client avec uniquement la clé publique, puis mon application extraira cette clé de ce keystore afin de déchiffrer le contenu du fichier "license.lic" ???
Tu peux, mais un keystore c'est facilement remplacable, t'aura fait tout ce boulot pour rien si il suffit à ton utilsiateur de fournir son propre store pour tout casser :)Dois-je donc alors stocker mon String concaténé "en clair" dans mon application pour la comparaison ?
A priori, tu extrait les données et tu les compare (la date de validité est-elle dépassé, le numéro de série du disque correspond-il bien à celui de la machine actuelle, ....)
Oui mais je dois aussi déchiffrer à l'aide de la clé publique le code contenu dans le fichier "license.lic", et comparer le résultat de ce déchiffrement qui doit être égal à mon String concaténé que j'ai créé, non ?
C'était ça le but quand même ou je me trompe ?
le but c'est d'avoir un fichier licence.lic crypté qui contient (décrypté)
"user
machine
midsque dur
blabla
dates
etc"
(bref ce que tu veux), de le décrypter puis de prendre un à un ces champs et comparer leur valeur avec ce qu'il y a réellement sur la machine (date dépassée? disque dur correct?)
Je vois, une sorte de fichier Properties (clé = valeur) que je crypte avec ma clé privée de mon côté, et que je décrypte avec la clé publique du côté client, et ensuite comparer toutes ces données, c'est bien ça ?
Cela est relativement simple je concatene le nom du logiciel + le user.name
je crypte cela me donne un code spécifique à l'application sur cet ordi particulier
Pour l'enregistrement de la licence j'affiche le code crypté et je propose la saisie de la clé cryptée.
Lors de la saisie je décrypte la clé et le code s'il sont égaux la licence est valide. j'enregistre le code dans un fichier dans le user.home avec comme nom de fichier le nom du logiciel.
En exploitation si le fichier est présent et est valide alors l'application est débridée.
je converti ma string code en valeur hexa et j'affiche cette valeur hexa. la clé doit être saisie en hexa une algo décale cette clé hexa et doit retomber sur la valeur de code.
Donc en résumé:
- user.name + nom application = string "concat"
- tu cryptes "concat" à l'aide de ta clé privée.
- tu codes cela en hexa et tu proposes au client de saisir ce code hexa que tu stockes dans un fichier dans user.home
- après saisie, tu décryptes ce code hexa avec ta clé publique
- et tu compares les 2
C'est bien ça ?
- user.name + nom application = string "concat"
oui
- tu cryptes "concat" à l'aide de ta clé privée.
oui j'obtien un code
je demande au client de m'envoyer ce code
à réception du code je calcule sa clé et je lui envoi
il saisie la clé dans son soft, lors de la saisie si la clé ok je l'enregistre dans un fichier dans user.home
Super merci :ccool:
Et 2 users ayant le même nom sur 2 pc différents pourront alors utiliser ton logiciel ?
Salut tchize_,
Quand je demandais comment stocker la clé publique dans l'application, je voulais dire où est-ce que mon appli va aller la chercher pour décrypter la license ? A partir de quelle source ?
Pour le moment cette clé publique est dans un keystore (protégé par mot de passe) avec la clé privée, et ce keystore reste chez moi évidemment.
Le disque dur n'est pas une bonne idée en soit, imagines que l'utilisateur en change....
Le seul élément dont on est sur qu'il va constamment rester identique est l'adresse MAC de l'ordinateur.
Celà est possible à partir de java 6 en java http://www.kodejava.org/examples/250.html ou en utilisant les commandes systèmes telles que ifconfig/ipconfig/... selon les OS
Et 2 users ayant le même nom sur 2 pc différents pourront alors utiliser ton logiciel ?
Oui
ta clé publique se résume à un byte[].
Une version naive de stockage de la clé publique serait une classe ou tu aurait
Je dit naif puisque, comme elle est centralisé elle est facilement changeable. La complexité de ta tâche dépendra de la sécurité que tu veux mettre dedans, et cette sécurité dépendra de l'intérêt que tu aura à protéger ton application. :)Code:publis static byte[] LICENCE_KEY = new Byte[] {0x24,0x45,0xa0,0x5b,0x0F,......}
Bah mon application n'est pas une révolution et elle sera majoritairement utilisée par des gens qui n'y connaissent pas grand chose en informatique, mais j'aimerais quand même que ça soit sécurisé un minimum.
Est-ce que cette solution de stockage "naif" suffit ?
Y'aurait-il d'autres solutions pour que mon appli puisse accéder à cette clé publique ?
Comment vous faites vous tous ???
Je compte à la fin obfusquer mes sources pour brouiller un peu les pistes et rendre la tâche difficile à un éventuel hacker.
Bonjour,
Pour la date d'expiration de la licence, je compare simplement avec la date système du pc du client. Mais si le client est malin et change la date de son pc, mon application n'expire jamais.
Comment peut-on remédier à ça ?
Sauvegarder la date et la timezone à chaque exécution et vérifier que la date précédente n'es pas postérieure à la date courante. (prendre en compte le changement de timezone et le changement d'heure été/hiver sur certaines configs.
Pour l'instant, j'ai testé en sauvegardant bêtement les dates dans un fichier et ça fonctionne. Le problème est que, si le client trouve la combine de modifier ce fichier, l'application n'expirera jamais.
Il faudrait pour ça qu'il change le fichier avant chaque démarrage de l'appli, un poil contraignant. Puis il te suffit de crytper ton fichier pour que la plupart des utilisateurs n'y arrivent pas. Un gars qui saura décrypter ce genre de fichiers saura de toute façon déjouer toute protection que tu mettras en place...
Bonjour,
j'ai développé une application java pour une clinique, et mon but est d'essayer de la sécuriser au maximum en sachant que je ne suis pas expert en cryptographie.
Voilà un peu en gros ce que j'ai fait, j'aimerais avoir un peu votre avis histoire de ne pas rester bête et de continuer à apprendre.
Une fois mon application installée sur un ordinateur de la clinique, j'exécute sur ce pc également un petit programme à moi qui va générer une licence pour le client.
- Je génère donc chez moi une paire de clés privée/publique que je stocke (chez moi) dans un keystore *.p12
- Je crée un fichier "licence" avec l'id du client, l'adresse mac de son pc, la date de début et de fin de la licence.
- Je chiffre ce fichier "licence" à l'aide de ma clé privée, et je le sauvegarde sur le disque du client.
- Je sauvegarde également la clé publique dans un fichier sur le disque du client.
- Lorsque le client exécute l'application, cette dernière va déchiffrer le fichier "licence" à l'aide de la clé publique, et vérifier sa validité en comparant les données du fichier "licence" avec celles du fichier Porperties (id du client), ainsi que l'adresse mac du pc avec celle contenue dans le fichier "licence", et enfin la date d'expiration.
- Concernant la date d'expiration, je sauvegarde la date courante à chaque exécution de l'application dans un fichier "dateExec" sur le pc du client. Ce fichier est chiffré à l'aide d'une clé AES (stockée aussi sur le pc du client). Je vérifie que cette date soit bien antérieure à la date courante pour éviter que le client triche en revenant à une date antérieure.
Voilà pour la partie licence.
Pour se connecter à la BD, les utilisateurs doivent entrer leur login et mdp. Les mdp sont hashés MD5 et stockés dans la BD.
J'ai aussi un fichier bd.conf contenant les données "en clair" pour se connecter à la BD. Je compte le crypter également avec une clé AES.
Que pensez-vous de tout cela ? Vos critiques argumentées et conseils sont les bienvenus. Merci d'avance.
PS: J'ai également obfusqué mon *.jar avec proguard histoire de brouiller un peu plus les pistes, mais les string restent codées "en clair" après décompilation, ce qui n'est pas très sûr je pense...
Salut
Ca me aprait pas mal, mais un gros trou : la clef AES stockée en clair chez le client. Autant ne pas crypter si tu fais ça :D
Ne peut tu faire une requête toute simple à un server NTP (par exemple) ? Comme ça tu reçois un temps sûr et non manipulable sans avoir besoin de crypter. Tu compares la date reçue avec celle du server en autorisant une marge d'erreur si tu le souhaites. Enfin à toi de voir :)