Java基础知识总结(五)——安全

本文详细介绍了Java语言如何从语言特性、安全管理器及签名加密等方面确保应用程序的安全性。具体包括类加载过程中的验证机制、保护域模型下的权限控制、数字签名与加密算法的应用实践等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

小结:

系统的整理了下有关的Java的安全的三个方面:语言特性,安全管理器(保护域),签名和加密算法的应用。在整理时,我觉得基于第三方的数字签名认证的方式充分体现了解耦的思想,这和集群中中心式拓扑结构的方式很像,将复杂耦合几种在中介上(中介者模式和迪米特法则),设计模式这个东西只要仔细理解,灵活运用才可以举一反三。

1. 概括

Java语言的安全由三个方面共同进行保证:

(1)语言特性:

类,域,方法的访问控制;
运行时的检查:指针操作的封装,数组的越界检查,类型转换的检查;
编译时检查:字节码加载,比如语义分析时会对变量是否初始化,运算符两边的类型是否匹配等等;
类加载:双亲委托加载模型/线程上下文类加载器,验证(文件格式验证,元数据验证,字节码验证(是否初始化,类型是否匹配等),符号验证(访问权限));

类加载器的安全性在于
(1)双亲委托的模型中核心类,扩展类,应用类有各自层次的类加载器;
(2)Java中每个类型有全限定名和类加载实例两者决定,每个类加载器实例提供一个类型的命名空间;
(3)类加载器会检查不能由非BootStrap类加载器加载JDK核心类;
(4)验证环节;

(2)访问控制:安全管理器

(3)代码签名,加密算法验证创建者;

2. 安全管理器

Java平台安全性的演进:
(1)JDK1.0:本地代码具有所有的权限,远程代码在沙盒中运行,只能打印到屏幕和与用户交互;
(2)JDK1.1:远程代码带有可信赖的签名,具有与本地代码资源的所有权限;
(3)JDK2.0:基于保护域的安全体系,保护域包括代码来源权限集合的映射,SecurityManager检查调用堆栈上所有方法的类的保护域,其权限集合是否允许执行当前的操作,如果所有域都通过检查则通过,否则抛出SecurityException异常;

2.1 重要的类和方法(体系结构):

(1)SecurityManager:checkPermission等验证方法,抛出SecurityException;
(2)java.Security.ProtectionDomain:保护域,包含代码来源(CodeSource)和权限集合(PermissionCollection);
(3)Class:getProtectionDomain获取该类的保护域;
(4)java.Security.CodeSource:代码来源,包含代码位置和相关联的证书链(用于类文件签名);
(5)Permission:包含implies方法(条件间的依赖关系),通过name属性是关键,equals和hashcode主要由它决定,和Principal一样可以自定义;

安全策略文件:
位置:
(1)Java平台主目录(java.policy文件);
(2)用户主目录(.java.policy);
(3)通过jvm参数指定,
(4)System.setProperty(“java.security.plicy”,)设置;

2.2 用户认证(JAAS API规范):

(1)通过LoginContext登录,可以传入CallbackHandler(handler中可以携带一些登录信息),使用在config文件中定义的LoginModule(也可以自定义)将callbackHandler中的登录信息传递到回调对象,从而获得登录信息;
(2)根据用户信息库(可以是目录文件,数据库等等)。通过验证后可以获得一个Subject;
(3)LoginModule会给这个用Subject添加一些Principal(特征),通过Subject.doAsPrivileged(一个新的上下文,与登录环境权限分离)中执行受检查的action;
(4)如果policy中subject带有的principal有对应权限的话这可以执行,否则抛出AccessControlException;

LoginModule是检查登录权限的关键,向它传入CallbackHandler(登录信息),Subject(认证对象),options(config中对应的键值对信息),sharedState(登录模块间通信);

3. 数字签名

3.1 消息摘要

消息摘要(message digest):是数据块的数字指纹。
基本属性:
(1)如果数据的1位或几位改变了,那么消息摘要也将要改变。
(2)拥有给定消息的伪造者不能创建与原消息具有相同的假消息;

Java中的MessageDigest和Charset的体系很像,作为消息摘要算法子类的超类的同时也是工厂类;

消息摘要意义在于检查文件(消息)是否被修改过,这就要求消息摘要和消息分开传递,否则一同截获再被修改(很容易)就无法起作用了。比如,保存文件是常常可以通过MD5验证的方式检查文件是否是同一文件。

3.2 消息签名

由于消息摘要的局限性,在需要认证用户身份的时候就需要数字签名同时能够校验消息有无被修改,中途被篡改。

数字签名算法(DSA);

重要概念:

(1)公共密钥:可以公开,数字签名中,对方通过公共密钥校验消息的来源可信性(原始信息未修改,验证对方身份);在非对称加密算法中,可以用来加密给对方的消息;
(2)私有密钥:非公开,在数字签名中,可以用生成签名;在非对加密中,可以通过私有密钥解密对方发来的加密消息;
(3)公共密钥和私有密钥的设计在于通过一个密钥推算出另一个密钥是实际不可性的,在数字签名中,私有密钥相当于个人的凭证。

