Brevet FR2887097A1 — publié le 20061215

PROCEDE DE PROTECTION D'UN CODE-SOURCE EN LANGAGE SEMI- INTERPRETE La présente invention concerne un procédé de protection d'un code-source en langage semi-interprété. L'invention trouve une application particulièrement avantageuse dans le domaine de la protection des codes-source en langage semi-interprété contre la reproduction illicite ou leur utilisation non autorisée par un distributeur. On entend ici par langage semi-interprété un langage reposant sur le principe d'une machine virtuelle et d'un code intermédiaire, ou codeoctet . Référence sera faite plus spécialement au langage Java, bien que l'invention puisse être mise en oeuvre par d'autres langages, tels que Flash de io Macromédia et C# de Microsoft. Avant d'être exécutable, un code-source Java écrit par un développeur est compilé en code-octet ou bytecode , c'est-à-dire en un langage interprétable directement par une machine virtuelle Java (JVM) qui charge ce code en utilisant un chargeur de classes ( Classloader ). Ce chargeur de classes est un composant de la machine virtuelle. Cependant, il est possible de facilement décompiler ce code-octet en utilisant des programmes de décompilation, accessibles sur l'Internet par exemple. Au cours de cette décompilation, certaines informations sont perdues, comme par exemple les commentaires et le nom de variables locales, mais le code-octet lui-même reste lisible par un développeur. On comprend alors qu'il est à la portée d'un fraudeur de décompiler le code-octet pour accéder au code-source correspondant, de le copier et éventuellement de le modifier. D'où la possibilité de réaliser des contrefaçons ainsi que des versions pirates en décompilant et en modifiant le code qui sert à vérifier la présence d'une clé de licence. Pour tenter de limiter la fraude, certains éditeurs d'applications Java ont recours au procédé dit d'obfuscation - par obfuscation d'un code étant entendu l'obscurcissement de ce code afin de le rendre illisible - qui consiste, au moyen de programmes appelés obfuscateurs , à transformer le code- s octet de telle façon qu'il soit isofonctionnel, c'est-à-dire sans changement visible lors d'une exécution correcte du programme, mais moins facile à comprendre après décompilation du fait que toutes les classes, variables, méthodes sont renommées lors de l'obfuscation avec des séquences qui n'ont plus aucun sens pour un lecteur humain (noms du type a , aa , b , io etc). Bien entendu, cette technique n'empêche pas pour autant de décompiler le code-octet, mais la réutilisation du code-octet décompilé est rendue plus délicate. Toutefois, même moins lisible, ce code est toujours vulnérable aux pirates et aux contrefacteurs. Un autre procédé pour limiter les risques de décompilation du code-octet consiste à utiliser un utilitaire pour compiler directement le code-octet Java en code natif. On obtient ainsi un programme binaire qui pourra être exécuté sur le système d'exploitation pour lequel le code-octet a été compilé en code natif. La décompilation de ce programme exécutable est très difficile puisqu'il s'agit cette fois de décompiler du code-machine. Ces procédés connus visant à limiter l'accès au code-source Java par décompilation du code-octet correspondant présentent cependant un certain nombre d'inconvénients. Le procédé par obfuscation conduit à un code-octet, certes obfusqué, 25 mais toujours décompilable. Le code-source qui en est tiré est seulement plus difficile à comprendre pour un développeur. D'autre part, le renommage systématique des classes, variables, etc, peut également poser des problèmes dans le sens où il rend plus difficile l'interprétation d'une file d'exécution. Ainsi, lorsque l'application part en échec, sur une exception non traitée par exemple, la file d'exécution affichée à la machine virtuelle Java devient assez peu compréhensible à cause des renommages et donc difficilement exploitable quand il s'agit de traquer un bogue notamment. Le procédé par compilation vers un code natif, s'il réduit fortement le risque de décompilation, supprime néanmoins un des avantages principaux de l'environnement Java, à savoir sa portabilité. En effet, le code natif est destiné à une version donnée d'un système d'exploitation donné, alors que l'intérêt de Java est de pouvoir exécuter des codes, en fait des codes-octet, sur n'importe quel système sur lequel une machine virtuelle conforme au standard a été installée. Ainsi, si on veut s'appuyer sur ce procédé de protection par code natif, il faut prévoir autant de versions compilées que de systèmes d'exploitation cibles. Cette situation est difficilement gérable pour un éditeur de io logiciels. Aussi, le problème technique à résoudre par l'objet de la présente invention est de proposer un procédé de protection d'un code-source en langage semi-interprété, qui permettrait d'améliorer la protection du code-source contre la décompilation sans perdre le bénéfice de la portabilité du langage semi-interprété utilisé, notamment Java, et sans rendre plus compliqué le dépistage d'erreurs dans le code résultant de l'application du procédé. La solution au problème technique posé consiste, selon la présente invention, en ce que, ledit code-source étant compilé en un code-octet, ledit 20 procédé comprend les étapes consistant à : - chiffrer ledit codeoctet par un algorithme de chiffrement, - fournir à une machine utilisatrice dudit code-source le code-octet chiffré et le code-octet d'un chargeur de classes déchiffrant apte à déchiffrer ledit code-octet chiffré au moyen d'une clé de déchiffrement disponible sur ladite machine utilisatrice. La figure 1 montre un schéma illustratif du procédé de protection conforme à l'invention. Le procédé selon l'invention présente donc l'intérêt qu'une fois chiffré le code-octet 13 associé au code-source 11 à protéger n'est plus décompilable puisqu'il ne s'agit plus alors d'un code-octet interprétable comme tel par une machine virtuelle Java par exemple. Par contre, après déchiffrement dans la machine 20, le dépistage et la correction de bogues restent possibles sur le code-octet 12 ainsi déchiffré. L'exécution du code-octet 12 repose sur l'ajout d'un composant spécifique, appelé ici chargeur 14 de classes déchiffrant, qui a pour fonction de déchiffrer le code-octet chiffré 13 en le chargeant dans la machine virtuelle Java 20. A cet effet, le chargeur 14 de classes déchiffrant retrouve ou accède s à la clé KS de déchiffrement disponible dans la machine 20. Dans le cas où, comme le prévoit l'invention, ledit algorithme de chiffrement est un algorithme cryptographique symétrique, la même clé KS sert au chiffrement du code-octet 12 par le distributeur 10 du codesource 11 et au déchiffrement par le chargeur 14 de classes déchiffrant du code-octet chiffré io 13 reçu par la machine 20. La clé KS de déchiffrement est, par exemple, stockée dans une mémoire de la machine 20 ou encore fournie par l'utilisateur dans une mémoire enfichable. Le chargeur 14 de classes déchiffrant est lui-même écrit en langage Java et utilisé à la place du chargeur de classes standard. Pour qu'il soit facilement utilisable par la machine 20, son propre code-octet n'est pas chiffré. Cependant, si on veut le protéger lui-même contre la décompilation, l'invention propose, par exemple, que ledit code-octet du chargeur 14 de classes déchiffrant est fourni sous forme obfusquée. En plus de la protection du code-source 11 contre la décompilation du code-octet 12 associé, le procédé conforme à l'invention permet également de protéger le même code-source contre une utilisation non autorisée par son distributeur 10. Si l'on souhaite que le code-source 11 ne soit utilisable que sur une machine 20 donnée, une première solution consiste en ce que ledit algorithme de chiffrement utilise une clé KS de chiffrement caractéristique d'une machine 20 donnée, et en ce que ledit chargeur 14 de classes déchiffrant est apte à reconstituer ladite clé Ks. Cette solution repose sur le fait que la clé KS de chiffrement, et donc la clé de déchiffrement dans le cadre d'un algorithme symétrique, est rendue fortement adhérente à la machine 20. Dans un mode de réalisation, ladite clé caractéristique est constituée à partir d'une adresse IP de ladite machine. Dans un autre mode de réalisation, ladite clé caractéristique est constituée à partir d'une adresse MAC de ladite machine. La clé est déduite de cette donnée d'adresse (Ks=f(adresselP) par exemple), ou autre, par le distributeur 10 et également par le chargeur 14 de classes déchiffrant de la machine 20 en effectuant un calcul sur cette donnée selon un algorithme f connu des deux parties. Si l'on souhaite que le code-source 11 ne puisse être utilisé que par un seul utilisateur, une deuxième solution consiste en ce que ledit algorithme de chiffrement utilise une clé Ks de chiffrement chiffrée au moyen d'une clé publique Kpublique d'un utilisateur (K's=g(Ks,Kpublique)), et en ce que ledit chargeur 14 de classes déchiffrant est apte à déchiffrer ladite clé K's de chiffrement io chiffrée, au moyen de la clé privée Kprivée de l'utilisateur associée à ladite clé publique Kpubiique (Ks=9-1(K's,Kprivée)). Selon cette solution, le code-octet 12 du code-source 11 dont on veut limiter la diffusion est chiffré par le distributeur 10 au moyen d'une clé Ks de chiffrement symétrique générée aléatoirement chez le distributeur 10. Le code-octet 13 ainsi chiffré est fourni à l'utilisateur accompagné de K's qui est la clé Ks de chiffrement elle- même chiffrée au moyen d'une clé publique Kpublique de l'utilisateur selon un algorithme de chiffrement asymétrique. A la réception du code- octet chiffré 13 et de la clé K's de chiffrement chiffrée, le chargeur 14 de classes déchiffrant déchiffre la clé K's de chiffrement au moyen de la clé privée Kprivée de l'utilisateur. A titre d'exemple, ladite clé privée est contenue dans un certificat détenu par ledit utilisateur dans une mémoire enfichable par exemple. Enfin, la clé Ks de chiffrement symétrique, une fois déchiffrée, sert au chargeur 14 de classes déchiffrant à déchiffrer le code-octet 13. Enfin, si l'on souhaite limiter l'utilisation du code-source dans le temps, une troisième solution consiste en ce que ledit algorithme de chiffrement utilise une clé Ks de chiffrement constituée à partir de la date limite d'utilisation dudit code-source Java 11, et en ce que ledit chargeur 14 de classes déchiffrant est apte à reconstituer ladite clé à partir de la date courante. Prenons l'exemple où la date limite pour l'utilisation autorisée du codesource vaut 123456999 unités de temps à partir d'une origine donnée. Le distributeur 10 chiffre le code-octet 12 au moyen de la clé symétrique Ks 123456 et installe dans le chargeur 14 de classes déchiffrant un algorithme indiquant que la clé Ks de déchiffrement est obtenue en tronquant la date courante de ses trois derniers chiffres. Tant que la date courante de la machine peut s'écrire 123456xyz avec xyz 999, le déchiffrement est correctement effectué. Au-delà de la date limite d'utilisation, la clé calculée par le chargeur de classes déchiffrant s'écrira 123457 et le déchiffrement ne pourra plus avoir lieu. L'invention concerne également un dispositif de chiffrement d'un codesource en langage semi-interprété, remarquable en ce que, ledit codesource (11) étant compilé en un code-octet (12), ledit dispositif comprend: - des moyens pour chiffrer ledit code-octet (12) par un algorithme de io chiffrement, - des moyens pour fournir à une machine utilisatrice (20) dudit code-source (11) le code-octet chiffré (13) et le code-octet d'un chargeur (14) de classes déchiffrant apte à déchiffrer ledit code-octet chiffré (13) au moyen d'une clé (KS) de déchiffrement disponible sur ladite machine utilisatrice (20). L'invention concerne encore un dispositif de déchiffrement d'un codesource en langage semi-interprété, ledit code-source (11) étant compilé en un code-octet chiffré (13) par un algorithme de chiffrement, remarquable en ce que ledit dispositif de déchiffrement dispose d'une clé (Ks) de déchiffrement dudit code-octet chiffré (13). L'invention concerne encore un programme d'ordinateur de chiffrement d'un code-source en langage semi-interprété, ledit code-source (11) étant compilé en un code-octet (12), ledit programme d'ordinateur comprenant des instructions de chiffrement dudit code-octet (12) par un algorithme de chiffrement, et de fourniture du code-octet chiffré (13) et du code- octet d'un chargeur (14) de classes déchiffrant à une machine utilisatrice (20) dudit code-source (11), ledit chargeur (14) de classes déchiffrant étant apte à déchiffrer ledit code-octet chiffré (13) au moyen d'une clé (KS) de déchiffrement disponible sur ladite machine utilisatrice (20), ainsi qu'un programme d'ordinateur de déchiffrementd'un code-source en langage semi-interprété, ledit code- source (11) étant compilé en un code-octet chiffré comprenant des instructions de déchiffrement d'un code octet chiffré reçu à l'aide du code-octet d'un chargeur (14) de classes déchiffrant reçu apte à déchiffrer ledit code-octet chiffré (13) au moyen d'une clé (Ks) de déchiffrement disponible. Enfin, l'invention concerne la mise à disposition par téléchargement du programme d'ordinateur de déchiffrement selon l'invention. REVENDICATIONS 1. Procédé de protection d'un code-source en langage semi-interprété, caractérisé en ce que, ledit code-source (11) étant compilé en un codeoctet (12), ledit procédé comprend les étapes consistant à : -chiffrer ledit code-octet (12) par un algorithme de chiffrement, -fournir à une machine utilisatrice (20) dudit code-source (11) le code-octet chiffré (13) et le code-octet d'un chargeur (14) de classes déchiffrant apte à déchiffrer ledit code-octet chiffré (13) au moyen d'une clé (K5) de déchiffrement disponible sur ladite machine utilisatrice (20). 2. Procédé selon la revendication 1, caractérisé en ce que ledit algorithme de 15 chiffrement est un algorithme cryptographique symétrique. 3. Procédé selon l'une des revendications 1 ou 2, caractérisé en ce que ledit code-octet du chargeur (14) de classes déchiffrant est fourni sous forme obfusquée. 4. Procédé selon l'une quelconque des revendications 1 à 3, caractérisé en ce que ledit algorithme de chiffrement utilise une clé de chiffrement caractéristique d'une machine (20) donnée, et en ce que ledit chargeur (14) de classes déchiffrant est apte à reconstituer ladite clé. 5. Procédé selon la revendication 4, caractérisé en ce que ladite clé caractéristique est constituée à partir d'une adresse IP de ladite machine (20). 6. Procédé selon la revendication 4, caractérisé en ce que ladite clé caractéristique est constituée à partir d'une adresse MAC de ladite machine (20). 7. Procédé selon l'une quelconque des revendications 1 à 6, caractérisé en ce que ledit algorithme de chiffrement utilise une clé (K5) de chiffrement chiffrée au moyen d'une clé publique (Kpublique) d'un utilisateur, et en ce que ledit chargeur (14) de classes déchiffrant est apte à déchiffrer ladite clé de chiffrement chiffrée, au moyen de la clé privée (Kprivée) de l'utilisateur associée à ladite clé publique. 8. Procédé selon la revendication 7, caractérisé en ce que ladite clé privée (Kprivée) est contenue dans un certificat détenu par ledit utilisateur. 9. Procédé selon l'une quelconque des revendications 1 à 8, caractérisé en ce que ledit algorithme de chiffrement utilise une clé (KS) de chiffrement constituée à partir de la date limite d'utilisation dudit code-source (11), et en ce que ledit chargeur (14) de classes déchiffrant est apte à reconstituer ladite clé à partir de la date courante. io 10. Dispositif de chiffrement d'un code-source en langage semiinterprété, caractérisé en ce que, ledit code-source (11) étant compilé en un code-octet (12), ledit dispositif comprend: - des moyens pour chiffrer ledit code-octet (12) par un algorithme de chiffrement, - des moyens pour fournir à une machine utilisatrice (20) dudit code-source (11) le code-octet chiffré (13) et le code-octet d'un chargeur (14) de classes déchiffrant apte à déchiffrer ledit code-octet chiffré (13) au moyen d'une clé (KS) de déchiffrement disponible sur ladite machine utilisatrice (20). 11. Dispositif de déchiffrement d'un code-source en langage semiinterprété, ledit code-source (11) étant compilé en un code-octet chiffré (13) par un algorithme de chiffrement, caractérisé en ce que ledit dispositif de déchiffrement dispose d'une clé (Ks) de déchiffrement dudit code-octet chiffré (1 3). 12. Programme d'ordinateur de chiffrement d'un code-source en langage 25 semi-interprété, ledit code-source (11) étant compilé en un code-octet (12), ledit programme d'ordinateur comprenant des instructions de: chiffrement dudit code-octet (12) par un algorithme de chiffrement, - de fourniture du code-octet chiffré (13) et du code-octet d'un chargeur (14) de classes déchiffrant à une machine utilisatrice (20) dudit code-source (11), ledit chargeur (14) de classes déchiffrant étant apte à déchiffrer ledit code-octet chiffré (13) au moyen d'une clé (KS) de déchiffrement disponible sur ladite machine utilisatrice (20). io 13. Programme d'ordinateur de déchiffrement d'un code-source en langage semi-interprété, ledit code-source (11) étant compilé en un code-octet chiffré comprenant des instructions de déchiffrement d'un code octet chiffré reçu à l'aide du code-octet d'un chargeur (14) de classes déchiffrant reçu apte à déchiffrer ledit code-octet chiffré (13) au moyen d'une clé (KS) de déchiffrement disponible.