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, int length) 验证指定长度数组的数据 |
3. 代码示例
采用DES加/解密指定的信息
1 import java.io.*; 2 import java.awt.*; 3 import java.awt.event.*; 4 import java.security.*; 5 import javax.crypto.*; 6 public class DESPlusGUI extends Frame{ 7 private static String strDefaultKey = "national"; 8 private Cipher encryptCipher = null; //加密对象实例 9 private Cipher decryptCipher = null; //解密对象实例 10 TextField ltext = new TextField(""); 11 TextField lpwd = new TextField(""); 12 TextField lcode = new TextField(""); 13 List lwords = new List(20); 14 Button bencode = new Button("加 密"); 15 Button bmatch = new Button("匹 配"); 16 Font f = new Font("Serif",Font.BOLD,30); //设置显示字体 17 public static String byteArr2HexStr(byte[] arrB) throws Exception { 18 int iLen = arrB.length; 19 // 每个byte用两个字符才能表示,所以字符串的长度是数组长度的两倍 20 StringBuffer sb = new StringBuffer(iLen * 2); 21 for (int i = 0; i < iLen; i++) { 22 int intTmp = arrB[i]; 23 // 把负数转换为正数 24 while (intTmp < 0) { 25 intTmp = intTmp + 256; 26 } 27 // 小于0F的数需要在前面补0 28 if (intTmp < 16) { 29 sb.append("0"); 30 } 31 sb.append(Integer.toString(intTmp, 16)); 32 } 33 return sb.toString(); 34 } 35 public static byte[] hexStr2ByteArr(String strIn) throws Exception { 36 byte[] arrB = strIn.getBytes(); 37 int iLen = arrB.length; 38 // 两个字符表示一个字节,所以字节数组长度是字符串长度除以2 39 byte[] arrOut = new byte[iLen / 2]; 40 for (int i = 0; i < iLen; i = i + 2) { 41 String strTmp = new String(arrB, i, 2); 42 arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16); 43 } 44 return arrOut; 45 } 46 //界面 47 public void initGUI(){ 48 Panel pl1 =new Panel();Panel pl2 =new Panel(); 49 Label l1 = new Label("待加密字符串"); 50 Label l2 = new Label("加密密钥"); 51 Label l3 = new Label("加密结果"); 52 setLayout(new BorderLayout()); //主窗体布局 53 pl1.setLayout(new GridLayout(2,3)); //顶输入布局 54 pl1.add(l1); pl1.add(l2); pl1.add(l3); 55 pl1.add(ltext); pl1.add(lpwd); pl1.add(lcode); 56 pl2.add(bencode); 57 pl2.add(bmatch); 58 this.add(pl1, BorderLayout.NORTH); 59 this.add(lwords, BorderLayout.CENTER); 60 this.add(pl2, BorderLayout.SOUTH); 61 lpwd.setFont(f);ltext.setFont(f);lcode.setFont(f); 62 lwords.setFont(f); 63 bmatch.setFont(f);bencode.setFont(f); 64 l1.setFont(f);l2.setFont(f);l3.setFont(f); 65 addWindowListener(new WindowAdapter() { 66 public void windowClosing(WindowEvent e) { 67 dispose(); 68 System.exit(0); 69 } 70 }); 71 bencode.addActionListener(new Listener0()); 72 bmatch.addActionListener(new Listener1()); 73 } 74 //使用默认密钥 75 public DESPlusGUI(){ 76 initGUI(); 77 } 78 //使用指定密钥 79 public DESPlusGUI(String strKey) throws Exception { 80 Security.addProvider(new com.sun.crypto.provider.SunJCE()); 81 Key key = getKey(strKey.getBytes()); 82 encryptCipher = Cipher.getInstance("DES"); 83 encryptCipher.init(Cipher.ENCRYPT_MODE, key); 84 decryptCipher = Cipher.getInstance("DES"); 85 decryptCipher.init(Cipher.DECRYPT_MODE, key); 86 } 87 public byte[] encrypt(byte[] arrB) throws Exception { 88 return encryptCipher.doFinal(arrB); 89 } 90 public String encrypt(String strIn) throws Exception { 91 return byteArr2HexStr(encrypt(strIn.getBytes())); 92 } 93 public byte[] decrypt(byte[] arrB) throws Exception { 94 return decryptCipher.doFinal(arrB); 95 } 96 public String decrypt(String strIn) throws Exception { 97 return new String(decrypt(hexStr2ByteArr(strIn))); 98 } 99 private Key getKey(byte[] arrBTmp) throws Exception { 100 // 创建一个空的8位字节数组(默认值为0) 101 byte[] arrB = new byte[8]; 102 // 将原始字节数组转换为8位 103 for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) { 104 arrB[i] = arrBTmp[i]; 105 } 106 // 生成密钥 107 Key key = new javax.crypto.spec.SecretKeySpec(arrB, "DES"); 108 return key; 109 } 110 public static void main(String[] args) { 111 DESPlusGUI mainFrame = new DESPlusGUI(); 112 mainFrame.setSize(1000, 600); 113 mainFrame.setTitle("DES暴力破解"); 114 mainFrame.setVisible(true); 115 } 116 //使用该类,来加密以前所编写的TCP通信程序 117 //加密 118 class Listener0 implements ActionListener{ 119 public void actionPerformed(ActionEvent e){ 120 try { 121 String test = new String(); 122 test = ltext.getText(); 123 DESPlusGUI des = new DESPlusGUI(lpwd.getText());//自定义密钥 124 lcode.setText(des.encrypt(test)); 125 //System.out.println("加密前的字符:"+ test); 126 //System.out.println("加密后的字符:"+ des.encrypt(test)); 127 //System.out.println("解密后的字符:"+ des.decrypt(des.encrypt(test))); 128 } catch (Exception e1) { 129 // TODO: handle exception 130 e1.printStackTrace(); 131 } } } 132 //暴力破解,需要关键词字典 133 class Listener1 implements ActionListener{ 134 public void actionPerformed(ActionEvent e){ 135 matchStr("words.txt"); 136 } } 137 public long matchStr(String fileName){ 138 long counter = 0; 139 boolean success = false; 140 DESPlusGUI des = null; 141 StringBuffer sb=new StringBuffer(""); 142 try{ 143 FileInputStream reader = new FileInputStream (fileName); 144 DataInputStream br = new DataInputStream (reader); 145 String s = null; 146 String rs = null; 147 while((s = br.readUTF()) != null) { 148 counter ++; 149 des = new DESPlusGUI(s); 150 rs = des.encrypt(ltext.getText()).toString(); 151 lwords.add(s + "::" + rs, 0); 152 if(lcode.getText().equalsIgnoreCase(rs)){ 153 lwords.add("经过"+counter+"次匹配成功", 0); 154 success = true; 155 break; 156 } } 157 if(!success) 158 lwords.add("经过"+counter+"次匹配失败", 0); 159 br.close(); 160 reader.close(); 161 }catch(Exception e){} 162 return counter; //返回字符串匹配成功次数 163 } }