当前位置:天才代写 > tutorial > JAVA 教程 > 运用加密技能掩护Java源代码

运用加密技能掩护Java源代码

2017-11-10 08:00 星期五 所属: JAVA 教程 浏览:339

副标题#e#

Java措施的源代码很容易被别人偷看。只要有一个反编译器,任何人都可以阐明别人的代码。本文接头如安在不修改原有措施的环境下,通过加密技能掩护源代码。

一、为什么要加密?

对付传统的C或C++之类的语言来说,要在Web上掩护源代码是很容易的,只要不宣布它就可以。遗憾的是,Java措施的源代码很容易被别人偷看。只要有一个反编译器,任何人都可以阐明别人的代码。Java的机动性使得源代码很容易被窃取,但与此同时,它也使通过加密掩护代码变得相对容易,我们独一需要相识的就是Java的ClassLoader工具。虽然,在加密进程中,有关Java Cryptography Extension(JCE)的常识也是必不行少的。

有几种技能可以“恍惚”Java类文件,使得反编译器处理惩罚类文件的结果大打折扣。然而,修改反编译器使之可以或许处理惩罚这些颠末恍惚处理惩罚的类文件并不是什么难事,所以不能简朴地依赖恍惚技能来担保源代码的安详。

我们可以用风行的加密东西加密应用,好比PGP(Pretty Good Privacy)或GPG(GNU Privacy Guard)。这时,最终用户在运行应用之前必需先举办解密。但解密之后,最终用户就有了一份不加密的类文件,这和事先不举办加密没有什么不同。

Java运行时装入字节码的机制隐含地意味着可以对字节码举办修改。JVM每次装入类文件时都需要一个称为ClassLoader的工具,这个工具认真把新的类装入正在运行的JVM。JVM给ClassLoader一个包括了待装入类(好比java.lang.Object)名字的字符串,然后由ClassLoader认真找到类文件,装入原始数据,并把它转换成一个Class工具。

我们可以通过定制ClassLoader,在类文件执行之前修改它。这种技能的应用很是遍及??在这里,它的用途是在类文件装入之时举办解密,因此可以当作是一种即时解密器。由于解密后的字节码文件永远不会生存到文件系统,所以窃密者很难获得解密后的代码。

由于把原始字节码转换成Class工具的进程完全由系统认真,所以建设定制ClassLoader工具其实并不坚苦,只需先得到原始数据,接着就可以举办包括解密在内的任何转换。

Java 2在必然水平上简化了定制ClassLoader的构建。在Java 2中,loadClass的缺省实现仍旧认真处理惩罚所有必须的步调,但为了顾及各类定制的类装入进程,它还挪用一个新的findClass要领。

这为我们编写定制的ClassLoader提供了一条捷径,淘汰了贫苦:只需包围findClass,而不是包围loadClass。这种要领制止了反复所有装入器必须执行的民众步调,因为这一切由loadClass认真。

不外,本文的定制ClassLoader并不利用这种要领。原因很简朴。假如由默认的ClassLoader先寻找颠末加密的类文件,它可以找到;但由于类文件已经加密,所以它不会承认这个类文件,装入进程将失败。因此,我们必需本身实现loadClass,稍微增加了一些事情量。

二、定制类装入器

每一个运行着的JVM已经拥有一个ClassLoader。这个默认的ClassLoader按照CLASSPATH情况变量的值,在当地文件系统中寻找符合的字节码文件。

应用定制ClassLoader要求对这个进程有较为深入的认识。我们首先必需建设一个定制ClassLoader类的实例,然后显式地要求它装入别的一个类。这就强制JVM把该类以及所有它所需要的类关联到定制的ClassLoader。Listing 1显示了如何用定制ClassLoader装入类文件。

【Listing 1:操作定制的ClassLoader装入类文件】

// 首先建设一个ClassLoader工具
ClassLoader myClassLoader = new myClassLoader();
// 操作定制ClassLoader工具装入类文件
// 并把它转换成Class工具
Class myClass = myClassLoader.loadClass( "mypackage.MyClass" );
// 最后,建设该类的一个实例
Object newInstance = myClass.newInstance();
// 留意,MyClass所需要的所有其他类,都将通过
// 定制的ClassLoader自动装入


