Java学习---传输安全设计
1.计算机安全的概念
用于保护数据和阻止Hacker的工具统称为计算机安全(Computer Security)。信息安全最基本的方法就是利用加密信息防止未授权的人窃听,加密是以某种特殊的算法改变原有的信息数据,使得未授权的用户即使获得了已加密的信息,但因不知解密的方法,仍然无法了解信息的内容。
简单的加密算法有:
l 取代:将明文中每个元素都映射成为另一元素,如下图所示。
图 取代加密算法
l 置换:重新排列明文中每个元素,如下图6-2所示。
图 置换加密算法
更安全的加密算法,对称加密和公钥加密,例如,对称加/解密算法的代表DES和公钥加/解密算法的代表RAS等。
2.Java中相关的内容
Java中体系结构提供了三类主要的安全解决方案分别是:
l Java 认证和授权服务(Java Authentication and Authorization Service,JAAS),提供了灵活和可伸缩的机制来保证客户端或服务器端的Java程序。JAAS已经整合进了Java 2 SDK 1.4,作为标准的用户认证与授权模型。
l Java 安全套接字扩展(Java Secure Socket Extension,JSSE),JSSE是基于安全算法和握手机制之上的合成体。SSL(Secure Sockets Layer)是JSSE中的重要的部分。SSL是用的最广泛的实现网络的加密协议。SSL用一个密码处理来提供网络安全通信。SSL是基于标准的TCP/IP socket协议的安全增加用于网络通信,所以SSL是位于传输层和应用程序层之间。最普通的使用SSL的是HTTP(Hypertext Transfer Protocol),用于网络页面。
l Java 加密扩展(Java Cryptography Extension,JCE),它们提供用于加密、密钥生成和协商以及 Message Authentication Code(MAC)算法的框架和实现。它提供对对称、不对称、块和流密码的加密支持,它还支持安全流和密封的对象。
Java加密扩展包JCE提供的是基于密钥的加密,它通过javax.crypto.Cipher类来实现数据加密合解密,加密解密的对象可以使程序中的数组对象,也可以是通过Java流接口读出或者写入的数据。使用Cipher类加密可以选择多种加密算法、加密模式和填补机制。加密算法JCE中提供了DES、多重DES、PBEWithMD5AndDES、RSA和Blowfish等多种加/解密算法。
Java 中为安全框架提供类和接口,包含在类库包java.security和子类中,该 API 设计用于帮助开发人员在程序中同时使用低级和高级安全功能,该类说明如图所示。
图 java.security类说明
该类中有一个重要的方法doFinal()用于执行加密和解密操作:
l Byte[ ] doFinal() 结束多部分加密或解密操作,返回字节数组;
l Byte[ ] doFinal(byte[] input) 按单部分操作加密或解密数据,或者结束一个多部分操作,返回字节数组。
3.数字签名
在通信安全中存在两种方法:数据加密,保证数据的机密性,防止窃听;数据签名,保证数据的认证和完整性,防止伪装、修改、反拒认。
数字签名(Digital Signature)就是附加在数据单元上的一些数据,或是对数据单元所作的密码变换。这种数据或变换允许数据单元的接收者用以确认数据单元的来源和数据单元的完整性并保护数据,防止被人(例如接收者)进行伪造。其主要的功能是:保证信息传输的完整性、发送者的身份认证、防止交易中的抵赖发生。数字签名技术是不对称加密算法的典型应用,典型的算法是RSA和DSA。
数字签名是个加密的过程,数字签名验证是个解密的过程。数字签名技术是将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送的公钥才能解密被加密的摘要信息,然后用HASH函数对收到的原文产生一个摘要信息,与解密的摘要信息对比。如果相同,则说明收到的信息是完整的,在传输过程中没有被修改,否则说明信息被修改过,因此数字签名能够验证信息的完整性。
在JDK1.1中提供了DSA(Digital Signture Algorithm)数字签名算法,使用公钥系统提供的数字签名。位于Java.Security类库包中存在用于产生和使用数字签名的类:
l KeyPairGenerator类:用于生成公私钥对;
l Signature类:用于生成签名信息,如图所示。。
图 Signature类说明
4.SSL协议
SSL是位于运输层的加密协议,被广泛地用于WEB上的加密应用,保护网络传输地信息。SSL实际是增强TCP/IP Socket协议,1994由NetScape开发,现由IETF(The Internet Engineering Task Force)控制。
JSSE(Java Security Socket Extension,Java安全套接字扩展)是Sun为了解决在Internet上的安全通讯而推出的解决方案。它实现了SSL和TSL(传输层安全)协议。在JSSE中包含了数据加密,服务器验证,消息完整性和客户端验证等技术。通过使用JSSE,开发人员可以在客户机和服务器之间通过TCP/IP协议安全地传输数据.
默认的JSSE配置包括以下内容:
l X509Cerificate :X509认证实体;
l HTTPs Protocol:安全HTTP协议;
l Provider:安全服务提供者;
l SSLSocketFactory:安全套接字客户端工厂类;
l SSLServerSockerFactory:安全套接字服务端工厂类;
l keyStore:密钥库;
l keyStore Type:密钥库类型;
在JAVA的安装目录下的bin目录中,提供了一个密钥和证书管理工具keytool.exe,它被用来管理私钥仓库(keystore)和与之相关的X.509证书链(用以验证与私钥对应的公钥),也可以用来管理其他信任实体。
它使用户能够管理自己的公钥/私钥对及相关证书,用于(通过数字签名)自我认证(用户向别的用户/服务认证自己)或数据完整性以及认证服务。它还允许用户储存他们的通信对等者的公钥(以证书形式)。
通过执行keytool -genkey 参数命令来生成密钥对(公钥和私钥),缺省的密钥对生成算法是“DSA”。在生成 DSA 密钥对时,密钥大小的范围必须在 512 到 1024 位之间,且必须是 64 的倍数。缺省的密钥大小为 1024 位。
keytool 将密钥和证书储存在一个所谓的密钥仓库中。缺省的密钥仓库实现将密钥仓库保存为一个文件,并且采用用口令来保护私钥。
每个 keytool 命令都有一个 -keystore 选项,用于指定 keytool 管理的密钥仓库的永久密钥仓库文件名称及其位置。缺省情况下,密钥仓库储存在用户宿主目录(由系统属性的“user.home”决定)中名为 .keystore 的文件中。在 Solaris 系统中“user.home”缺省为用户的宿主目录。
该类库中可能会出现的异常包括:
l SSLException:SSL实体异常;
l SSLHandshakeException:SSL握手连接异常;
l SSLKeyException:SSL密钥实体异常;
l SSLPeerUnverifiedException:SSL对验证异常;
l SSLProtocolException:SSL协议异常。
2. 类及方法
1.security常用方法
static int |
addProvider(Provider provider) 增加服务提供者 |
static Set |
getAlgorithms(String serviceName) 获得安全算法 |
static String |
getProperty(String key) 获得安全特性 |
static Provider |
getProvider(String name) 获得服务提供者 |
static Provider[] |
getProviders() 获得所有的提供者 |
static int |
insertProviderAt(Provider provider, int position) 在指定位置插入服务提供者 |
static void |
removeProvider(String name) 删除指定的服务提供者 |
static void |
setProperty(String key, String datum) 设置安全属性 |
2.singature常用方法
protected |
|
getAlgorithm() 获得签名算法 |
|
static Signature |
getInstance(String algorithm) 获得签名算法实例 |
static Signature |
getInstance(String algorithm, String provider) 获得指定提供者的签名算法实例 |
getParameters() 获得签名算法参数 |
|
getProvider() 获得签名算法提供者 |
|
void |
initSign(PrivateKey privateKey) 初始化签名算法 |
void |
initVerify(PublicKey publicKey) 初始化验证算法 |
void |
setParameter(AlgorithmParameterSpec params) 设置算法参数 |
byte[] |
sign() 执行签名操作 |
int |
sign(byte[] outbuf, int offset, int len) 执行签名操作,返回指定返回的字节数组 |
void |
update(byte[] data) 使用指定数组修改签名或验证数据 |
void |
update(byte[] data, int off, int len) |
boolean |
verify(byte[] signature) 执行验证签名 |
boolean |
verify(byte[] signature, int offset, |
3. 代码示例
采用DES加/解密指定的信息
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.security.*;
import javax.crypto.*;
public class DESPlusGUI extends Frame{
private static String strDefaultKey = "national";
private Cipher encryptCipher = null; //加密对象实例
private Cipher decryptCipher = null; //解密对象实例
TextField ltext = new TextField("");
TextField lpwd = new TextField("");
TextField lcode = new TextField("");
List lwords = new List(20);
Button bencode = new Button("加 密");
Button bmatch = new Button("匹 配");
Font f = new Font("Serif",Font.BOLD,30); //设置显示字体
public static String byteArr2HexStr(byte[] arrB) throws Exception {
int iLen = arrB.length;
// 每个byte用两个字符才能表示,所以字符串的长度是数组长度的两倍
StringBuffer sb = new StringBuffer(iLen * 2);
for (int i = 0; i < iLen; i++) {
int intTmp = arrB[i];
// 把负数转换为正数
while (intTmp < 0) {
intTmp = intTmp + 256;
}
// 小于0F的数需要在前面补0
if (intTmp < 16) {
sb.append("0");
}
sb.append(Integer.toString(intTmp, 16));
}
return sb.toString();
}
public static byte[] hexStr2ByteArr(String strIn) throws Exception {
byte[] arrB = strIn.getBytes();
int iLen = arrB.length;
// 两个字符表示一个字节,所以字节数组长度是字符串长度除以2
byte[] arrOut = new byte[iLen / 2];
for (int i = 0; i < iLen; i = i + 2) {
String strTmp = new String(arrB, i, 2);
arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16);
}
return arrOut;
}
//界面
public void initGUI(){
Panel pl1 =new Panel();Panel pl2 =new Panel();
Label l1 = new Label("待加密字符串");
Label l2 = new Label("加密密钥");
Label l3 = new Label("加密结果");
setLayout(new BorderLayout()); //主窗体布局
pl1.setLayout(new GridLayout(2,3)); //顶输入布局
pl1.add(l1); pl1.add(l2); pl1.add(l3);
pl1.add(ltext); pl1.add(lpwd); pl1.add(lcode);
pl2.add(bencode);
pl2.add(bmatch);
this.add(pl1, BorderLayout.NORTH);
this.add(lwords, BorderLayout.CENTER);
this.add(pl2, BorderLayout.SOUTH);
lpwd.setFont(f);ltext.setFont(f);lcode.setFont(f);
lwords.setFont(f);
bmatch.setFont(f);bencode.setFont(f);
l1.setFont(f);l2.setFont(f);l3.setFont(f);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
dispose();
System.exit(0);
}
});
bencode.addActionListener(new Listener0());
bmatch.addActionListener(new Listener1());
}
//使用默认密钥
public DESPlusGUI(){
initGUI();
}
//使用指定密钥
public DESPlusGUI(String strKey) throws Exception {
Security.addProvider(new com.sun.crypto.provider.SunJCE());
Key key = getKey(strKey.getBytes());
encryptCipher = Cipher.getInstance("DES");
encryptCipher.init(Cipher.ENCRYPT_MODE, key);
decryptCipher = Cipher.getInstance("DES");
decryptCipher.init(Cipher.DECRYPT_MODE, key);
}
public byte[] encrypt(byte[] arrB) throws Exception {
return encryptCipher.doFinal(arrB);
}
public String encrypt(String strIn) throws Exception {
return byteArr2HexStr(encrypt(strIn.getBytes()));
}
public byte[] decrypt(byte[] arrB) throws Exception {
return decryptCipher.doFinal(arrB);
}
public String decrypt(String strIn) throws Exception {
return new String(decrypt(hexStr2ByteArr(strIn)));
}
private Key getKey(byte[] arrBTmp) throws Exception {
// 创建一个空的8位字节数组(默认值为0)
byte[] arrB = new byte[8];
// 将原始字节数组转换为8位
for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {
arrB[i] = arrBTmp[i];
}
// 生成密钥
Key key = new javax.crypto.spec.SecretKeySpec(arrB, "DES");
return key;
}
public static void main(String[] args) {
DESPlusGUI mainFrame = new DESPlusGUI();
mainFrame.setSize(1000, 600);
mainFrame.setTitle("DES暴力破解");
mainFrame.setVisible(true);
}
//使用该类,来加密以前所编写的TCP通信程序
//加密
class Listener0 implements ActionListener{
public void actionPerformed(ActionEvent e){
try {
String test = new String();
test = ltext.getText();
DESPlusGUI des = new DESPlusGUI(lpwd.getText());//自定义密钥
lcode.setText(des.encrypt(test));
//System.out.println("加密前的字符:"+ test);
//System.out.println("加密后的字符:"+ des.encrypt(test));
//System.out.println("解密后的字符:"+ des.decrypt(des.encrypt(test)));
} catch (Exception e1) {
// TODO: handle exception
e1.printStackTrace();
} } }
//暴力破解,需要关键词字典
class Listener1 implements ActionListener{
public void actionPerformed(ActionEvent e){
matchStr("words.txt");
} }
public long matchStr(String fileName){
long counter = 0;
boolean success = false;
DESPlusGUI des = null;
StringBuffer sb=new StringBuffer("");
try{
FileInputStream reader = new FileInputStream (fileName);
DataInputStream br = new DataInputStream (reader);
String s = null;
String rs = null;
while((s = br.readUTF()) != null) {
counter ++;
des = new DESPlusGUI(s);
rs = des.encrypt(ltext.getText()).toString();
lwords.add(s + "::" + rs, 0);
if(lcode.getText().equalsIgnoreCase(rs)){
lwords.add("经过"+counter+"次匹配成功", 0);
success = true;
break;
} }
if(!success)
lwords.add("经过"+counter+"次匹配失败", 0);
br.close();
reader.close();
}catch(Exception e){}
return counter; //返回字符串匹配成功次数
} }
暴力破解字典生成
import java.io.*; class WordList{
String file; public WordList(String file){
this.file = file;
} public void createBirthdayCode() throws IOException{ //shu zi ge shu
FileOutputStream fis = new FileOutputStream(file);
DataOutputStream dos = new DataOutputStream(fis); String [] monthes = new String[12];
for(int i=0; i<12; i++){
if((i+1)<10){
monthes[i] = "0"+ (i + 1);
}else{
monthes[i] = "" + (i + 1);
}
} String temp;
for(int i=1900; i<2020; i++){
for(int j=0; j<12; j++){
for(int k=0; k<31; k++){
if((k+1)<10){
temp = i + monthes[j] + "0"+ (k+1);
}else{
temp = i + monthes[j] + "" + (k+1);
}
System.out.println(temp);
dos.writeUTF(temp);
}
}
dos.flush();
}
dos.close();
fis.close();
} public static void main(String [] args){
WordList wl = new WordList("word.txt");
double start = System.currentTimeMillis();
try{
wl.createBirthdayCode();
}catch(IOException e){
e.printStackTrace();
}
double end = System.currentTimeMillis();
System.out.println("complete!" + (end - start)+"secs");
}
}
使用上Encode类进行加密和解密举例
客户端加密:
//客户端加密
import java.io.*;
import java.net.*;
public class SimpleClient{
public static void main(String args[]) throws IOException{
Socket comSocket = null;
Encode en = new Encode("123456");
PrintStream out = null;
DataInputStream in = null;
try{
//建立socket连接
comSocket = new Socket("localhost", 1080);//发出连接请求
in = new DataInputStream(comSocket.getInputStream());//分别对应服务器端的O/I流
out = new PrintStream(comSocket.getOutputStream());
}catch(UnknownHostException e){
System.err.println("Can't find the Server host");
System.exit(0);
}catch(IOException e){
System.err.println("Can't get I/O for the Connection");
System.exit(0);
}
DataInputStream stdIn = new DataInputStream(System.in);
String fromServer, fromUser;
while((fromServer = in.readLine()) != null){
System.out.println("Server:" + fromServer);
if(fromServer.equals("bye")) break;
fromUser = stdIn.readLine();
try{
fromUser = en.encrypt(fromUser);
}catch(Exception e){
System.out.println("加密有错误");
}
if(fromUser != null){
System.out.println("Client:" + fromUser);
out.println(fromUser);
} }
out.close();
in.close();
stdIn.close();
comSocket.close();
} }
服务端解密:
//服务器端解密
import java.io.*;
import java.net.*;
public class SimpleServer{
public static void main(String [] args){
//TCP通信,作为服务器
ServerSocket serverSocket = null;
Encode en = new Encode("123456");
//首先建立服务器
try{
serverSocket = new ServerSocket(1080);//端口号唯一标是本进程
System.out.println("Server is Listening Now");
}catch(IOException e){
System.err.println("Can't listen on port");
System.exit(1);
}
//接受客户端连接
Socket clientSocket = null;
try{
clientSocket = serverSocket.accept(); //connect
}catch(IOException e){
System.err.println("Accept failed");
System.exit(1);
}
try{
PrintStream out = new PrintStream(
new BufferedOutputStream(clientSocket.getOutputStream(), 1024) ,false);
DataInputStream in = new DataInputStream(
new BufferedInputStream(clientSocket.getInputStream()));
String inputLine, outputLine;
//输出操作
out.println("Welcome to My Chat Server");
out.flush();//立即将数据从输出缓存提交给网络发送
DataInputStream stdIn = new DataInputStream(System.in);
//输入操作
while((inputLine = in.readLine()) != null){ //接受网络数据
try{
inputLine = en.decrypt(inputLine);
}catch(Exception e){
System.out.println("解密有错误");
}
System.out.println("Customer:" + inputLine);
System.out.print("Server:");
outputLine = stdIn.readLine(); //接受键盘输入
//System.out.println("Server:" + outputLine);
out.println(outputLine); //向网络发送数据
out.flush();
if(outputLine.equals("bye")) break;
}
out.close();//流关闭
in.close();
clientSocket.close();//套接字关闭
serverSocket.close();
}catch(IOException e){
System.err.println("Protocol Stream Error");
} } }
签名指定的文件
import java.security.Signature;
import java.security.KeyPairGenerator;
import java.security.KeyPair;
import java.security.SignatureException;
/*数字签名,使用RSA私钥对对消息摘要签名,然 后使用公鈅验证 测试*/
public class DigitalSignature2Example{
public static void main(String[] args) throws Exception{
if(args.length!=1){
System.err.println("Usage:java DigitalSignature2Example ");
System.exit(1);
}
byte[] plainText=args[0].getBytes("UTF8");
//形成RSA公钥对
System.out.println("\nStart generating RSA key");
KeyPairGenerator keyGen=KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair key=keyGen.generateKeyPair();
System.out.println("Finish generating RSA key");
//使用私鈅签名
Signature sig=Signature.getInstance("SHA1WithRSA");
sig.initSign(key.getPrivate());
sig.update(plainText);
byte[] signature=sig.sign();
System.out.println(sig.getProvider().getInfo());
System.out.println("\nSignature:");
System.out.println(new String(signature,"UTF8"));
//使用公鈅验证
System.out.println("\nStart signature verification");
sig.initVerify(key.getPublic());
sig.update(plainText);
try{
if(sig.verify(signature)){
System.out.println("Signature verified");
}else System.out.println("Signature failed");
}catch(SignatureException e){
System.out.println("Signature failed");
} } }
使用keytool工具生成指定X509密钥包
1.进入Java安装目录,例如 c:\>cd files programs\java1.5_01\bin
2.执行keytool创建密钥库
Keytool –genkey –alias sslTest –keystore sslkeystore
默认在自己的home目录下(windows系统是c:\documents and settings\<你的用户名> 目录下的.keystore文件),创建我们用 RSA 算法生成别名为 feiUserKey 的自签名的证书,如果使用了-keystore mm 就在当前目录下创建一个密钥库mm文件来保存密钥和证书。
3.查看证书:keytool -list
列举了密钥库的所有的证书
PS:dos下输入keytool -help查看帮助
客户端:
/使用该证书进行通信的客户端程序
import java.net.*;
import java.io.*;
import java.util.*;
import java.security.*;
import javax.net.*;
import javax.net.ssl.*;
import java.security.cert.*;
import sun.security.x509.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
public class EchoClient{
public static void main(String [] args){
try{
String KEYSTORE ="sslkeystore";
char [] KEYSTOREPW = "123456".toCharArray();
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(new FileInputStream(KEYSTORE), KEYSTOREPW);
java.security.cert.Certificate c=keystore.getCertificate("sslTest");
System.out.println("密钥库信息:\n"+c.toString());
X509Certificate t=(X509Certificate)c;
System.out.println("Version:" + t.getVersion());
System.out.println("Serials:" + t.getSerialNumber().toString(16));
System.out.println("host:" + t.getSubjectDN());
System.out.println("signer:" + t.getIssuerDN());
System.out.println("valid:"+t.getNotBefore());
System.out.println("sign algorthimdf:"+t.getSigAlgName());
//Enumeration e=ks.aliases();
//while(e.hasMoreElements())
//java.security.cert.Certificate c=ks.getCertificate((String)e.nextElement());
TrustManagerFactory kmf = TrustManagerFactory.getInstance("SunX509");
kmf.init(keystore);
SSLContext sslc = SSLContext.getInstance("SSLv3");
sslc.init(null, kmf.getTrustManagers(), null);
SocketFactory ssf = sslc.getSocketFactory();
//ssf = (SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket sslSocket = (SSLSocket)ssf.createSocket("localhost", 8000);
InputStream is = System.in;
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
OutputStream os = sslSocket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
String str = null;
while((str=br.readLine())!= null){
bw.write(str + "\n");
bw.flush();
}
}catch(Exception e){
e.printStackTrace();
} } }
服务端:
//服务器端
import java.net.*;
import java.io.*;
import java.util.*;
import java.security.*;
import javax.net.*;
import javax.net.ssl.*;
public class EchoServer{
public static void main(String [] args){
try{
String KEYSTORE ="sslkeystore";
char [] KEYSTOREPW = "123456".toCharArray();
char [] KEYPW = "ssltest".toCharArray();
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(new FileInputStream(KEYSTORE), KEYSTOREPW);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, KEYPW);
SSLContext sslc = SSLContext.getInstance("SSLv3");
sslc.init(kmf.getKeyManagers(), null, null);
ServerSocketFactory ssf = sslc.getServerSocketFactory();
SSLServerSocket serverSocket = (SSLServerSocket)ssf.createServerSocket(8000);
System.out.println("ssl Echo Server start at 8000");
SSLSocket sslSocket = (SSLSocket)serverSocket.accept();
InputStream is = sslSocket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String str = null;
while((str=br.readLine())!= null){
System.out.println(str);
System.out.flush();
}
}catch(Exception e){
e.printStackTrace();
} } }
最新文章
- 清华微积分-1_Ch1习题
- 四川软件人才网:打造四川最专业的IT人才招聘平台
- js 判断pc端或手机端
- Memcached和Memcache安装(64位win7)
- jQuery each用法及each解析json
- acpi参考网站
- python subprocess重定向标准输出
- python之面向对象2
- C# 之 提高WebService性能大数据量网络传输处理
- onSaveInstanceState场景
- 分布式爬虫之elasticsearch基础6(bluk)
- centos7安装Lnmp(Linux+Nginx+MySql+Php+phpMyAdmin+Apache)
- libevent安装方法
- 1.5 C++ new和delete操作符
- 遍历frame中的表单:
- CSS 中 nth-child 和 nth-of-type 的区别
- javascript获取地址栏参数
- FMDB给表添加新的字段
- 十六:The YARN Service Registry
- Excel 读写操作