RADIUS 库未正确加密或编码密码字符串
RADIUS Library not encrypting or encoding password string correctly
我目前正在从事一个项目,涉及使用 Java 和 Python 对 RADIUS 服务器进行直接身份验证。
我的 Python 实现工作正常,但是我已经尝试了两个 Java
库(TinyRADIUS 和 JRaD),两者表现出相同的行为:
当发送 PAP 请求时,密码中前 16 个之后的所有字符似乎都变成了乱码或半 UTF 编码,导致服务器无法通过身份验证。
见下文:
JRaDclient rc = new JRaDclient();
rc.setSecretKey("secrets");
rc.setServer("127.0.0.1", 1812);
rc.authenticate("testuser", "pass123LogeuCcoboJKidoDeVFesekSoluneCgaLttrjhrkrhn");
然而,服务器上的结果请求变成了这个
rad_recv: Access-Request packet from host 127.0.0.1 port 1337, id=210, length=96
User-Name = "testuser"
User-Password = "pass123LogeuCcob53?302A13mP63P[h4zL21/3[=11=]14233BuKM2\t31r135
673+W'b1"
这是库同时出现的问题还是 Radius 的 configuration/version 问题?
作为参考,这是 Java 8 上的 运行,使用 FreeRADIUS v2.1.12。
JRaD:https://github.com/punyal/JRaD
微小半径:http://tinyradius.sourceforge.net/
当密码超过 16 个字符时会出现问题 - 密码编码填充密码然后对其进行加密,这是不正确的。
我们在项目中包含了源代码,并替换了 pap 密码编码和 pap 密码解码方法(本质上,在填充之前加密密码,而不是用填充加密)。
基于此:http://sourceforge.net/p/tinyradius/discussion/461293/thread/dcedb4ed/
这是修改后的相关代码:
/**
* This method encodes the plaintext user password according to RFC 2865.
*
* @param userPass
* the password to encrypt
* @param sharedSecret
* shared secret
* @return the byte array containing the encrypted password
*/
private byte[] encodePapPassword(final byte[] userPass, byte[] sharedSecret) {
// the password must be a multiple of 16 bytes and less than or equal
// to 128 bytes. If it isn't a multiple of 16 bytes fill it out with zeroes
// to make it a multiple of 16 bytes. If it is greater than 128 bytes
// truncate it at 128.
if (userPass == null) {
throw new IllegalArgumentException("password is null");
} else if (sharedSecret == null) {
throw new IllegalArgumentException("shared secret is null");
} else if (getAuthenticator() == null) {
throw new IllegalArgumentException("authenticator is null");
}
byte[] userPassBytes = null;
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) {
// tt is already a multiple of 16 bytes
encryptedPass = new byte[userPassBytes.length];
} else {
// make it a multiple of 16 bytes
encryptedPass = new byte[((userPassBytes.length / 16) * 16) + 16];
}
} else {
// the encrypted password must be between 16 and 128 bytes
encryptedPass = new byte[128];
}
// copy the userPass into the encrypted pass and then fill it out with zeroes
System.arraycopy(userPassBytes, 0, encryptedPass, 0, userPassBytes.length);
for (int i = userPassBytes.length; i < encryptedPass.length; i++) {
encryptedPass[i] = 0;
}
// digest shared secret and authenticator
MessageDigest md5 = getMd5Digest();
md5.reset();
byte[] bn = new byte[sharedSecret.length + getAuthenticator().length];
for (int i = 0; i < bn.length; i++) {
if (i < sharedSecret.length)
bn[i] = sharedSecret[i];
else
bn[i] = getAuthenticator()[i - sharedSecret.length];
}
md5.update(bn);
bn = md5.digest();
// perform the XOR as specified by RFC 2865
for (int i = 0; i < 16; i++) {
encryptedPass[i] = (byte) (bn[i] ^ encryptedPass[i]);
}
if (encryptedPass.length > 16) {
for (int i = 16; i < encryptedPass.length; i += 16) {
md5.reset();
md5.update(sharedSecret);
// add the previous (encrypted) 16 bytes of the user password
md5.update(encryptedPass, i - 16, 16);
bn = md5.digest();
// perform the XOR as specified by RFC 2865.
for (int j = 0; j < 16; j++) {
encryptedPass[i + j] = (byte) (bn[j] ^ encryptedPass[i + j]);
}
}
}
return encryptedPass;
}
/**
* Decodes the passed encrypted password and returns the clear-text form.
*
* @param encryptedPass
* encrypted password
* @param sharedSecret
* shared secret
* @return decrypted password
*/
private String decodePapPassword(byte[] encryptedPass, byte[] sharedSecret) throws RadiusException {
if (encryptedPass == null) {
throw new IllegalArgumentException("encrypted password is null");
} else if (sharedSecret == null) {
throw new IllegalArgumentException("shared secret is null");
} else if (getAuthenticator() == null) {
throw new IllegalArgumentException("authenticator is null");
} else if (encryptedPass.length < 16) {
throw new IllegalArgumentException("invalid length of encrypted pass: " + encryptedPass.length);
}
// save original (encoded) encrypted pass
byte[] encryptedPassOrig = encryptedPass.clone();
// MessageDigest md5;
MessageDigest md5 = getMd5Digest();
md5.reset();
md5.update(sharedSecret);
md5.update(getAuthenticator());
byte bn[] = md5.digest();
// perform the XOR as specified by RFC 2865
for (int i = 0; i < 16; i++) {
encryptedPass[i] = (byte) (bn[i] ^ encryptedPass[i]);
}
if (encryptedPass.length > 16) {
for (int i = 16; i < encryptedPass.length; i += 16) {
md5.reset();
md5.update(sharedSecret);
// now use original (encoded) pass to generate MD5
md5.update(encryptedPassOrig, i - 16, 16);
bn = md5.digest();
// perform the XOR as specified by RFC 2865.
for (int j = 0; j < 16; j++) {
encryptedPass[i + j] = (byte) (bn[j] ^ encryptedPass[i + j]);
}
}
}
// remove trailing zeros
int len = encryptedPass.length;
while (len > 0 && encryptedPass[len - 1] == 0) {
len--;
}
byte[] passtrunc = new byte[len];
System.arraycopy(encryptedPass, 0, passtrunc, 0, len);
// convert to string
return RadiusUtil.getStringFromUtf8(passtrunc);
}
我目前正在从事一个项目,涉及使用 Java 和 Python 对 RADIUS 服务器进行直接身份验证。
我的 Python 实现工作正常,但是我已经尝试了两个 Java 库(TinyRADIUS 和 JRaD),两者表现出相同的行为:
当发送 PAP 请求时,密码中前 16 个之后的所有字符似乎都变成了乱码或半 UTF 编码,导致服务器无法通过身份验证。
见下文:
JRaDclient rc = new JRaDclient();
rc.setSecretKey("secrets");
rc.setServer("127.0.0.1", 1812);
rc.authenticate("testuser", "pass123LogeuCcoboJKidoDeVFesekSoluneCgaLttrjhrkrhn");
然而,服务器上的结果请求变成了这个
rad_recv: Access-Request packet from host 127.0.0.1 port 1337, id=210, length=96
User-Name = "testuser"
User-Password = "pass123LogeuCcob53?302A13mP63P[h4zL21/3[=11=]14233BuKM2\t31r135
673+W'b1"
这是库同时出现的问题还是 Radius 的 configuration/version 问题?
作为参考,这是 Java 8 上的 运行,使用 FreeRADIUS v2.1.12。
JRaD:https://github.com/punyal/JRaD 微小半径:http://tinyradius.sourceforge.net/
当密码超过 16 个字符时会出现问题 - 密码编码填充密码然后对其进行加密,这是不正确的。
我们在项目中包含了源代码,并替换了 pap 密码编码和 pap 密码解码方法(本质上,在填充之前加密密码,而不是用填充加密)。
基于此:http://sourceforge.net/p/tinyradius/discussion/461293/thread/dcedb4ed/
这是修改后的相关代码:
/**
* This method encodes the plaintext user password according to RFC 2865.
*
* @param userPass
* the password to encrypt
* @param sharedSecret
* shared secret
* @return the byte array containing the encrypted password
*/
private byte[] encodePapPassword(final byte[] userPass, byte[] sharedSecret) {
// the password must be a multiple of 16 bytes and less than or equal
// to 128 bytes. If it isn't a multiple of 16 bytes fill it out with zeroes
// to make it a multiple of 16 bytes. If it is greater than 128 bytes
// truncate it at 128.
if (userPass == null) {
throw new IllegalArgumentException("password is null");
} else if (sharedSecret == null) {
throw new IllegalArgumentException("shared secret is null");
} else if (getAuthenticator() == null) {
throw new IllegalArgumentException("authenticator is null");
}
byte[] userPassBytes = null;
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) {
// tt is already a multiple of 16 bytes
encryptedPass = new byte[userPassBytes.length];
} else {
// make it a multiple of 16 bytes
encryptedPass = new byte[((userPassBytes.length / 16) * 16) + 16];
}
} else {
// the encrypted password must be between 16 and 128 bytes
encryptedPass = new byte[128];
}
// copy the userPass into the encrypted pass and then fill it out with zeroes
System.arraycopy(userPassBytes, 0, encryptedPass, 0, userPassBytes.length);
for (int i = userPassBytes.length; i < encryptedPass.length; i++) {
encryptedPass[i] = 0;
}
// digest shared secret and authenticator
MessageDigest md5 = getMd5Digest();
md5.reset();
byte[] bn = new byte[sharedSecret.length + getAuthenticator().length];
for (int i = 0; i < bn.length; i++) {
if (i < sharedSecret.length)
bn[i] = sharedSecret[i];
else
bn[i] = getAuthenticator()[i - sharedSecret.length];
}
md5.update(bn);
bn = md5.digest();
// perform the XOR as specified by RFC 2865
for (int i = 0; i < 16; i++) {
encryptedPass[i] = (byte) (bn[i] ^ encryptedPass[i]);
}
if (encryptedPass.length > 16) {
for (int i = 16; i < encryptedPass.length; i += 16) {
md5.reset();
md5.update(sharedSecret);
// add the previous (encrypted) 16 bytes of the user password
md5.update(encryptedPass, i - 16, 16);
bn = md5.digest();
// perform the XOR as specified by RFC 2865.
for (int j = 0; j < 16; j++) {
encryptedPass[i + j] = (byte) (bn[j] ^ encryptedPass[i + j]);
}
}
}
return encryptedPass;
}
/**
* Decodes the passed encrypted password and returns the clear-text form.
*
* @param encryptedPass
* encrypted password
* @param sharedSecret
* shared secret
* @return decrypted password
*/
private String decodePapPassword(byte[] encryptedPass, byte[] sharedSecret) throws RadiusException {
if (encryptedPass == null) {
throw new IllegalArgumentException("encrypted password is null");
} else if (sharedSecret == null) {
throw new IllegalArgumentException("shared secret is null");
} else if (getAuthenticator() == null) {
throw new IllegalArgumentException("authenticator is null");
} else if (encryptedPass.length < 16) {
throw new IllegalArgumentException("invalid length of encrypted pass: " + encryptedPass.length);
}
// save original (encoded) encrypted pass
byte[] encryptedPassOrig = encryptedPass.clone();
// MessageDigest md5;
MessageDigest md5 = getMd5Digest();
md5.reset();
md5.update(sharedSecret);
md5.update(getAuthenticator());
byte bn[] = md5.digest();
// perform the XOR as specified by RFC 2865
for (int i = 0; i < 16; i++) {
encryptedPass[i] = (byte) (bn[i] ^ encryptedPass[i]);
}
if (encryptedPass.length > 16) {
for (int i = 16; i < encryptedPass.length; i += 16) {
md5.reset();
md5.update(sharedSecret);
// now use original (encoded) pass to generate MD5
md5.update(encryptedPassOrig, i - 16, 16);
bn = md5.digest();
// perform the XOR as specified by RFC 2865.
for (int j = 0; j < 16; j++) {
encryptedPass[i + j] = (byte) (bn[j] ^ encryptedPass[i + j]);
}
}
}
// remove trailing zeros
int len = encryptedPass.length;
while (len > 0 && encryptedPass[len - 1] == 0) {
len--;
}
byte[] passtrunc = new byte[len];
System.arraycopy(encryptedPass, 0, passtrunc, 0, len);
// convert to string
return RadiusUtil.getStringFromUtf8(passtrunc);
}