#p#副标题#e#

如前所述,定制ClassLoader只需先获取类文件的数据,然后把字节码通报给运行时系统,由后者完成余下的任务。

ClassLoader有几个重要的要领。建设定制的ClassLoader时,我们只需包围个中的一个,即loadClass,提供获取原始类文件数据的代码。这个要领有两个参数:类的名字,以及一个暗示JVM是否要求理会类名字的标志(等于否同时装入有依赖干系的类)。假如这个标志是true,我们只需在返回JVM之前挪用resolveClass。

【Listing 2:ClassLoader.loadClass()的一个简朴实现】

public Class loadClass( String name, boolean resolve )
throws ClassNotFoundException ...{
  try ...{
   // 我们要建设的Class工具
   Class clasz = null;
   // 必须的步调1:假如类已经在系统缓冲之中,
   // 我们不必再次装入它
   clasz = findLoadedClass( name );
   if (clasz != null)
    return clasz;
   // 下面是定制部门
   byte classData[] = /**//* 通过某种要领获取字节码数据 */;
   if (classData != null) ...{
    // 乐成读取字节码数据,此刻把它转换成一个Class工具
    clasz = defineClass( name, classData, 0, classData.length );
   }
   // 必须的步调2:假如上面没有乐成,
   // 我们实验用默认的ClassLoader装入它
   if (clasz == null)
    clasz = findSystemClass( name );
   // 必须的步调3:如有须要,则装入相关的类
   if (resolve && clasz != null)
    resolveClass( clasz );
   // 把类返回给挪用者
   return clasz;
  } catch( IOException ie ) ...{
   throw new ClassNotFoundException( ie.toString() );
  } catch( GeneralSecurityException gse ) ...{
   throw new ClassNotFoundException( gse.toString() );
  }
}

#p#分页标题#e#

Listing 2显示了一个简朴的loadClass实现。代码中的大部门对所有ClassLoader工具来说都一样,但有一小部门(已通过注释标志)是特有的。在处理惩罚进程中,ClassLoader工具要用到其他几个帮助要领:

findLoadedClass:用来举办查抄,以便确认被请求的类当前还不存在。loadClass要领应该首先挪用它。

defineClass:得到原始类文件字节码数据之后,挪用defineClass把它转换成一个Class工具。任何loadClass实现都必需挪用这个要领。

findSystemClass:提供默认ClassLoader的支持。假如用来寻找类的定制要领不能找到指定的类(可能有意地不消定制要领),则可以挪用该要领实验默认的装入方法。这是很有用的,出格是从普通的JAR文件装入尺度Java类时。

resolveClass:当JVM想要装入的不只包罗指定的类,并且还包罗该类引用的所有其他类时,它会把loadClass的resolve参数配置成true。这时,我们必需在返回方才装入的Class工具给挪用者之前挪用resolveClass。

#p#副标题#e#

三、加密、解密

Java加密扩展即Java Cryptography Extension,简称JCE。它是Sun的加密处事软件,包括了加密和密匙生乐成能。JCE是JCA(Java Cryptography Architecture)的一种扩展。

JCE没有划定详细的加密算法,但提供了一个框架,加密算法的详细实现可以作为处事提供者插手。除了JCE框架之外,JCE软件包还包括了SunJCE处事提供者,个中包罗很多有用的加密算法,好比DES(Data Encryption Standard)和Blowfish。

为简朴计,在本文中我们将用DES算法加密息争密字节码。下面是用JCE加密息争密数据必需遵循的根基步调:

步调1:生成一个安详密匙。在加密或解密任何数据之前需要有一个密匙。密匙是伴同被加密的应用一起宣布的一小段数据,Listing 3显示了如何生成一个密匙。 【Listing 3:生成一个密匙】

// DES算法要求有一个可信任的随机数源
SecureRandom sr = new SecureRandom();
// 为我们选择的DES算法生成一个KeyGenerator工具
KeyGenerator kg = KeyGenerator.getInstance( "DES" );
kg.init( sr );
// 生成密匙
SecretKey key = kg.generateKey();
// 获取密匙数据
byte rawKeyData[] = key.getEncoded();
/**//* 接下来就可以用密匙举办加密或解密,可能把它生存
为文件供今后利用 */
doSomething( rawKeyData );

