openssl – 使用EVP函数进行base64编码

三月 23, 2007 @ 12:23 上午 | 发表在 C/C++, 网络安全 | 留下评论

近日需要在C++下面做一些加解密的事情,因为是在Linux下面,首选自然是openssl. 但openssl的文档真是不敢恭维,文档少得可怜不说,在网络上搜索到的东西也是鱼龙混杂.我想我还是总结一下,有益于自己也造福别人.

首先是Base64的编码,openssl提供两种方式,BIO和EVP. BIO是老的方式,比较复杂,EVP则是新的方式,号称比较容易,但其实也不是那么直接.

一些网络上的文章号称使用EVP_EncodeBlock来处理Base64,但他们常常都忽略了一点,EVP_EncodeBlock的结果中没有换行符. openssl的命令行工具使用的是PEM的格式,也就是说,每隔64个字符,就会插入一个换行符.所以,使用EVP_EncodeBlock生成的结果和PEM是不兼容的.换句话说,这个结果使用openssl的命令行工具是读不出来的.这最开始让我非常困惑.

使用下面的代码可以生成正确的PEM编码.

使用openssl base64命令行工具可以确认结果.

/**
* Use EVP to Base64 encode the input byte array to readable text
*/
char* base64(const unsigned char *inputBuffer, int inputLen)
{
EVP_ENCODE_CTX ctx;
int base64Len = (((inputLen+2)/3)*4) + 1; // Base64 text length
int pemLen = base64Len + base64Len/64; // PEM adds a newline every 64 bytes
char* base64 = new char[pemLen];
int result;
EVP_EncodeInit(&ctx);
EVP_EncodeUpdate(&ctx, (unsigned char *)base64, &result, (unsigned char *)inputBuffer, inputLen);
EVP_EncodeFinal(&ctx, (unsigned char *)&base64[result], &result);
return base64;
}


/**
* Use EVP to Base64 decode the input readable text to original bytes
*/
unsigned char* unbase64(char *input, int length, int* outLen)
{
EVP_ENCODE_CTX ctx;
int orgLen = (((length+2)/4)*3) + 1;
unsigned char* orgBuf = new unsigned char[orgLen];
int result, tmpLen;
EVP_DecodeInit(&ctx);
EVP_DecodeUpdate(&ctx, (unsigned char *)orgBuf, &result, (unsigned char *)input, length);
EVP_DecodeFinal(&ctx, (unsigned char *)&orgBuf[result], &tmpLen);
result += tmpLen;
*outLen = result;
return orgBuf;
}

Advertisements

C++ Makefile 模版

三月 16, 2007 @ 5:26 上午 | 发表在 C/C++ | 留下评论

偶尔需要做些C++的项目,第一件事情就是写Makefile.其实我需要的Makefile都是大同小异.但每次都要花些时间重新写一个.终于决定把模板记下来.
目录结构:
[项目根目录]

| — inc
| — src
| — bin

头文件位于inc, .cpp位于src,生成的可执行文件位于bin. 这个Makefile也是位于src下面.

每添加一个新文件的时候,只需要在OBJS里面加上对应的项就可以.

# Compiler options
CC = g++
DEBUG = -g
CFLAGS = -Wall -c $(DEBUG)
LFLAGS = -Wall $(DEBUG)# Project layout
INC_DIR = ../inc
BIN_DIR = ../bin
EXE = licenseAgent
OBJS = LicenseAgent.o License.o Log.o
INCLUDES = -I $(INC_DIR)

all:$(EXE)

$(EXE): $(OBJS)
\mkdir -p $(BIN_DIR)
$(CC) $(LFLAGS) -o $(BIN_DIR)/$(EXE) $(OBJS)

clean:
\rm $(BIN_DIR)/$(EXE) $(OBJS)

%.o: %.C
$(CC) $(CFLAGS) $(INCLUDES) -c $<

散列(Hash)和加密(Encryption)

三月 10, 2007 @ 1:43 上午 | 发表在 网络安全 | 留下评论

散列和加密是两个很容易混淆的概念,特别在中文里,我们会通常把它们统称为”加密”.但实际上,这是两个截然不同的东东,虽然他们的目的都是要防止原来的数据被人看到,他们却有两个独特的属性.

  • 散列是单向而加密是双向的.

当数据被Hash的时候,你保证别人无法用别的数据产生同样的结果,这样接收方收到你的数据,它只要运行同样的Hash函数,就可以确认这个数据确实来自于你,而不是某个人假冒你的名义发过来的.但同时,Hash的结果也不能被还原成原来的数据.否则的话,Hash的数据在网络上传递就没法保证安全.

而加密注定是双向的,收到的一方通过使用一个私有的钥匙(Key)就可以把数据还原.

  • 散列的结果是定长的,而加密的长度对应于原来的长度.

散列的结果通常都是定长的.比如说,不管输入的数据是多长,MD5的结果是128位,而SHA-1总是160位.而加密的结果通常是对应于输入长度的,长的数据会输出长的结果,短的则会输出短的结果.

在URL里传送中文参数

三月 6, 2007 @ 2:47 上午 | 发表在 J2EE | 留下评论

在WEB开发中,支持象中文,日文等非ASCII经常是一件头痛的事情。因为所有的服务器和浏览器最开始都是基于ASCII的,后来开始支持了,对标准的支持也是参差不齐,很让人混淆,所以很多时候一件简单的事情做起来都不容易。HTTP参数的传递就有点混淆。

从网页传送参数到服务器通常有两种方式, 直接附在URL后面,或者通过表单来传递。两种方式如下:

URL:参数和值被附加在URL后面,作为URL的一部分被送到服务器。

