利用数字签名超越Java Applet的安全限制(转)

  Java 技术之所以在今天得到了如此广阔的应用,其中它的安全性是不能不提的。不同于其它技术(例如 Microsoft ActiveX )中安全性作为附加设计和补丁, Java 从设计之初便考虑到了安全性。因此 Java 的安全性是在语言层次实现的。 Java 的安全性由下列三个方面保证:

   1 语言特性(包括数组的边界检查、类型转换、取消指针型变量)。

   2 资源访问控制(包括本地文件系统访问、 Socket 连接访问)。

   3 代码数字签名(通过数字签名来确认代码源以及代码是否完整)。

  本文主要讨论结合后两种技术来实现超越 Applet 的安全限制。我们先来看一下这三个方面的具体实现。我们知道 Java 的原代码是先编译成为一种字节码的中间代码,存放这种代码的文件就是 .class 的文件。真正执行的时候是将 class 文件装载到 JVM (虚拟机)中,然后由 JVM 解释执行的。所以数组的上下界检查及合法的类型转换是通过 JVM 得到保证的。

   Java 通过一个类装载器类 (ClassLoader) 将虚拟机代码文件(即 class 文件)装载到 JVM 中,当完成装载后,一个被称做安全管理器( SecurityManager )的类开始运行,这就是上面描述的第二个方面的实现。例如当一个 Applet class 文件被缺省的类装载器装载到 JVM 中后, JVM 会立即为它装载一个 SecurityManager 的子类 AppletSecurity ,由这个管理器来验证操作。代码的所有动作(例如文件读写)都要先经过验证,只有被该安全管理器接受的动作才能完成,否则就会抛出 SecurityException 异常。那么安全管理器类是怎么判断代码的权限的呢?这就是利用 Policy 文件。

  对于 JDK1.0 ,权限被笼统的划分为两大块。一是拥有所有的权限,一个是仅拥有 " 沙箱 " sandBox )权限,这也是普通的 Applet 所拥有的权限。这时本地文件读写或是与源主机( Orignal Server )以外的主机连接都是被禁止的。这种划分的最大问题就是缺乏灵活性。例如我们希望一个 Applet 在用户信任的情况下能够对本地文件系统的某个目录进行读写,但并不要通过 Socket 与其它主机连接。这是 JDK1.0 的权限划分就不能达到要求。 JDK1.1 后改进了权限的划分,引入了权限集( PermissionSet )的概念。它细划了权限的放放面面,你可以有选择性的组合你需要的权限来达到特殊的要求。下图显示了这种划分:



1

 

 

 

 

图一中的BasicPermission还可以进一步细分为更多的细节权限,例如:AWTPermissionRuntimePermission等等。Java通过一个后缀名为.policy的文件来组合这些权限。安装完JRE(Java Runtime Environment)后有两个缺省的权限文件,它们是:

 

 

 

${java.home}/lib/security/java.policy
${user.home}/.java.policy

 

 

 

 

 


  下面是一个java.policy文件的实例:

 

 

 

// Standard extensions get all permissions by default

grant codeBase "file:${java.home}/lib/ext/*" {
permission java.security.AllPermission;
};

// default permissions granted to all domains

grant {
// Allows any thread to stop itself using the java.lang.Thread.stop()
// method that takes no argument.
// Note that this permission is granted by default only to remain
// backwards compatible.
// It is strongly recommended that you either remove this permission
// from this policy file or further restrict it to code sources
// that you specify, because Thread.stop() is potentially unsafe.
// See "http://java.sun.com/notes" for more information.
permission java.lang.RuntimePermission "stopThread";

// allows anyone to listen on un-privileged ports
permission java.net.SocketPermission "localhost:1024-", "listen";

// "standard" properies that can be read by anyone

permission java.util.PropertyPermission "java.version", "read";
permission java.util.PropertyPermission "java.vendor", "read";
permission java.util.PropertyPermission "java.vendor.url", "read";
permission java.util.PropertyPermission "java.class.version", "read";
permission java.util.PropertyPermission "os.name", "read";
permission java.util.PropertyPermission "os.version", "read";
permission java.util.PropertyPermission "os.arch", "read";
permission java.util.PropertyPermission "file.separator", "read";
permission java.util.PropertyPermission "path.separator", "read";
permission java.util.PropertyPermission "line.separator", "read";

permission java.util.PropertyPermission "java.specification.version", "read";
permission java.util.PropertyPermission "java.specification.vendor", "read";
permission java.util.PropertyPermission "java.specification.name", "read";

permission java.util.PropertyPermission "java.vm.specification.version", "read";
permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
permission java.util.PropertyPermission "java.vm.specification.name", "read";
permission java.util.PropertyPermission "java.vm.version", "read";
permission java.util.PropertyPermission "java.vm.vendor", "read";
permission java.util.PropertyPermission "java.vm.name", "read";
};

 

 

 

 

 


  由上面的这个文件可以看出,所有policy文件的写法都是类似下面的格式:

 

 

 