步调2:加密数据。获得密匙之后,接下来就可以用它加密数据。除相识密的ClassLoader之外,一般还要有一个加密待宣布应用的独立措施(见Listing 4)。

【Listing 4:用密匙加密原始数据】

// DES算法要求有一个可信任的随机数源
SecureRandom sr = new SecureRandom();
byte rawKeyData[] = /**//* 用某种要领得到密匙数据 */;
// 从原始密匙数据建设DESKeySpec工具
DESKeySpec dks = new DESKeySpec( rawKeyData );
// 建设一个密匙工场,然后用它把DESKeySpec转换成
// 一个SecretKey工具
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" );
SecretKey key = keyFactory.generateSecret( dks );
// Cipher工具实际完成加密操纵
Cipher cipher = Cipher.getInstance( "DES" );
// 用密匙初始化Cipher工具
cipher.init( Cipher.ENCRYPT_MODE, key, sr );
// 此刻,获取数据并加密
byte data[] = /**//* 用某种要领获取数据 */
// 正式执行加密操纵
byte encryptedData[] = cipher.doFinal( data );
// 进一步处理惩罚加密后的数据
doSomething( encryptedData );

步调3:解密数据。运行颠末加密的应用时,ClassLoader阐明并解密类文件。操纵步调如Listing 5所示。

【Listing 5:用密匙解密数据】

#p#分页标题#e#

// DES算法要求有一个可信任的随机数源
SecureRandom sr = new SecureRandom();
byte rawKeyData[] = /**//* 用某种要领获取原始密匙数据 */;
// 从原始密匙数据建设一个DESKeySpec工具
DESKeySpec dks = new DESKeySpec( rawKeyData );
// 建设一个密匙工场,然后用它把DESKeySpec工具转换成
// 一个SecretKey工具
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" );
SecretKey key = keyFactory.generateSecret( dks );
// Cipher工具实际完成解密操纵
Cipher cipher = Cipher.getInstance( "DES" );
// 用密匙初始化Cipher工具
cipher.init( Cipher.DECRYPT_MODE, key, sr );
// 此刻,获取数据并解密
byte encryptedData[] = /**//* 得到颠末加密的数据 */
// 正式执行解密操纵
byte decryptedData[] = cipher.doFinal( encryptedData );
// 进一步处理惩罚解密后的数据
doSomething( decryptedData );

#p#副标题#e#

四、应用实例

前面先容了如何加密息争密数据。要陈设一个颠末加密的应用,步调如下:

步调1:建设应用。我们的例子包括一个App主类,两个帮助类(别离称为Foo和Bar)。这个应用没有什么实际坚守,但只要我们可以或许加密这个应用,加密其他应用也就不在话下。

步调2:生成一个安详密匙。在呼吁行,操作GenerateKey东西(拜见GenerateKey.java)把密匙写入一个文件: % java GenerateKey key.data

步调3:加密应用。在呼吁行,操作EncryptClasses东西(拜见EncryptClasses.java)加密应用的类: % java EncryptClasses key.data App.class Foo.class Bar.class

该呼吁把每一个.class文件替换成它们各自的加密版本。

步调4:运行颠末加密的应用。用户通过一个DecryptStart措施运行颠末加密的应用。DecryptStart措施如Listing 6所示。

【Listing 6:DecryptStart.java,启动被加密应用的措施】

