PGP (Pretty Good Privacy) 是由 Phil Zimmermann 于 1991 开发的一个用于数据加密和数字签名的程序,由于被广泛应用以至于后来形成一个开放的标准 OpenPGP,而 GnuPG 则是实现了该标准的一个开源免费程序,本文将会简单介绍如何使用 GnuPG 管理钥匙、加密解密文件和电子邮件、数字签名文件和电子邮件等内容。篇幅有点长,不过内容是很简单的,可以一步一步跟着来。@ivarptr
一、加密和数字签名的简单原理首先每个人使用程序生成地球上惟一的一对钥匙,分别称为公钥和私钥。公钥用于加密,私钥用于解密。使用公钥加密过的信息只能由配对的私钥解开。
加密的过程是:如果A君要发送信息给B君,首先B君得把自己的公钥扔出来,A君得获取B君的公钥后加密信息并发送过去,B君收到(加过密的)信息使用自己的私钥解密就可以还原信息了。
而数字签名的过程稍微不同,信息是通过普通未加密方式发送信息给对方的,只是在每条信息后面都会附加一坨字符(名曰:签名),这个签名是由程序根据发送者的私钥以及信息内容计算得出,接收者使用发送者的公钥就可以核对信息有无被篡改。
二、获取并安装 GnuPGGnuPG 是一个集钥匙管理、加密解密、数字签名于一身的工具,对于 Linux 系统,一般可以在系统本身的软件源找到 GnuPG,比如对于 ArchLinux 可以使用如下命令安装:
$ sudo pacman -S gnupg
对于 Windows 系统可以下载免费开源程序包 GPG4Win,建议下载完全版,里面既包含了 GnuPG 命令行工具,又有图形钥匙管理工具 Kleopatra 和支持 GnuPG 的邮件客户端程序 Claws Mail。
三、生成钥匙对并发布公钥到网上下面的讲解是基于命令行的,如果你比较喜欢图形工具,仍然建议先阅读完本章,因为图形工具的操作基本上跟下面的命令一一对应,而使用命令行讲解比较方便和容易理解。
1、生成钥匙对$ gpg --gen-key
使用上面的命令可以生成地球上惟一的一对钥匙对(注:命令行前的钱币符号是提示符,不用输入),运行后会询问你几个问题,首选是选择钥匙对的算法:
Please select what kind of key you want:
(1) RSA and RSA (default)
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
Your selection?
默认选择 RSA 就可以了,输入1并回车。然后选择钥匙的长度:
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
默认是 2048 位,直接回车。然后选择钥匙的有效期限:
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0)
默认是永远有效的,如果你打算过一段时间就换一把新钥匙,可以在此输入期限,比如输入30表示有效期为30天,数字后面可以加上单位,比如30m表示30个月,30y表示30年。如果你不知道这步有什么作用,则直接回车。然后询问你确定吗?果断输入y并回车。
接下来是填写一些个人信息:
Real name: ivarptr
Email address: ivarptr@126.com
Comment: ivarptr on Twitter
第1行输入你的名字或者平常喜欢用的网名,比如我的是 ivarptr。
第2行输入你的email地址,比如我上面输入的是 ivarptr@126.com。
第3行输入一行备注,备注的作用是进一步标识自己的身份,比如你在姓名一行输入“小明”,为了让你的朋友更确切地知道是哪个小明,你可以输入“你隔壁家的小明”。
这三行信息用于产生一个标识(uid),用来标识这个钥匙对,在下面的命令行里,就可以用名字或者email地址来指定这个钥匙对。
虽然理论上这些信息是可以随意输入,而且也不会有人阻止你这么做,不过当别人拿到你的公钥时可能会搞不清谁对应谁,所以尽量使用别人分辨得清的名字和email地址。信息输入完之后再输入o并回车确定。
因为 GnuPG 的钥匙(包括公钥和私钥)是保存在本机上的,如果有人或者黑客进入你的计算机把你的私钥盗走了,那么你的身份就有可能被冒充的危险。所以接下来你需要输入一个密码用于保护你的私钥。这个密码最好选择一个稍微复杂一些的。
接下来就是等待程序生成钥匙对了,这个过程根可能需要几秒到几分钟时间,期间你可以去忙其他的事情。
2、查看本机钥匙信息上一步完成后,可以使用如下命令查看本机上的公钥:
$ gpg --list-keys
输出的结果跟下面的类似:
/home/ivarptr/.gnupg/pubring.gpg
——————————–
pub 2048R/72E75B05 2013-04-17 [expires: 2015-04-17]
uid ivarptr (ivarptr on Twitter) <ivarptr@126.com>
sub 2048R/74F0F5F9 2013-04-17 [expires: 2015-04-17]
从中我们可以看到刚才生成的钥匙对的公钥部分,其中:
- 72E75B05 是这个公钥的id,这个id跟uid都是用于标识这个公钥的,因为uid是用户随便输入的所以会有重复的情况,因此在某些需要明确指定公钥的命令,需要用id而不能用uid表示这个公钥。
- 2013-04-17 是生成这个公钥的时间,后面括号内的是有效期。
在实际应用中,因为要发送加密信息给你的朋友就需要对方的公钥,所以在本机上可能会有多个朋友的公钥,但私钥往往只有一个,也就是自己的私钥。使用下面命令可以查看本机上的私钥。
$ gpg –list-secret-keys
3、导出公钥为了将自己的公钥扔给他人,你需要把公钥导出成为一个文件:
$ gpg -a --output key.public --export UID
注:你要把其中的 UID 替换成你的名字或者email地址。
其中参数
- -a 表示输出文本文件格式。默认输出是二进制格式,因为二进制格式不太方便在网络(比如论坛或者博客)上展示,所以推荐文本格式。
- –output 指定输出文件的名字,你可以更改为其他名字。
- –export 表示执行输出公钥操作,后面的 UID 为你要输出的公钥的标识。
运行之后会在当前文件夹得到一个 key.public 文件,你可以使用文本编辑器或者 cat 命令查看里面的内容,大致如下:
$ cat key.public
—–BEGIN PGP PUBLIC KEY BLOCK—–
Version: GnuPG v2.0.19 (GNU/Linux)
…………
…………
—–END PGP PUBLIC KEY BLOCK—–
公钥导出之后,你可以通过各种方式把它发送给你的朋友,比如email或者聊天工具。而比较方便的是把公钥发布到公钥服务器。
4、把公钥发布到公钥服务器 公钥服务器用于储存和
//no-cache指示请求或响应消息不能缓存
response.setHeader("Cache-Control", "no-cache");
int width = 80, height = 20;
//在内存中创建图像
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
//获取画笔
Graphics g = image.getGraphics();
//设置画笔颜色
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
//开始生成验证码,这里用加法求和
Random r = new Random();
int num1 = r.nextInt(10);//操作数1
int num2 = r.nextInt(10);//操作数2
int result = num1 + num2;//加法和结果
String code = String.valueOf(result);
//将验证码存入session
session.setAttribute("code", code);
//将验证码显示到图像中
g.setColor(Color.BLACK);
g.setFont(new Font("", Font.PLAIN, 20));
g.drawString(num1+"+"+num2+"= ?", 10, 15);
//随即产生干扰图像
//随即产生5条直线
for (int i = 0; i < 5; i++) {
int x = r.nextInt(width);
int y = r.nextInt(height);
g.setColor(Color.RED);
g.drawLine(width/(x+1), height/(y+1), x, y);
}
//产生100个点
for(int i=0;i<100;i++) {
int x = r.nextInt(width);
int y = r.nextInt(height);
g.setColor(Color.BLUE);
g.drawOval(x, y, 1, 1);
}
ImageIO.write(image, "JPEG", response.getOutputStream());
out.clear();
out = pageContext.pushBody();
%>
td><img id="validation" src=/blog_article/"validation.html onclick="refresh()">引用验证码即可
写着玩的
代码:
1.从网站主页开始访问,递归获取到链接,list1放所有遇到过的链接,list2放需要请求的链接
起始状态list1、list2只包含主页的地址,size=1
当list2为空时,递归结束
2.请求链接时,判断返回的类型,如果是图片类型,就保存到文件夹
# coding:utf-8 import re, urllib2, os, datetime, urlparse def main(LEFT_PAGES): if len(LEFT_PAGES) == 0: print "没有需要访问的网页了,END" return else: global MAIN_CNT print "...第%s次递归进入MAIN函数..." % MAIN_CNT tmp_pages = [] for page in LEFT_PAGES: tmp_pages.append(page) for each in tmp_pages: print "准备获取网页:%s" % each try: resp = urllib2.urlopen(each) except urllib2.HTTPError as err: print err.code, each continue finally: LEFT_PAGES.remove(each) source = resp.read() current_url = resp.geturl() content_type = resp.headers.get("Content-Type") resp.close() # 保存图片文件 type1, type2 = content_type.split(";")[0].split("/") if type1 is not None and type2 is not None and type1.lower() == "image": src_dir = os.path.dirname(__file__) filename = os.path.join(src_dir, "source", datetime.datetime.now().strftime("%Y%m%d.%H%M%S%f") + "." + type2) fp = file(filename, "wb") fp.write(source) fp.close() # 抽取链接 hrefs = re.findall(PATTERN, source) if len(hrefs) > 0: for each in hrefs: href = each[1] href = urlparse.urljoin(current_url, href) href = href.replace("/../", "/") if href not in HAS_MEET_PAGES: HAS_MEET_PAGES.append(href) if href.startswith("http://www.renrendai.com"): LEFT_PAGES.append(href) MAIN_CNT += 1 main(LEFT_PAGES) if __name__ == '__main__': VISIT_SITE = "http://www.renrendai.com/" HAS_MEET_PAGES = [VISIT_SITE] LEFT_PAGES = [VISIT_SITE] MAIN_CNT = 1 PATTERN = re.compile('(href|src|area)="([^\s]+)"') main(LEFT_PAGES)
运行结果: