Java实现RFC2865 Radius协议的密码加密

时间: 2009-8-07 - 分类: Java - 评论: 1 - 阅读: 1,128 次

最近项目中要做Radius协议的客户端,一看Radius几百页的协议,我头皮都发麻。放狗搜索一番,发现了一个实现好的Java的Radius客户端,哈哈,很好很强大:

http://sourceforge.net/projects/jradius-client/

摸索了一番,挖出了里面关于用户密码加密的一段代码,相信对很多想学习下Java MD5加密的同学会很有帮助。

首先来了解一番背景跟算法要求:

User-Password

描述

该属性表示用户用来认证的用户密码,或者是用户在收到接入挑战报文后用户的输入。它只出现在接入请求报文中。

在传输的过程中,密码是隐藏起来的,首先,将密码用null(\0)填充到16的整数倍个字节长,然后对在请求认证字后面加上共享密钥的字节流进行MD5加密生成hash值,将该hash值和密码的第一个16字节进行异或,然后将结果放入User-Password属性的第一个16字节中。

如果密码长度超过16个字符,则对第一个异或值后面加上共享密钥的字节流进行MD5加密生成hash值。该hash值和密码的第二个16个字节进行异或,然后将异或值放入User-Password属性的第二个16字节中。

如果必要,重复这个操作,每一个异或值后面加上共享密钥产生下一个hash值,然后和密码下一段的16字节进行异或。密码最长不能超过128个字符。

这个方法引用自Kaufman, Perlman和Speciner合写的《网络安全》一书,在第109到第110页。该方法的一个更加精确的解释如下:

共享密钥为S,伪随机数Request Authenticator为RA,将密码拆分成多个16个字节的块p1,p2,等等。最后一块用null(\0)填充满16个字节,密码块为c(1),c(2),等等。中间值为b1,b2,等等。

b1 = MD5(S + RA) c(1) = p1 xor b1
b2 = MD5(S + c(1)) c(2) = p2 xor b2
. .
. .
. .
bi = MD5(S + c(i-1)) c(i) = pi xor bi
User-Password属性字符串为c(1)+c(2)+…c(i),+表示连接。
一旦接收到报文,逆向处理就能得到密码明文。

User-Password属性的格式如下所示。各个域是按照自左向右的顺序传输的。

    0              1               2
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
   |     Type      |    Length     |  String ...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

类型

2               代表用户密码

长度

至少18字节,但不能超过130个字节

字符串

本字符串占位16到128(包含)个字节。

Java RADIUS Client的密码生成实现

//生成请求的Authenticator

byte [] requestAuthenticator = this.makeRFC2865RequestAuthenticator();

//Authenticator的生成
private byte[] makeRFC2865RequestAuthenticator() {
 byte [] requestAuthenticator = new byte[16];

 Random r = new Random();

 for (int i = 0; i < 16; i++)
 {
 requestAuthenticator[i] = (byte) r.nextInt();
 }

 this.md5MessageDigest.reset();
 this.md5MessageDigest.update(this.sharedSecret.getBytes());//sharedSecret指的是服务器端与客户端的共享密钥
 this.md5MessageDigest.update(requestAuthenticator);

 return this.md5MessageDigest.digest();//返回生成的hash值
 }
//生成加密后的密钥,userPass指未加密的密码
byte [] encryptedPass = this.encodePapPassword(userPass, requestAuthenticator);

//用来加密的算法
	private byte[] encodePapPassword(final byte[] userPass,
			final byte[] requestAuthenticator) {
		// encrypt the password.
		byte[] userPassBytes = null;
//密码必须大于16小于或者等于128bytes,如果不是16的整数倍个字节长,不足的位补0。如果大于128位,则截断成128位。

		if (userPass.length > 128) {
			userPassBytes = new byte[128];
			System.arraycopy(userPass, 0, userPassBytes, 0, 128);
		} else {
			userPassBytes = userPass;
		}
		// declare the byte array to hold the final product
		byte[] encryptedPass = null;

		if (userPassBytes.length < 128) {
			if (userPassBytes.length % 16 == 0) {
				 // 如果是16的整数倍长
				encryptedPass = new byte[userPassBytes.length];
			} else {
				// 数组长度变成16位的整数倍长
				encryptedPass = new byte[((userPassBytes.length / 16) * 16) + 16];
			}
		} else {
			// the encrypted password must be between 16 and 128 bytes
			encryptedPass = new byte[128];
		}

		 // 填充0
		System.arraycopy(userPassBytes, 0, encryptedPass, 0,
				userPassBytes.length);
		for (int i = userPassBytes.length; i < encryptedPass.length; i++) {
			encryptedPass[i] = 0; // fill it out with zeroes
		}
		this.md5MessageDigest.reset();
		 // 添加共享密钥
		this.md5MessageDigest.update(this.sharedSecret.getBytes());
		 // 添加请求Authenticator
		this.md5MessageDigest.update(requestAuthenticator);
		//  //获得md5 hash( b1 = MD5(S + RA) ).
		byte bn[] = this.md5MessageDigest.digest();

		for (int i = 0; i < 16; i++) {
			// perform the XOR as specified by RFC 2865.
			encryptedPass[i] = (byte) (bn[i] ^ encryptedPass[i]);
		}

		if (encryptedPass.length > 16) {
			for (int i = 16; i < encryptedPass.length; i += 16) {
				this.md5MessageDigest.reset();
				// add the shared secret
				this.md5MessageDigest.update(this.sharedSecret.getBytes());
				// add the previous(encrypted) 16 bytes of the user password
				this.md5MessageDigest.update(encryptedPass, i - 16, 16);
				// get the md5 hash( bn = MD5(S + c(i-1)) ).
				bn = this.md5MessageDigest.digest();
				for (int j = 0; j < 16; j++) {
					// perform the XOR as specified by RFC 2865.
					encryptedPass[i + j] = (byte) (bn[j] ^ encryptedPass[i + j]);
				}
			}
		}
		return encryptedPass;
	}
标签: ,

1个评论

  1. goldant - 2009年11月10日 3:46 下午

    呃,我在进行radius底层开发…..

发表评论