grant codesource
{
 permission_1;
 permission_2;
}

 

 

 

 

 


  其中permission_1代表一个具体的权限描述,这里我们只来看看关于文件权限的描述,其它的权限描述是类似的。

 

 

 

写法

 

 

 

 

 

代表含义

 

 

 

 

 

file

 

 

 

 

 

一个指定的文件

 

 

 

 

 

directory/

 

 

 

 

 

一个指定的目录

 

 

 

 

 

dirctory/*

 

 

 

 

 

一个目录下的所有文件

 

 

 

 

 

dirctory/-

 

 

 

 

 

指定目录及子目录的所有文件

 

 

 

 

 

-

 

 

 

 

 

当前目录及子目录的所有文件

 

 

 

 

 

*

 

 

 

 

 

当前目录的所有文件

 

 

 

 

 

<<ALL FILES>>

 

 

 

 

 

整个文件系统

 

 

 

 

 

                       表1

  下面是几个具体的例子:

 

 

 

grant
{
permission java.io.FilePermission "
<<ALL FILES>>" "read,write";
};

grant
{
permission java.io.FilePermission "
<<${user.home/-} >>" "read,write,delete";
};

 

 

 

 

 


  光有policy文件还是不够,客户无法判断现在执行的代码是否是由你发布的,也不能保证这个代码在传输的过程中有没有被人给恶意的破坏。所以还需要数字签名技术来保证这两方面。JDK中给我们提供了几个工具来完成这些工作。

  结合这几种技术就可以达到本文的目的了,下面就是本文的目标代码,它是一个可以读取本地文件系统的Applet

代码1

 

 

 

/-------------------------------------
package jcomponent;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.io.*;

public class FileReaderApplet extends Applet {
boolean isStandalone = false;
TextField fileNameField;
TextArea fileArea;

file://Get a parameter value
public String getParameter(String key, String def) {
 return isStandalone ? System.getProperty(key, def) :
 (getParameter(key) != null ? getParameter(key) : def);
}

file://Construct the applet
public FileReaderApplet() {
}

file://Initialize the applet
public void init() {
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}

file://Component initialization
private void jbInit() throws Exception {
this.setSize(new Dimension(400,300));
this.setLayout(new BorderLayout());
Panel panel=new Panel();
Label label=new Label("File Name");
panel.add(label);
fileNameField=new TextField(25);
panel.add(fileNameField);
Button b=new Button("Open File");
b.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
loadFile(fileNameField.getText());
}
});
panel.add(b);
this.add(panel,BorderLayout.NORTH);
fileArea=new TextArea();
this.add(fileArea,BorderLayout.CENTER);
}

public void loadFile(String fileName){
try{
BufferedReader reader=new BufferedReader(new FileReader(fileName));
String context=new String();
while((context=reader.readLine())!=null){
fileArea.append(context+"/n");
}
reader.close();
}catch(IOException ie){
fileArea.append(ie.getMessage());
}catch(SecurityException se){
fileArea.append("because of security constraint ,it can not do that!");
}
}

file://Get Applet information
public String getAppletInfo() {
return "This is an applet can read and write the local file system";
}

}

 

 

 

 

 


  如果你将这个代码嵌入网页中并执行它,当你试图打开一个本地文件时就会发生SecurityException。大家跟着我进行下面的步骤就可以最终拥有读写文件的权限。在此之前你需要有以下的工具:JDK1.1以上、JREHTMLConvert。这些工具在SUNJava站点上都有,而且也是免费的。将它们分别安装好,我们将所有涉及的文件都放在c:/admin中。

  步骤一:(打包class文件)

  在命令行中执行以下的语句:jar -cvf MyApplet.jar class

  注意这里的所有.class文件均是放在一个class的目录中的。本步骤执行完毕后,将在c:/admin中产生一个名为MyApplet.jar的文件。

 

 

 

步骤二:(在网页中嵌入Applet

  这个网页的名字叫做FileReaderApplet.html,下面是嵌入Applet部分的写法:

 

 

 

APPLET
CODEBASE = "."
CODE = "jcomponent.FileReaderApplet.class"
ARCHIVE ="MyClass.jar"
NAME = "TestApplet"
WIDTH = 400
HEIGHT = 300
HSPACE = 0
VSPACE = 0
ALIGN = middle

/APPLET

 

 

 

 

 



  完成这个步骤后,这个Applet已经可以显示了。但是还不能读写本地的文件系统。

  步骤三:(生成证书及签名)

  请在命令行环境下执行以下的命令:

   1keytool -genkey -keystore pepper.store -alias pepper

  这个命令用来产生一个密匙库,执行完毕后应该在c:/admin中产生一个pepper.store的文件,这里的pepper是我自己的名字,你可以对它进行修改。另外在执行命令的时候还有提示你输入密匙库的密码,这里你一定要记住,否则后面要用的时候无法输入。

  2keytool -export -keystore pepper.store -alias pepper -file pepper.cert

  这个命令用来产生签名时所要用的证书,同样这里的pepper也可以换成你自己需要的名字。这个命令执行完后在c:/admin中产生一个pepper.cert的文件。

  4 jarsigner -keystore pepper.store MyApplet.jar pepper

  这个命令用上面产生的证书将我们的jar文件进行了签名。

  步骤四:(修改文件)

  1 c:/admin中产生一个名为applet.policy的文件,其内容如下:

 

 

 

keystore "file:c: /admin/pepper.store", "JKS";

grant signedBy "pepper"
{ permission java.io.FilePermission "
<<ALL FILES>>", "read";
};

 

 

 

 

 


  这个文件让由pepper签名的Applet拥有本地所有文件的读权限。

  2 修改${java.home}/jre/lib/security目录下的java.security,找到下面这两行:

 

 

 

policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy

 

 

 

 

 


  在下面添写第三行

 

 

 

policy.url.3=file:c: /admin/applet.policy

 

 

 

 

 


  完成这个修改后我们在前面创建的applet.policy文件才有效。

  步骤五:(转换html文件)

  运行前面提到的HTMLConvert工具,将原有的FileReaderApplet.html转化成下面的形式:

 

 

 

!--"CONVERTED_APPLET"--
!-- CONVERTER VERSION 1.3 --
OBJECT classid="clsid:8AD 9C840-044E-11D1-B3E9 -00805F499D93"
WIDTH = 400 HEIGHT = 300 NAME = "TestApplet" ALIGN = middle VSPACE = 0 HSPACE = 0 codebase="http://java.sun.com/products/plugin/1.3/jinstall-13-win32.cab#Version=1,3,0,0"

PARAM NAME = CODE VALUE = "jcomponent.FileReaderApplet.class"
PARAM NAME = CODEBASE VALUE = "."
PARAM NAME = ARCHIVE VALUE = "MyApplet.jar"
PARAM NAME = NAME VALUE = "TestApplet"

PARAM NAME="type" VALUE="application/x-java-applet;version=1.3"
PARAM NAME="scriptable" VALUE="false"
COMMENT
EMBED type="application/x-java-applet;version=1.3" CODE = "jcomponent.FileReaderApplet.class" CODEBASE = "." ARCHIVE = "MyApplet.jar" NAME = "TestApplet" WIDTH = 400 HEIGHT = 300 ALIGN = middle VSPACE = 0 HSPACE = 0 scriptable=false pluginspage="http://java.sun.com/products/plugin/1.3/plugin-install.html"><NOEMBED></COMMENT

/NOEMBED></EMBED
/OBJECT

!--
APPLET CODE = "jcomponent.FileReaderApplet.class" CODEBASE = "." ARCHIVE = "MyApplet.jar" WIDTH = 400 HEIGHT = 300 NAME = "TestApplet" ALIGN = middle VSPACE = 0 HSPACE = 0


/APPLET
--

!--"END_CONVERTED_APPLET"--

 

 

 

 

 



  大家不要看到这里的写法很复杂,但是这些都是由HTMLConvert工具自动实现的。这个工具有命令行和图形界面两种运行方式。

  好了,现在这个Applet可以运行读写文件的功能了。如果你要考虑在Internet上实现这个Applet,那么你也不需要在所有的客户端均做上面的步骤,你只需要在你的服务器上创建一个目录,例如c:/admin,将这个目录映射为www.testApplet.com/admin。这里的www.testApplet.com是一个假定的网址,将pepper.certpepper.storeFileReaderApplet.htmlMyApplet.jar以及applet.policy放在这个目录中,然后修改applet.policy文件如下:

 

 

 

keystore "http:// www.testApplet.com/admin/pepper.store", "JKS";

grant signedBy "pepper"
{ permission java.io.FilePermission "
<<ALL FILES>>", "read";
};

 

 

 

 

 


  3 而每个客户端仅仅需要修改一下它们的${java.home}/jre/lib/security目录下的java.security文件如下:

 

 

 

policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy
policy.url.3= http:// www.testApplet.com/admin/applet.policy

 

 

 

 

 


  当然每个客户端还是需要安装JRE的,不过现在的浏览器安装时都已经自动安装了。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值