表单:参数和值放在消息体里面发送。

在表单的情形下,中文参数比较好处理。通常对非ASCII的字符集,页面的编码会用UTF-8。表单提交了以后,在服务器端,用HttpServletRequest.getParameter()就可以拿到表单的值。

但如果参数是在URL里传递的,事情就会比较麻烦。URL里面只能用ASCII(更准确地说,ISO8859_1) ,所有的不在字符集里面的字符都要进行转化才可以传送。javascript的两个函数encodeURI和encodeURIComponent可以做转化的工作。他们的差别是encodeURI 可以用来编码整个URL,但encodeURIComponent只能用在一个部分。

一个典型的URL的建造方式:

var uservalue = "客人";
var sURL = "http://www.mysite.com/myapp/myServlet?";
sURL += “username";
sURL += "=";
sURL += encodeURIComponent(uservalue);


在服务器里看到的参数是经过ISO8859_1编码的字节流。假设页面编码是UTF-8,下面的代码可以读出正确的用户名:

String username = request.getParameter("username");
byte[] bytes = username.getBytes("8859_1");
username = new String(bytes, "UTF-8");

如果参数的名字本身也是中文的,那就不能使用getParameter()而应该用getParameterMap(),否则的话参数的值会找不到,因为参数本身也是8859_1的字节流。

如果不使用encodeURIComponent或者encodeURI会怎么样呢?因为他们不在8859_1的字符集里面,送到服务器端的会是无法解释得乱码。

以上的代码在tomcat5.5里面测试通过。

在IE里创建可覆盖ActiveX控件的DHTML菜单

一月 20, 2007 @ 2:13 上午 | 发表在 DHTML | 留下评论

对于很多面向企业用户的网页,使用下拉式菜单是很自然的事情。企业用户通常习惯于使用MS Windows类型的用户界面,下拉式菜单是最习惯的界面元素之一。下拉式菜单有很多种,有基于DHTML的,有基于Applet的, 也有基于ActiveX控件的。其中DHTML菜单对大部分应用是最合适的。因为相对于另外两种菜单,它对客户端没有特别的要求。Applet和ActiveX菜单都需要在客户端安装对象,特别Applet还要求安装庞大的JVM,这对很多客户都不能接受。另外随着IE对安全级别的提高,用户需要修改浏览器的安全设置,这也是很多企业级用户不能接受的。而DHTML菜单唯一的要求只要浏览器支持Javascript,现在基本上所有的浏览器都支持,所以成为最合适的菜单选择。但DHTML菜单也不是没有缺点,其中一个就是下拉的菜单不能覆盖ActiveX控件。当下拉的菜单碰到ActiveX控件的时候,IE会把ActiveX控件显示在最上面,这样菜单就会被截断。没有谁会需要这样的方式,所以为了解决这个问题,很多人就会宁愿选用Applet或ActiveX菜单。

问题的根源在于IE对控件的处理方式上。每个网页的显示元素都有一个z-index值,当多个元素重叠的时候,IE通过使用z-index来决定谁覆盖谁。z-index高的元素会覆盖z-index低的元素。听起来似乎只要给菜单项一个高的z-index值就可以解决上面的问题。不是那么简单。问题就在IE对普通HTML元素和控件的z-index是分开处理的。不管HTML元素的z-index值多高,只要和控件有重叠,就会被控件覆盖。

对IE为什么要这样做非常困惑,但反正我们对于微软的类似做法早已是见多了,所以也就见怪不怪了。但问题还是要解决,Applet菜单对我是绝对不可接受的。 为了一个菜单强迫客户安装十几兆的JVM,估计产品会都被退货。

所幸的是还有一个IFrame。IFrame在IE的z-index显示结构里面地位很特别。它横跨两边。普通HTML元素显示会把它计算在内,控件显示也会考虑它。这样,我们就可以利用IFrame把控件挡在下面而显示菜单。简单地说,我们可以给控件z-index为1,创建一个和菜单一样大小的IFrame,并且设它的z-index为2,然后菜单的z-index为3。这样的话,菜单就可以成功地显示在控件上面。

IFrame可以直接用HTML来些,只要保证它的大小和上面的菜单项大小一样就可以了。一个事例代码可以是:

<IFRAME style="DISPLAY:none;LEFT:0px;POSITION:absolute;TOP:0px" src="javascript:false;" frameBorder="0" scrolling="no">
</IFRAME>

在src里面放置javascript:false的目的是防止IFrame试图载入文件。

在真正的菜单开发上,更有可能用到是动态地生成IFrame.这可以使用insertAdjacentHTML()来做到。然后可以通过改变IFrame的style来调整它的尺寸,位置和显示与否。下面的CSS属性可能会用到。

<em>iframe</em>.style.top
<em>iframe</em>.style.left
<em>iframe</em>.style.width
<em>iframe</em>.style.height
<em>iframe</em>.style.zIndex
<em>iframe</em>.style.display

如果你决定不自己开发(明智的决定,开发并维护一个菜单其实是一件耗力的事情,想想这么多的浏览器版本就知道是一件多麻烦的事了),而采用现成的菜单的话。在决定使用某一个产品之前,你可能会想知道它是否支持ActiveX控件。答案有点出人意料,支持的其实很少。在我测试过的产品中,免费的只有Yahoo的YUI支持,付费的有milonic sothink支持。在YUI中,缺省 的设置只支持IE6及以下版本的,所以如果你需要支持IE7,在代码里面把菜单项的iframe设为true就可以了。另外,ActiveX控件需要放在跟菜单不同的IFrame里。不确定为什么会这样,但测试的结果是这样.

« 上一页

在WordPress.com的博客.
Entries评论 feeds.