import java.io.*;
import java.security.*;
import java.lang.reflect.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class DecryptStart extends ClassLoader
...{
  // 这些工具在结构函数中配置,
  // 今后loadClass()要领将操作它们解密类
  private SecretKey key;
  private Cipher cipher;
  // 结构函数:配置解密所需要的工具
  public DecryptStart( SecretKey key ) throws GeneralSecurityException,IOException ...{
   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进程:我们要在这里读入密匙,建设DecryptStart的
  // 实例,它就是我们的定制ClassLoader。
  // 配置好ClassLoader今后,我们用它装入应用实例,
  // 最后,我们通过Java Reflection API挪用应用实例的main要领
  static public void main( String args[] ) throws Exception ...{
   String keyFilename = args[0];
   String appName = args[1];
   // 这些是通报给应用自己的参数
   String realArgs[] = new String[args.length-2];
   System.arraycopy( args, 2, realArgs, 0, args.length-2 );
   // 读取密匙
   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 );
   // 建设解密的ClassLoader
   DecryptStart dr = new DecryptStart( key );
   // 建设应用主类的一个实例
   // 通过ClassLoader装入它
   System.err.println( "[DecryptStart: loading "+appName+"]" );
   Class clasz = dr.loadClass( appName );
   // 最后,通过Reflection API挪用应用实例
   // 的main()要领
   // 获取一个对main()的引用
   String proto[] = new String[1];
   Class mainArgs[] = ...{ (new String[1]).getClass() };
   Method main = clasz.getMethod( "main", mainArgs );
   // 建设一个包括main()要领参数的数组
   Object argsArray[] = ...{ realArgs };
   System.err.println( "[DecryptStart: running "+appName+".main()]" );
   // 挪用main()
   main.invoke( null, argsArray );
  }
  public Class loadClass( String name, boolean resolve )
  throws ClassNotFoundException ...{
   try ...{
    // 我们要建设的Class工具
    Class clasz = null;
    // 必须的步调1:假如类已经在系统缓冲之中
    // 我们不必再次装入它
    clasz = findLoadedClass( name );
    if (clasz != null)
     return clasz;
    // 下面是定制部门
    try ...{
     // 读取颠末加密的类文件
     byte classData[] = Util.readFile( name+".class" );
     if (classData != null) ...{
      // 解密...
      byte decryptedClassData[] = cipher.doFinal( classData );
      // ... 再把它转换成一个类
      clasz = defineClass( name, decryptedClassData,0, decryptedClassData.length );
      System.err.println( "[DecryptStart: decrypting class "+name+"]" );
     }
    } catch( FileNotFoundException fnfe ) ...{}
    // 必须的步调2:假如上面没有乐成
    // 我们实验用默认的ClassLoader装入它
    if (clasz == null)
     clasz = findSystemClass( name );
    // 必须的步调3:如有须要,则装入相关的类
    if (resolve && clasz != null)
     resolveClass( clasz );
    // 把类返回给挪用者
    return clasz;
   } catch( IOException ie ) ...{
    throw new ClassNotFoundException( ie.toString()
   );
  } catch( GeneralSecurityException gse ) ...{
   throw new ClassNotFoundException( gse.toString()
  );
}
}
}

对付未经加密的应用,正常执行方法如下: % java App arg0 arg1 arg2

对付颠末加密的应用,则相应的运行方法为: % java DecryptStart key.data App arg0 arg1 arg2

#p#分页标题#e#

DecryptStart有两个目标。一个DecryptStart的实例就是一个实施即时解密操纵的定制ClassLoader;同时,DecryptStart还包括一个main进程,它建设解密器实例并用它装入和运行应用。示例应用App的代码包括在App.java、Foo.java和Bar.java内。Util.java是一个文件I/O东西,本文示例多处用到了它。完整的代码请从本文最后下载。

五、留意事项

我们看到,要在不修改源代码的环境下加密一个Java应用是很容易的。不外,世上没有完全安详的系统。本文的加密方法提供了必然水平的源代码掩护,但对某些进攻来说它是懦弱的。 固然应用自己颠末尾加密,但启动措施DecryptStart没有加密。进攻者可以反编译启动措施并修改它,把解密后的类文件生存到磁盘。低落这种风险的步伐之一是对启动措施举办高质量的恍惚处理惩罚。可能,启动措施也可以回收直接编译成呆板语言的代码,使得启动措施具有传统执行文件名目标安详性。

别的还要记着的是,大大都JVM自己并不安详。调皮的黑客大概会修改JVM,从ClassLoader之外获取解密后的代码并生存到磁盘,从而绕过本文的加密技能。Java没有为此提供真正有效的调停法子。

不外应该指出的是,所有这些大概的进攻都有一个前提,这就是进攻者可以获得密匙。假如没有密匙,应用的安详性就完全取决于加密算法的安详性。固然这种掩护代码的要领称不上浑然一体,但它仍不失为一种掩护常识产权和敏感用途的代码一种要领。

 

    关键字:

天才代写-代写联系方式