content:?
?
First, why do you want to encrypt? ?
Second, custom class loader??
Third, encryption, decryption?
Fourth, application examples?
V. Precautions?
Reference resources?
The source code of the Java program is easily peeled by others. As long as there is an anti-compiler, anyone can analyze the code of others. This article discusses how to protect source code by encryption technology without modifying the original program.
First, why do you want to encrypt?
For languages such as traditional C or C , it is easy to protect source code on the web, as long as it does not release it. Unfortunately, the source code of the Java program is easily peeked by others. As long as there is an anti-compiler, anyone can analyze the code of others. The flexibility of Java makes the source code are easily stealing, but at the same time, it also makes it relatively easy to encrypt the code, and the only thing we need to know is Java's ClassLoader object. Of course, in the process of encryption, knowledge about Java? Cryptography® Extension (JCE) is also indispensable.
There are several technologies that can "fuzzy" Java files that make the anti-compiler handle the effect of the class file. However, modifying the anti-compiler makes it possible to handle these fuzzy-processed class files, so it is not easy to rely on fuzzy techniques to ensure the security of the source code.
We can encrypt applications with popular encryption tools, such as PGP (Pretty? Good? Privacy) or GPG (GNU? Privacy? Guard). At this time, the end user must first decrypt before running the application. But after decryption, the end user has a non-encrypted class file, which does not differ in advance.
The mechanism of Java runtime installation is implicitly implicitly implicitly modified. JVM requires an object called ClassLoad every time you load a class file, which is responsible for putting new classes into running JVM. JVM gives the ClassLoader a string containing the name of the name (such as a java.lang.object), then by the ClassLoader to find the class file, load the original data, and convert it into a Class object.
We can modify it before the class file is executed by custom ClassLoader. This technology is very wide - here, its use is to decrypt when the class file is loaded, so it can be seen as an instant decryption device. Since the decrypted bytecode file will never save to the file system, the screwdriver is hard to get the decrypted code.
Since the process of converting the original byte code into a Class object is completely responsible for the system, create a custom ClassLoader object is not difficult, just get the original data first, then you can make any conversion in the decryption.
Java? 2 simplifies custom ClassLoader builds to a certain extent. In Java? 2, LoadClass's default implementation is still responsible for processing all required steps, but in order to take into account various customized class equipment, it also calls a new FindClass method.
This provides a shortcut to our customized ClassLoader, which reduces trouble: just override FindClass instead of overwriting loadingClass. This approach avoids the public steps necessary to repeat all loaders because all of this is responsible by loadingClass.
However, the custom ClassLoader herein does not use this method. the reason is simple. If you look for encrypted class files by the default classloader, it can be found; but because the class file has been encrypted, it will not recognize this class file, and the load process will fail. Therefore, we must implement loadclass themselves, and some workload has increased slightly.
Second, custom class loader
Each running JVM already has a ClassLoader. This default ClassLoader is looking for a suitable bytecode file in a local file system based on the value of the ClassPath environment variable. Application Custom ClassLoader requires a more in-depth understanding of this process. We must first create an instance of custom ClassLoader classes, and then explicitly require it to load another class. This enables the JVM to associate this class and all the classes they need to customized ClassLoader. LISTING? 1 shows how to put into class files with custom ClassLoader.
[LISTING? 1: Using custom ClassLoader to load class files]
?? //? First create a ClassLoader object
?? ClassLoader? Myclassloader? =? New? Myclassloader ();
?? //? Using custom ClassLoader objects to load class files
?? //? And convert it into a Class object
?? Class? Myclass? =? Myclassloader.loadclass (? "Mypackage.myclass"?);
?? //? Finally, create an instance of this class
?? Object? Newinstance? =? Myclass.newinstance ();
?? //? Note that all other classes needed by Myclass will pass
?? //? Customized ClassLoader automatic loading
?
As mentioned earlier, custom ClassLoad is just acquire data of class files, and then pass the byte code to the runtime system, which completes the remaining tasks by the latter.
ClassLoader has several important ways. When you create a custom ClassLoader, we only need to overwrite one of them, that is, LoadClass, providing code for obtaining the original class file data. This method has two parameters: the name of the class, and a mark indicating whether the JVM requires the parsing class name (ie, whether it is equally loaded with a dependency). If this tag is True, we only need to call ResolVeclass before returning JVM.
[Listing? 2: a simple implementation of classloader.loadclass ()]
?????? publicclass (? String? name,? boolean? resolve?)
?????? throws? ClassNOTFOUNDEXCEPTION? {
???? try? {
?????? //? We want to create a Class object
??????? class? Clasz? =? Null;
?????? //? Required step 1: If the class is already in the system buffer,
?????? //? We don't have to load it again
?????? clasz? =? findloadedclass (? name?);
?????? i (CLASZ?! =? null)
???????? RETURN? CLASZ;
?????? //? The following is a custom section
?????? Byte? classdata []? =? / *? Get the word code data through some way? * /;
?????? f? (ClassData?! =? null)? {
???????? //? Successfully read bytecode data, now convert it into a Class object
???????? Clasz? =? defineclass (? name,? ClassData,? 0,? ClassData.Length;
??????}
?????? //? Required step 2: If there is no success,
?????? //? We tried to load it with the default classloader
?????? i? (CLASZ? ==? null)
???????? Clasz? =? FindsystemClass (? name?);
?????? //? Required step 3: If necessary, load the related class ?????? f? (Resolve? &&? Clas?! =? Null)
???????? resolveclass (? Clasz?);
?????? //? Return the class to the caller
?????? Return? CLASZ;
????}? catch (? iException? ie?)? {
?????? throw? new? classnotfoundexception (? IE.tostring ()?);
????}? catch (? generalsecurityException? GSE?)? {
?????? throw? new? classnotfoundexception (? gse.tostring ()?);
????}
??}
?
LISTING? 2 shows a simple loadClass implementation. Most of the code is the same for all ClassLoader objects, but there is a small part (marked by comment marks). During the processing, the ClassLoader object is used to use several other auxiliary methods:
FindloadedClass: It is used to check to confirm that the requested class does not currently exist. The LoadClass method should first call it. ?
DefineClass: After getting the original class file number data, call DefineClass to convert it into a Class object. Any LoadClass implementation must call this method. ?
FindsystemClass: Provides support for default ClassLoader. If the custom method used to find the class cannot find the specified class (or intentionally do not customize the custom method), you can call the method to try the default loading method. This is useful, especially when loading standard Java classes from ordinary JAR files. ?
ResolVeclass: When the JVM wants to load not only includes the specified class, but also when all other classes referenced, it sets the LoadClass's Resolve parameter to TRUE. At this time, we must call ResolVeclass before returning to the originally loaded CLASS object to call the caller. ?
Third, encryption, decryption
Java encryption extension is Java? Cryptography? Extension, referred to as JCE. It is Sun's encryption service software that includes encryption and key generation functions. JCE is an extension of JCA (Java? Cryptography? Architecture).
JCE does not specify a specific encryption algorithm, but provides a framework, and the specific implementation of the encryption algorithm can be added as a service provider. In addition to the JCE framework, the JCE package also includes a SunJCE service provider, including many useful encryption algorithms, such as DES (Data? Encryption? Standard) and Blowfish.
For a simple meter, in this article we will encrypt and decrypt by the DES algorithm. Below is the basic steps that must be followed by JCE encryption and decryption data:
Step 1: Generate a security key. There is a key before encrypt or decrypt any data. The key is a small piece of data released with the encrypted application. LISTING? 3 shows how to generate a key. ? [LISTING? 3: Generate a key]
?? //? DES algorithm requires a trusted random number
?? Securerandom? Sr? =? New? Securerandom ();
?? //? Generate a keygenerator object for the DES algorithm we choose.
?? KEYGENERATOR? KG? =? KEYGENERATOR.GETITINSTANCE (? "Des"?);
?? kg.init (? sr?);
?? //? Generate a key
?? SECRETKEY? KEY? =? Kg.generateKey (); ?? //? Get key data
?? Byte? rawkeydata []? =? key.getencoded ();
?? / *? Next, you can encrypt or decrypt it with a key, or save it.
????? is used for files? * /
?? DOSMETHING (? rawkeydata?);
?
Step 2: Encrypt the data. After getting a key, you can use it to encrypt the data. In addition to decrypting ClassLoader, there is generally a separate program that encrypts the application (see Listing? 4). ? [Listing? 4: Encrypt the original data with a key]
???? //? DES algorithm requires a trusted random number
???? Securerandom? SR? =? New? Securerandom ();
???? byte? rawkeydata []? =? / *? Get key data in some way? * /;
???? //? Create a Deskeyspec object from the original key data
???? deskeyspec? DKS? =? New? Deskeyspec (? Rawkeydata?);
???? //? Create a key factory, then use it to convert Deskeyspec into
???? //? A SECRETKEY object
???? SECRETKEYFACTORY? KeyFactory? =? SecretKeyFactory.getInstance (? "Des"?);
???? secretkey? Key? =? KeyFactory.GenerateSecret (? DKS?);
???? //? Cipher object actually completed encryption operations
???? cipher? Cipher? =? Cipher.getInstance (? "Des"?);
???? //? Initialize the Cipher object with a key
???? cipher.init (? cipher.encrypt_mode,? key,? sr?);
???? //? Now, get data and encrypt
???? Byte? data []? =? / *? Get data in some way? * /
???? //? Officially perform encryption operations
???? Byte? EncryptedData []? =? cipher.dofinal (? data?);
???? //? Further handle the encrypted data
????????????????;
?
Step 3: Decrypt the data. ClassLoader analyzes and decrypts class files when running encrypted applications. Procedure such as Listing? 5. ? [LISTING? 5: Decrypt data with key]
???? //? DES algorithm requires a trusted random number
???? Securerandom? SR? =? New? Securerandom ();
???? byte? rawkeydata []? =? / *? Get the original key data in some way? * /;
???? //? Create a Deskeyspec object from the original key data
???? deskeyspec? DKS? =? New? Deskeyspec (? Rawkeydata?);
???? //? Create a key factory, then use it to convert Deskeyspec objects into
???? //? A SECRETKEY object
???? SECRETKEYFACTORY? KeyFactory? =? SecretKeyFactory.getInstance (? "Des"?);
???? secretkey? Key? =? KeyFactory.GenerateSecret (? DKS?);
???? //? Cipher object actually completes the decryption operation
???? cipher? Cipher? =? Cipher.getInstance (? "Des"?);
???? //? Initialize the Cipher object with a key
???? cipher.init (? cipher.decrypt_mode,? key,? sr?); ???? //? Now, get data and decrypt
???? byte? encrypteddata []? =? / *? Get encrypted data? * /
???? //? Officially perform decryption operations
???? byte? decrypteddata []? =? cipher.dofinal (? EncryptedData?);
???? //? Further handle the decimal data
????????????????;
?
Fourth, the application example
The previous introduction how to encrypt and decrypt data. To deploy an encrypted application, the steps are as follows:
Step 1: Create an app. Our example contains an App main class, two auxiliary classes (known as Foo and Bar, respectively). There is no practical function of this app, but as long as we can encrypt this app, encrypting other applications will not be in. ?
Step 2: Generate a security key. In the command line, use the GenerateKey tool (see GenerateKey.java) to write a file:?%? Java? GenerateKey? Key.Data
?
Step 3: Encrypt App. In the command line, use the EncryptClasses tool (see EncryptClasses.java) Encrypted Application Class:?%? Java? EncryptClasses? KEY.DATA? App.class? Foo.class? Bar.class? Bar.class?
?
This command replaces each .class file with their respective encryption versions. ?
Step 4: Run the encrypted application. The user runs encrypted applications through a DecryptStart program. DecryptStart programs are shown in Listing? 6. ? [LISTING? 6: DecryptStart.java, launching program encrypted application]
IMPORT? Java.io. *;
IMPORT? Java.security. *;
IMPORT? Java.lang.reflect. *;
IMPORT? JAVAX.CRYPTO. *;
IMPORT? JAVAX.CRYPTO.SPEC. *;
PUBLIC? CLAS? DECRYPTSTART? EXTENDS? CLASSLOADER
{
?? //? These objects are set in the constructor,
?? //? LoadClass () method will use them to decrypt classes.
?? private? secondkey? key;
?? private? cipher? cipher;
?? //? Constructor: Set the object required to decrypt
?? public? decryptStart (? SecretKey? KEY?)? THROWS? GeneralSecurityException,
?????? oException? {
???? this.key? =? key;
???? string? Algorithm? =? "Des";
???? Securerandom? SR? =? New? Securerandom ();
???? system.err.println (? "[Decryptstart:? CREATING? CIPHER]"?);
???? cipher? =? cipher.getinstance (? algorithm?);
???? cipher.init (? cipher.decrypt_mode,? key,? sr?);
??}
?? //? MAIN process: We have to read the key here, create DecryptStart
?? //? Instance, it is our custom ClassLoader.
?? //? After setting the ClassLoader, we use it to load the application instance.
?? //? Finally, we pass the Java? Reflection? API call application instance's main method ?? static? PUBLIC? VOID? · THROWS? EXCEPTION? {
???? string? Keyfilename? =? Args [0];
???? String? Appname? =? Args [1];
????? //? These are the parameters passed to the application itself.
???? String? Reaargs []? =? New? String [args.length-2];
???? system.Arraycopy (? Args,? 2,? Reaargs,? 0,? Args.length-2?);
???? //? Read the key
???? system.err.println (? "[Decryptstart:? Reading? Key]"?);
???? Byte? Rawkey []? =? util.readfile (? KEYFILENAME?);
???? deskeyspec? DKS? =? New? Deskeyspec (? Rawkey?);
???? SECRETKEYFACTORY? KeyFactory? =? SecretKeyFactory.getInstance (? "Des"?);
???? secretkey? Key? =? KeyFactory.GenerateSecret (? DKS?);
???? //? Create a decrypted ClassLoader
???? DecryptStart? DR? =? New? Decryptstart (? KEY?);
???? //? Create an instance of the application main class
???? //? Plug it through ClassLoader
???? system.err.println (? "[Decryptstart:? Loading?" Appname "]"?);
???? Class? Clasz? =? Dr.loadClass (? Appname?);
???? //? Finally, the application instance is called via the Reflection? API
???? //? MAIN () method
???? //? Get a reference to main ()
???? String? Proto []? =? New? String [1];
???? class? Maings []? =? {? (New? String [1]). GetClass ()?};
???? Method? Main? =? CLASZ.GETMETHOD (? "Main",? Margs?);
???? //? Create an array containing main () method parameters
???? Object? Argsarray []? =? {? Reaargs?}
???? system.err.println (? "[Decryptstart:? Running?" Appname ". Main ()]"?);
???? //? Call main ()
???? main.invoke (? null,? argsarray?);
??}
?? public? Class? loadclass (? string? name,? boolean? resolve?)
?????? throws? ClassNOTFOUNDEXCEPTION? {
???? try? {
?????? //? We want to create a Class object
?????? class? Clasz? =? Null;
?????? //? Required step 1: If the class is already in the system buffer
?????? //? We don't have to load it again
?????? clasz? =? findloadedclass (? name?);
?????? i (CLASZ?! =? null)
???????? RETURN? CLASZ;
?????? //? The following is a custom section
?????? try? {
????????? //? Read the encrypted class file ???????? Byte? Classdata []? =? Util.readfile (? Name ". Class";
???????? IF? (ClassData?! =? null)? {
?????????? //? Decrypt ...
?????????? Byte? decryptedclassdata []? =? cipher.dofinal (? ClassData?);
??????????? //? ...? Turn it into a class
?????????? Clasz? =? DefineClass (? Name,? decryptedclassdata,
????????????? 0, decryptedclassdata.length?);
?????????? system.err.println (? "[Decryptstart:? Decrypting? Class?" Name "]"?);
????????}
??????}? catch (? filenotfoundexception? fnfe?)? {
??????}
?????? //? Required step 2: If there is no success
?????? //? We tried to load it with the default classloader
?????? i? (CLASZ? ==? null)
???????? Clasz? =? FindsystemClass (? name?);
?????? //? Required step 3: If necessary, load the relevant class
?????? f? (resolve? &&? Clasz?! =? null)
???????? resolveclass (? Clasz?);
?????? //? Return the class to the caller
?????? Return? CLASZ;
????}? catch (? iException? ie?)? {
?????? throw? new? ClassNotFoundException (? ie.tostring ()
);
????}? catch (? generalsecurityException? GSE?)? {
?????? throw? new? ClassNotFoundException (? gse.tostring ()
);
????}
??}
}
?
For uncrypsed applications, normal execution is as follows:?%? Java? App? Arg0? Arg1? Arg2
?
For encrypted applications, the corresponding operation mode is:?%? Java? Decryptstart? Key.data? App? Arg0? Arg1? Arg2
?
DecryptStart has two purposes. An instance of a DecryptStart is a custom ClassLoader that implements instant decryption operations; at the same time, DecryptStart also contains a main process, which creates a decompressed instance and uses it to load and run applications. The code Application App to include app.java, foo.java and bar.java. Util.java is a file I / O tool that uses more examples in this example. Complete code Please download it from this article.
V. Precautions
We see that it is easy to encrypt a Java app without modifying the source code. However, there is no fully safe system in the world. This paper provides a certain degree of source code protection, but it is fragile for some attacks.
Although the application itself has been encrypted, the starter DecryptStart is not encrypted. An attacker can refactor to build the launcher and modify it, save the decrypted class file to disk. One way to reduce this risk is to perform high quality fuzzy processing on the startup program. Alternatively, the startup program can also be used directly into a machine language, so that the launcher has the security of traditional execution file formats.
In addition, it is necessary to remember that most JVM itself is not safe. The hacker may modify the JVM, obtain the decrypted code from the ClassLoader and save to disk, thus bypass the encryption technology of this article. Java did not provide truly effective remedies for this purpose. However, it should be noted that all these possible attacks have a premise, which is the attacker can get a key. If there is no key, the security of the application is entirely on the security of the encryption algorithm. Although this protective code is not perfect, it is still not a valid program for protecting intellectual property and sensitive user data.