1 安全
安全性相关内容分为认证、授权和审计(发现安全问题时可以查看相关历史记录)
用户认证
java API表示主体的是javax.security.auth.Subject类型,表示用户身份标识的是java.security.Principal接口,一个主体可以与多个身份标识关联,
主体所关联的凭证信息分成公开(公钥等)和私有(密码和私钥等)两类;对于身份标识和凭证,Subject类都是使用java.util.Set接口来管理
登陆相关的API再javax.security.auth.login包中,整个登陆过程由javax.security.auth.login.LoginContext类的对象来负责启动和管理,每个LoginContext类的对象只用来对一个Subject类的对象进行认证以及认证通过后的管理;
但是登陆的实现是由javax.security.auth.api.LoginModule接口的实现类来进行的
登陆过程的第一个阶段调用的是LoginModule接口的login方法,若认证成功,第一个阶段LoginModle的commit方法会被调用,用来把身份标识和凭证关联到主体,
很多情况下,LoginModule接口的实现中都需要通过与用户或其他组件进行交互来获取认证所需的相关信息,这种交互式同股沟CallbackHandler接口及其关联的Callback接口来完成的
权限控制
访问控制权限的检查由java.lang.SecurityManager和java.security.AccessController来共同完成
特权动作
在一个方法调用的执行过程中,实际的执行流程通常会跨越多个保护域的边界,需要进行权限检查,不仅会查看该方法的调用在当前调用上下文是否合法,还需要沿着方法调用栈逐个向上检查,
如果方法的调用执行的是特权动作,就不设这个限制,在进行权限监察室,会根据当前调用上下文的即时状态来进行判断 ,特权动作之关心是否具备动作本身要求的权限,而不关系调用者是谁。
特权动作由java.security.PrivilegedAction接口和java.security.PrivilegedExceptionAction接口的实现类表示,run方法中执行
2 加解密
对称加密算法使用同一个密钥;非对称加密使用一对密钥进行加解密,两个密钥分别是公钥和私钥,公钥是公开的,私钥则由程序保存,需要与程序进行通信的其他代码使用公钥对数据加密,程序则使用私钥进行解密
密钥在java密码框架中有两种表示方式,一种是基于java.security.Key接口的不透明表示方式,另一种是基于java.security.spec.KeySpec接口的透明表示方式
对于对称说法来说,javax.crypto.SecretKey接口表示唯一的私钥,对于非对称算法来说,java.security.PublicKey接口和java.security.PrivateKey接口分别表示公钥和私钥
密钥的获取通过标准的服务提供者来完成,对称算法使用javax.crypto.KeyGenerator类,而非对称算法使用java.security.KeyPairGenerator类,这两个类通过内部静态方法getInstance实例化
***加密解密***
加密解密功能由服务javax.crypto.Cipher类提供,Cipher类的初始化也是通过getInstance方法来完成,getInstance时需要指定加密时的转换方式(算法名称、反馈模式、填充方式),接着调用init方法通过合适的密钥
把Cipher类对象初始化成所需的加密或解密模式;加解密数据时,可以update..update..doFinal或者直接doFinal(数据较少时)
e1.
public void encrypt(){
KeyGenerator generator=KeyGenerator.getInstance("DES");
SecretKey key=generator.generateKey();
Files.write(Paths.get("key.data"),key.getEncoded()); //保留密钥编码之后的格式
Cipher cipher=Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE,key);
String text="Hello World";
byte[] encrypted=cipher.doFinal(text.getBytes());
Files.write(Paths.get("encrypted.bin"),encrypted);
}
public void decrypt(){
byte[] keyData=Files.readAllBytes(Paths.get("key.data"));
SecretKeySpec keySpec= new SecretKeySpec(keyData,"DES");
Cipher cipher=Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE,keySpec);
byte[] data=Files.readAllBytes(Paths.get("encrypted.bin"));
byte[] result=cipher.doFinal(data);
System.out.println(new String(result));
}
要对一个数据流进行加解密,可以使用javax.crypto.CipherInputStream和javax.crypto.CipherOutputStream这两个过滤流实现
eg try(ObjectOutputStream oos=new ObjectOutputStream(new CipherOutputStream(Files.newOutputStream(path),cipher))){
oos.writeObject(obj);
}
要对Java对象进行加解密,可以使用javax.crypto.SealedObject类
***报文摘要***
使用报文摘要的目的是防止数据的内容在传输时被第三方拦截并篡改,做法是使用某种算法对原始数据进行处理,得到一个固定长度摘要,接收者使用同样的算法得到摘要,再与正确摘要比对
进行报文摘要的MessageDigest类的使用方式类似于Cipher类,update..update..digest
eg MessageDigest md=MessageDigest.getInstance("MD5");
byte[] digest=md.digest("HELLO".getBytes());
***消息验证码***
与报文摘要类似的机制是消息验证码(MAC),消息验证码相对于报文摘要在计算中使用了密钥,可以解决摘要本身被篡改的问题,在实现中使用散列函数的消息验证码成文HMAC,
消息验证码使用javax.crypto.Mac类进行计算
eg KeyGenerator generator=KeyGenerator.getInstance("HmacMD5");
SecretKey key=generator.generateKey();
Mac mac=Mac.getInstance("HmacMD5");
mac.init(key);
byte[] result=mac.doFinal("Hello".getBytes());
***数字签名***
数字签名可以用来实现身份验证功能,在进行签名时使用一对公钥和私钥,若A需要验证B的身份,那么B需要使用私钥对消息加密并发送给A,A使用公钥解密,由于私钥只有B知道,所以相当于B对消息进行了签名
数字签名服务由java.security.Signature类来提供,Signature类的对象有签名和验证两种不同的工作模式(签名 initSign-->update ,验证 initVerify-->update..verify),KeyPairGenerator类的对象生成分别用于
签名和验证操作的公钥和私钥
eg
private void init(){
signature=Signature.getInstance("SHA1withDSA");
KeyPairGenerator keyGenerator=KeyPairGenerator.getInstance("DSA");
KeyPair keyPair=keyGenerator.generateKeyPair();
publicKey=keyPair.getPublic();
privateKey=keyPair.getPrivate();
}
public byte[] sign(){
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
public boolean verify(byte[] signatureData){
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(signatureData);
}
当对某个Java对象进行进行签名时,可以使用java.security.SignedObject类,SignedObject类的对象可以用来封装任何实现了Serializable接口的类的对象,SignedObject类的对象是不可变的,
同时它所封装的是基于序列化机制实现的对象的深拷贝,SignedObject类的对象一旦创建后对原始对象的修改不会影响到它
SignedObject signedObj=new SignedObject(obj,keyPair.getPrivate(),signature);
安全套接字
在设计网络传输过程中,可以使用安全套接字层(Secure Sockets Layer,SSL)协议,SSL协议在TCP/IP协议栈中位于传输层协议(TCP)和应用层协议(HTTP、Telnet和FTP)之间,
标准化后的SSL协议改名为传输层安全协议(Transport Layer Security,TLS)
SSL协议目的在于解决网络传输中3个安全问题,
1身份认证,协议允许通信双方在建立数据连接前先进行身份认证,在身份认证中使用经过数字签名的证书
2数据被窃取 SSL会在传输过程中的加密实现
3数据被篡改 SSL协议对传输的数据添加了消息验证码,接受者可以对数据的完整性进行校验
浏览器会把一些重要的信任机构的公钥保存起来,对于服务端传来的私钥加密的证书进行解密验证从而建邺证书本身的合法性
在SSL握手过程中,数据传输使用的是对称加密,客户端把生产的密钥经过服务端的公钥加密后,发送给服务器端,服务器端用自己的私钥解密,就得到了双方共同使用的密钥
HTTPS是一种把HTTP和SSL/TLS协议结合起来的通信方式,通过http传输的数据都会被加密
java类在读取https链接内容时会忽略所有与证书和主机名称验证相关的错误,总是能够读出内容