大 学
《密码学原理》 课 程 设 计 报 告
题 目: word解密算法分析
学 院: 信 息 工 程 学 院
班 级: 信 息 安 全 班
姓 名:
班级序号: 01 号
学 号:
指导教师:
时 间: 2014.12.30—2015.1.9
目 录
一、背景介绍. 3
1、背景及意义. 3
2、研究现状. 3
2.1 Office 漏洞及破解. 3
2.2 RSA算法介绍. 5
3、课程设计研究内容. 6
二、预备知识. 7
1、加密与解密的基本技术. 7
三、OFFICE 文档格式及 Word 加密机制分析. 10
1、OFFICE 文档格式分析. 10
2、Word 加密机制分析. 11
四、word解密算法(JAVA实现). 13
一、背景介绍
1、背景及意义
文档的安全是每个用户都非常关心的话题,尤其是在公共办公场所,如何更加有效地保护文档更是一个刻不容缓的问题。 Word 提供了多种方法限制访问用户文档,通过加密被编辑文档的内容,使未经授权者无法查看和更改,给用户的数据提供了必要的安全保障。但在信息化的今天,随着密码应用范围的扩大,遗忘密码的情况也在与日俱增,一旦忘记密码,用户将无法打开或访问该文档,给用户造成很大的损失。在忘记密码之后如何破解这些密码,尽可能地减少损失就成为用户所关心的一个话题。 Word 文档破解研究有着非常重要而现实的意义。对于国家安全部门而言,将破解研究成果与执法工作相结合,可以极大地丰富安全部门的网络执法手段,提高安全部门的执法效率,降低执法成本;对于公司个人而言,Word 文档破解可以在遗失密码时解开文档,恢复重要信息,避免造成各项损失。
2、研究现状
2.1 Office 漏洞及破解
随着 Microsoft Office 系列文档在桌面系统中的广泛应用,其相关应用软件的安全问题也引起了国内外信息安全界和攻击者的广泛关注,从最初的宏病毒到现在的利用文档格式解析的漏洞,各种攻击层出不穷。Office 系列中的 Excel,Word,Powerpoint 软件都采用基于 OLE2 的复合文档结构,这种结构可以包含多种格式的数据。同时,它也导致了 Office 文档面临的安全威胁主要集中在以下三个方面:基于宏病毒的攻击,文档被非法拷贝或者正常交流时,涉密信息和隐藏信息泄漏的威胁,以及基于应用程序漏洞的攻击。
微软的 Word 文档自带有密码保护措施,用户可以用它来防止其他人对文档进行编辑。网上流行的破解 Office 系列文档密码的软件,如 Accent Office Password Recovery、Advanced Office Password Recovery 等,均采用穷举法对所有可能的口令字进行测试,这种传统的破解方法对于短口令有效,但对于长而复杂的口令,因其搜索的密钥空间大,破解很费时,很难在有效的时间内恢复口令。 而当前 Word97(包括 Word97)以后的各个版本为了保持向下兼容性,默认情况下都使用Office 97/2000 兼容的加密算法。但是这种默认的加密算法在实现时存在一些缺陷,加密强度在最好的情况下也仅相当于 40 位密钥的加密强度。 因此,寻找一种绕开猜测用户口令字的传统破解模式,分析和研究 Word 文档加密算法细节,利用加密机制中存在的缺陷设计一种与加密口令长度无关的破解方案是非常有意义的。
2.2 RSA算法介绍
2.2.1 RSA 算法简介
公钥密码算法最主要的特点是加密和解密使用不同的密钥,且加密密钥能公开,而仅需保守解密密钥的机密的密码算法。在这种加密算法中,从公开的加密密钥无法推导出保密的解密密钥,也无法从加密密钥和密文恢复出相应的明文。最有影响力的算法是RSA,它能抵抗到目前为止己知的所有密码攻击。
RSA算法是第一个既能用于数据加密也能用于数字签名的算法,算法的名字以发明者的名字命名。RSA算法的安全性依赖于大数分解问题的难解性#算法中使用的公钥和私钥都是两个大素数(大于100 个十进制位)的函数。据猜测,从一个密钥和密文推断出明文的难度等同于分解两个大素数的积
2.2.2 RSA 算法原理
假设我们需要将信息从机器A传到机器B,首先由机器B随机确定一个Key,我们称之为密匙private_key,将这个可KEY始终保存在机器B中而不发出来;然后,由这个private_key计算出另一个Key,我们称之为公匙Public_key。这个Public_key的特性是几乎不可能通过该Key计算生成它的private_key。接下来通过网络把这个Public_key传给机器A,机器A受到Public_key后,利用该key,将信息加密,并把加密后的信息通过网络发送到机器B,最后机器B利用已知的private_key,就可以解开加密信息。
2.2.3 步骤
RSA算法的安全性依赖于大数因数分解的困难性。公匙和私匙都是两个大素数的函数。
(1)首先选择两个大素数p、q,计算n=p*q; m=(p-1)*(q-1);
(2)而后随机选择加密密匙Public_key,要求和m互质,比如Public_key=m-1;
(3)利用欧几里德算法计算解密密匙private_key,使private_key满足:
Public_key*private_key三1(mod m)
其中Public_key,n是公匙,private_key是密匙。
3、课程设计研究内容
本文主要围绕 Word 加密文档的破解方案展开研究,实现了 Word 加密文档的快速破解。首先利用 Word 加密机制中存在的缺陷,实现了一种与加密口令长度无关的常量时间破解方案,并通过深入分析RSA的原理和实现机制,将RSA算法和 Word 文档破解有机结合。
二、预备知识
本章对文中涉及的密码学知识进行了描述,首先概述了密码技术,介绍了加密解密技术及破解密码的常用方法。
1、加密与解密的基本技术
密码学是研究信息系统安全和保密的科学,它包含密码编码学以及密码分析学。密码编码学是对信息进行编码以实现信息隐蔽,保证其安全性;而密码分析学研究分析破译密码,它包括密码攻击、发现漏洞和系统安全性证明。两者相互对立,而又互相促进地向前发展。
加密就是把数据和信息(称为明文)转换为不可辨认形式(称为密文)的过程,使不应了解数据和信息的人不能够知道和识别。已知密文的内容,再将其转变为明文,这就是解密过程。加密和解密过程组成为加密系统。
任何一种加密系统无论形式多复杂,都可以描述为一个 5 元组(P, C, K, Ek, Dk) ,其中:
(1) P :需要采用某种方法来掩盖其要传送的信息或字符串,即明文;
(2) C :经加密过程将明文变成的信息或字符串,即密文;
(3) K :代表密钥空间,由所有可能的密钥组成的有限集;
(4) Ek :采用某种方法将明文变为另一种不能被非授权者所理解的信息或字符串的过程,即加密变换;
(5) Dk :将密文还原为明文的过程,即解密变换;
加密是在不安全的信息渠道中实现信息安全传输的重要方法。例如,发送方向接收方发送一条信息,发送方需要用加密密钥把信息加密后发送给接收方;接收方收到密文后,用解密密钥把密文恢复为明文。如果信息传输过程中有第三者试图窃密,他只能得到一些密文信息,无法理解其意义,如图 2.1 所示。
图2.1 信息加密系统示意图
破解密码的方法很多,常见的可以分为 3 类。
(1)密钥的穷举搜索
这是破译密码最简单也是最常用的方法,尝试所有可能的密钥组合。但用这种方法进行破译所用的时间非常长。
(2)密码分析
密码分析是指在密钥未知的情况下,利用数学方法破译密文或找到密钥的方法。密码分析有两个基本目标:利用密文发现明文和利用密文发现密钥。常见的密码分析方法有:
①已知明文的破译方法:密码分析者不仅知道一些密文,还知道这些密文相对应的明文,目的是发现加密的密钥。
②选定明文的破译方法:密码分析者设法让对手加密一段分析者选定的明文,并获得加密后的结果,目的是确定加密的密钥。
③差别比较分析法:这种方法是选定明文的破译法的一种,密码分析者设法让对手加密一组相似却差别细微的明文,然后比较它们加密后的结果,从而获得加密的密钥。
不同的加密算法,对以上这些攻击方法的抵抗力是不同的。
(3)其他密码破译方法
除了对密钥的穷举搜索和进行密码分析外,在实际生活中,对手更可能针对人机系统的弱点进行攻击,而不是攻击加密算法本身,以达到其目的。例如可以欺骗用户,套出密钥;在用户输入密钥时,应用各种技术手段,“窥视”或“偷窃”密钥内容;利用加密系统实现中的缺陷或漏洞;对用户使用的加密系统偷梁换柱;从用户工作生活环境的其他来源获得未加密的保密信息,比如进行“垃圾分析”等等。
三、OFFICE 文档格式及 Word 加密机制分析
微软的 Office 系列软件是目前使用最广泛的文字处理软件,Office 的文档结构采用了基于 OLE2 的复合文档结构(Microsoft Word 97 的二进制格式及其更高版本的基础结构都是 .doc文件,即复合文档,格式相当复杂。复合文档不仅可以包含文本,而且可以包含图形、电子表格数据、声音、视频图像以及其它信息。
本章首先分析了复合文档的基本格式,在此基础上分析了 Word 文档的加密机制及其存在的攻击漏洞,随后介绍了两种破解 Word 文档的攻击方案,并对这两种攻击方案进行了比较。
1、OFFICE 文档格式分析
Office 系列软件所产生的文档是 Microsoft Compound Document(微软复合文档)的一种,其文档格式与微软复合文档的文档格式一致。微软复合文档是一种结构化的储存文件,这种文件由微软的 OLE Structured Storage API 来创建和管理。
Office 文件是一种典型的复合文档,基于对象连接和嵌入(Object Linking and Embedding简称 OLE)存储格式,也即“文件里包含文件”,跟操作系统的存储格式类似。OLE 文档无缝地集成了各种类型的数据或组件,实现了一个文件,是一个文件系统的概念。
Office 文档的格式非常复杂,其中可以包含多种不同格式的元数据,如图象,音频,视频,甚至 Excel 表格,PPT 演示文档等其他复合文档。 通过对 OpenOffice,WvWare,AbiWord 等开源软件的代码进行解析和测试,本文得出一些关于复合文档的格式信息。并对 Word 文档格式作出了一定程度的解析。
2、Word 加密机制分析
Office系列软件所产生的文档均采用基于OLE2的微软复合文档结构,它由若干个stream和storage组成。Word文档包含Data stream, 1Table stream,Word Document stream,Summary Informationstream,Document Summary Information stream等。其中Data stream中是图片数据,Word Docu-ment stream中是文本数据,Summary Information stream和Document Summary Informationstream中是摘要信息,等等。
当Word文档被加密时,文档中只有1 Table,WordDocument等带有文本、图片等数据的stream被加密。1 Table stream中存放验证口令是否正确的信息,它由3个16字节的域组成,其中第一个域中存放的是一个新鲜数Salt,用户输入的口令在hash计算过程中加入Salt值生成40位数,该40位数为决定RC4初始化向量的值,通过该40位数可以生成RC4加/解密的密钥,计算过程如图1所示。第二个域中存放的是系统随机产生的一个128位的新鲜数B被RC4加密后的结果。扩展新鲜数B为一个64字节的字节串,然后对所得的64位字节串计算MD5hash,计算出来的hash值被RC4加密后存放在第三个域中。
测试用户输入的口令正确与否,主要依据1 Table第二个域和第三个域中存放的数据。用户输入口令打开一个被加密的Word文档时,口令加上第一个域中的Salt值经过hash计算等生成RC4加/解密的密钥流。Word程序读取1 Table中第二个域和第三个域的值,经RC4解密得到值b和c。如果c为b的MD5Hash值,那么口令正确,否则口令错误。过程如图2所示。
四、word解密算法(JAVA实现)
Word解密代码:
package edu.wx.test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
public class RSATool {
public static void makekeyfile(String pubkeyfile, String privatekeyfile)
throws NoSuchAlgorithmException, FileNotFoundException, IOException {
// KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
// 初始化密钥对生成器,密钥大小为1024位
keyPairGen.initialize(1024);
// 生成一个密钥对,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
// 得到私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 得到公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 生成私钥
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
privatekeyfile));
oos.writeObject(privateKey);
oos.flush();
oos.close();
oos = new ObjectOutputStream(new FileOutputStream(pubkeyfile));
oos.writeObject(publicKey);
oos.flush();
oos.close();
System.out.println("make file ok!");
}
/**
*
* @param k
* @param data
* @param encrypt
* 1 加密 0解密
* @return
* @throws NoSuchPaddingException
* @throws Exception
*/
public static byte[] handleData(Key k, byte[] data, int encrypt)
throws Exception {
if (k != null) {
Cipher cipher = Cipher.getInstance("RSA");
if (encrypt == 1) {
cipher.init(Cipher.ENCRYPT_MODE, k);
byte[] resultBytes = cipher.doFinal(data);
return resultBytes;
} else if (encrypt == 0) {
cipher.init(Cipher.DECRYPT_MODE, k);
byte[] resultBytes = cipher.doFinal(data);
return resultBytes;
} else {
System.out.println("参数必须为: 1 加密 0解密");
}
}
return null;
}
/**
* 公钥加密
*
* @param data
* @param publicKey
* @return
* @throws Exception
*/
public static String encryptByPublicKey(String data, RSAPublicKey publicKey)
throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 模长
int key_len = publicKey.getModulus().bitLength() / 8;
// 加密数据长度 <= 模长-11
String[] datas = splitString(data, key_len - 11);
String mi = "";
//如果明文长度大于模长-11则要分组加密
for (String s : datas) {
mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
}
/**
* 私钥解密
*
* @param data
* @param privateKey
* @return
* @throws Exception
*/
public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey)
throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
//模长
int key_len = privateKey.getModulus().bitLength() / 8;
byte[] bytes = data.getBytes();
byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
//System.err.println(bcd.length);
//如果密文长度大于模长则要分组解密
String ming = "";
byte[][] arrays = splitArray(bcd, key_len);
for(byte[] arr : arrays){
ming += new String(cipher.doFinal(arr));
}
return ming;
}
/**
* ASCII码转BCD码
*
*/
public static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) {
byte[] bcd = new byte[asc_len / 2];
int j = 0;
for (int i = 0; i < (asc_len + 1) / 2; i++) {
bcd[i] = asc_to_bcd(ascii[j++]);
bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++])) + (bcd[i] << 4));
}
return bcd;
}
public static byte asc_to_bcd(byte asc) {
byte bcd;
if ((asc >= '0') && (asc <= '9'))
bcd = (byte) (asc - '0');
else if ((asc >= 'A') && (asc <= 'F'))
bcd = (byte) (asc - 'A' + 10);
else if ((asc >= 'a') && (asc <= 'f'))
bcd = (byte) (asc - 'a' + 10);
else
bcd = (byte) (asc - 48);
return bcd;
}
/**
* BCD转字符串
*/
public static String bcd2Str(byte[] bytes) {
char temp[] = new char[bytes.length * 2], val;
for (int i = 0; i < bytes.length; i++) {
val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
val = (char) (bytes[i] & 0x0f);
temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
}
return new String(temp);
}
/**
* 拆分字符串
*/
public static String[] splitString(String string, int len) {
int x = string.length() / len;
int y = string.length() % len;
int z = 0;
if (y != 0) {
z = 1;
}
String[] strings = new String[x + z];
String str = "";
for (int i=0; i<x+z; i++) {
if (i==x+z-1 && y!=0) {
str = string.substring(i*len, i*len+y);
}else{
str = string.substring(i*len, i*len+len);
}
strings[i] = str;
}
return strings;
}
/**
*拆分数组
*/
public static byte[][] splitArray(byte[] data,int len){
int x = data.length / len;
int y = data.length % len;
int z = 0;
if(y!=0){
z = 1;
}
byte[][] arrays = new byte[x+z][];
byte[] arr;
for(int i=0; i<x+z; i++){
arr = new byte[len];
if(i==x+z-1 && y!=0){
System.arraycopy(data, i*len, arr, 0, y);
}else{
System.arraycopy(data, i*len, arr, 0, len);
}
arrays[i] = arr;
}
return arrays;
}
public static void main(String[] args) throws Exception {
String pubfile = "d:/temp/pub.key";
String prifile = "d:/temp/pri.key";
makekeyfile(pubfile, prifile);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
pubfile));
RSAPublicKey pubkey = (RSAPublicKey) ois.readObject();
ois.close();
ois = new ObjectInputStream(new FileInputStream(prifile));
RSAPrivateKey prikey = (RSAPrivateKey) ois.readObject();
ois.close();
// 使用公钥加密
String msg = "~O(∩_∩)O哈哈~";
String enc = "UTF-8";
// 使用公钥加密私钥解密
System.out.println("原文: " + msg);
byte[] result = handleData(pubkey, msg.getBytes(enc), 1);
System.out.println("加密: " + new String(result, enc));
byte[] deresult = handleData(prikey, result, 0);
System.out.println("解密: " + new String(deresult, enc));
msg = "abcdefg";
// 使用私钥加密公钥解密
System.out.println("原文: " + msg);
//byte[] result2 = handleData(prikey, msg.getBytes(enc), 1);
//System.out.println("加密: " + new String(result2, enc));
//byte[] deresult2 = handleData(pubkey, result2, 0);
//System.out.println("解密: " + new String(deresult2, enc));
String result2 = encryptByPublicKey(msg,pubkey);
System.out.println("加密: " + result2);
String deresult2 = decryptByPrivateKey(result2, prikey);
System.out.println("解密: " + deresult2);
}
}