数字签名原理:

(1)发送方,生成自己的密钥库,包含私有密钥和公有密钥,将公有密钥包含导出成证书将这个证书发送给接受方;
(2)接收方,验证证书的真实性,接受到带有签名的消息后,利用证书验证是否为发送方的签名消息有无被修改

基于第三方认证机构的认证

我觉得这种模式类似于中介者设计模式/迪米特法则,类似的还有集群架构中中心式的拓扑结构,将多对多的联系变成一对多,复杂性集中在中介者身上,将认证的步骤分离出来。
(1)第三方将自己的根证书(公有密钥)导出,可以利用pem格式(隐私增强型邮件格式);
(2)用户将根证书导入密钥库中,这时用户的密钥库能够信任这个第三方平台发来的信息;
(3)将发送方的证书同样导出成pem格式,基于openssl和第三方的CA对该证书签名生成pem,这是第三方平台对发送方的认证;
(4)将这个带有第三方签名的证书由接收方导入密钥库(因为已有了第三方的证书),最后接收方就拥有了发送方的证书;

keytool,jarsign:JDK数字证书,签名生成工具

生成密钥库:keytool -genkeypair -keystore xxx.certs -alias xxx;
导出证书:keytool -exportcert -keystore xxx.certs -alias acmeroot -file acmeroot.cer;
导入证书到密钥库:keytool -importcert -keystore xxx.certs -alias yyy -file yyy.cer(yyy发送方别名);
打印证书:keytool -printcert -file xxx.cer;
列出密钥库证书:keytool -list -v -keystore xxx

使用证书签名jar:jarsigner -keystore xxx.certs document.jar xxx
验证jar签名:jarsigner -verify -keystore xxx.certs document.jar

4. 加密:

消息签名可以认证代码的来源,并且校验是否被修改,但是消息本身是可见的,对于重要的信息需要加密。

Cipher类:与Charset和MessageDigest一样,超类+工厂类。

4.1 加密算法:

对称密码:DES(穷举法可破解),AES等;
非对称密码:RSA等

非对称加密算法的应用方式

公有密钥算法比对称密钥算法慢的多,因此不能用来加密大量的信息。使用对称密钥算法加密正文,公有密钥加密对称密钥是一种可行的方式。
(1)发送方生成对称密钥,对明文进行加密,并使用接收方的公有密钥对对称密钥进行加密;
(2)将加密后的明文和对称密钥一同发送给接收方;
(3)接收方使用自己的私有密钥将对称密钥解密,再用对称密钥解密得到明文;

使用公有密钥算法的方式和下面对称加密算法的方式类似:主要要使用WARP_MODE和UNWARP_MODE对对称加密的key进行加解密。

生成密钥

生成密钥可以用SecureRandom或者随机序列(随机敲键盘)的得到随机种子。密钥可以用序列化的方式输出到磁盘等地方保存。
方式一:

KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
//"真正的随机数",加密的随机数,需要额外性能
//Random以日期和时间为种子并不安全
SecureRandom secureRandom = new SecureRandom();
keyGenerator.init(secureRandom);
Key key = keyGenerator.generateKey();

方式二:

byte[] seq = "sdlkfjiuqoenrgiuerds,msfd".getBytes();
SecretKey key = new SecretKeySpec(seq, "AES");
加密

选择加密算法:

 Cipher aes = Cipher.getInstance("AES");
 /* 
     模式有4种:
     ENCRYPT_MODE
     DECRYPT_MODE
     UNWARP_MODE
     WARP_MODE
 */
 int mode = Cipher.ENCRYPT_MODE;
 aes.init(mode, key);

将输入流中的数据加密写入输出流。注意如果数据不能以blockSize对齐,应该使用doFinal写入深入部分,该方法可以补齐。

public static void crypt(InputStream in, OutputStream out, Cipher cipher) throws IOException, GeneralSecurityException {
        int blockSize = cipher.getBlockSize();
        int outputSize = cipher.getOutputSize(blockSize);
        byte[] inBytes = new byte[blockSize];
        byte[] outBytes = new byte[outputSize];

        boolean more = true;
        int inLen = 0;
        while(more) {
            inLen = in.read(inBytes);
            if(inLen == blockSize) {
                int outputLen = cipher.update(inBytes, 0, blockSize, outBytes);
                out.write(outBytes, 0, outputLen);
            } else {
                more = false;
            }
        }
        if(inLen > 0) outBytes = cipher.doFinal(inBytes, 0, inLen);
        else outBytes = cipher.doFinal();
        out.write(outBytes);
        out.flush();
    }
密码流

使用CiperInputStream和CiperOutputStream可以屏蔽updatedoFinal等细节;


附:policy,config语法小记:

grant:codeBase,principal,signedBy(加alias);
Login1 {LoginModule}:登录上下文定义;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值