java
import java.util.Properties;
/**
* Password obfuscate utility class. Lifted from Jetty org.mortbay.jetty.security.Password
*
* <p>Passwords that begin with OBF: are de obfuscated.</p>
*
* <p>Passwords can be obfuscated by running Obfuscate as a main class. Obfuscated password are required if a system needs
* to recover the full password (eg. so that it may be passed to another system).</p>
*
* <p>They are not secure, but prevent casual observation.</p>
*
* @see <a
* href="http://grepcode.com/file_/repo1.maven.org/maven2/org.mortbay.jetty/jetty/6.1.11/org/mortbay/jetty/security/Password.java/?v=source"
* >Jetty Source org.mortbay.jetty.security.Password</a>
* @since 2.0
*/
public class PasswordUtil
{
public static final String __OBFUSCATE = "OBF:";
/* ------------------------------------------------------------ */
public static String obfuscate( String s )
{
StringBuilder buf = new StringBuilder();
byte[] b = s.getBytes();
buf.append( __OBFUSCATE );
for ( int i = 0; i < b.length; i++ )
{
byte b1 = b[i];
byte b2 = b[s.length() - ( i + 1 )];
int i1 = 127 + b1 + b2;
int i2 = 127 + b1 - b2;
int i0 = i1 * 256 + i2;
String x = Integer.toString( i0, 36 );
switch ( x.length() )
{
case 1:
buf.append( '0' );
case 2:
buf.append( '0' );
case 3:
buf.append( '0' );
default:
buf.append( x );
}
}
return buf.toString();
}
/* ------------------------------------------------------------ */
public static String deobfuscate( String s )
{
if ( s.startsWith( __OBFUSCATE ) )
{
s = s.substring( __OBFUSCATE.length() );
byte[] b = new byte[s.length() / 2];
int l = 0;
for ( int i = 0; i < s.length(); i += 4 )
{
String x = s.substring( i, i + 4 );
int i0 = Integer.parseInt( x, 36 );
int i1 = ( i0 / 256 );
int i2 = ( i0 % 256 );
b[l++] = (byte) ( ( i1 + i2 - 254 ) / 2 );
}
return new String( b, 0, l );
}
else
{
return s;
}
}
public static void deobfuscateSystemProps()
{
Properties props = System.getProperties();
for ( Object obj : props.keySet() )
{
if ( obj instanceof String )
{
String key = (String) obj;
String value = props.getProperty( key );
if ( value != null && value.startsWith( __OBFUSCATE ) )
{
System.setProperty( key, deobfuscate( value ) );
}
}
}
}
public static void main( String[] args )
{
if ( args[0].startsWith( __OBFUSCATE ) )
{
System.out.println( PasswordUtil.deobfuscate( args[0] ) );
}
else
{
System.out.println( PasswordUtil.obfuscate( args[0] ) );
}
}
}
这段代码是 PasswordUtil 类,它是从 Jetty 6 的安全模块 (org.mortbay.jetty.security.Password)中"提取"(lifted)出来的一个密码混淆(obfuscation)工具类 。它的核心目的不是加密(encryption),而是防止密码被"一眼看到" ------ 即 防 casual observation(防随意窥视)。
下面我将从 用途、原理、安全性、使用方式 四个维度帮你彻底理解。
🔍 一、用途:为什么需要它?
在配置文件(如 tomcat-users.xml、Maven settings.xml、系统属性等)中,有时需要写明用户名和密码:
xml
<!-- Maven settings.xml -->
<server>
<id>my-tomcat</id>
<username>admin</username>
<password>secret123</password> <!-- 明文!危险! -->
</server>
虽然这不是真正的"安全存储",但如果你把密码写成:
xml
<password>OBF:1v2j1uum1xtv1uvk1v0h1v2j</password>
那么:
- 普通人看不出这是什么密码
- 但程序在运行时能自动还原成明文(因为 Tomcat 或插件会调用
deobfuscate) - 适用于那些必须传递明文密码给其他系统的场景(比如 Tomcat Manager 部署)
✅ 所以文档注释说:
"Obfuscated passwords are required if a system needs to recover the full password (e.g. so that it may be passed to another system)."
⚙️ 二、工作原理:如何混淆/还原?
1. 混淆(obfuscate)算法
对字符串 s,从两端向中间配对字符:
- 第 i 个字符:
b1 = s[i] - 对称位置字符:
b2 = s[len - 1 - i]
然后计算两个整数:
i1 = 127 + b1 + b2i2 = 127 + b1 - b2
合并成一个 16 位整数:i0 = i1 * 256 + i2
再将 i0 转为 36 进制字符串(用 0-9 + a-z 表示),并补零到 4 位。
最后拼接所有 4 位块,加上前缀 "OBF:"。
示例:
java
PasswordUtil.obfuscate("admin")
// 输出:OBF:1v2j1uum1xtv1uvk1v0h1v2j
2. 还原(deobfuscate)算法
- 去掉
"OBF:"前缀 - 每 4 个字符一组,转回十进制
i0 - 分解出
i1 = i0 / 256,i2 = i0 % 256 - 计算原始字节:
b = (i1 + i2 - 254) / 2 - 拼成字符串
🔁 这是一个可逆的、确定性的变换,不是哈希,也不是加密。
🛡️ 三、安全性:它安全吗?
不安全!绝对不要用于真正敏感的场景!
- ❌ 不是加密:没有密钥,算法公开,任何人都能还原
- ❌ 不是哈希:可以反向计算出明文
- ✅ 只是"混淆"(Obfuscation):让密码看起来不像密码
📌 官方注释明确说:
"They are not secure, but prevent casual observation."(它们不安全,但能防止随意观察。)
适用场景:
- 开发环境配置
- 内部测试服务器
- 自动化脚本中的临时凭证
- 不能用于生产环境的高敏感密码!
🧪 四、如何使用?
1. 命令行工具
bash
# 混淆密码
java PasswordUtil admin
# 输出:OBF:1v2j1uum1xtv1uvk1v0h1v2j
# 还原密码
java PasswordUtil OBF:1v2j1uum1xtv1uvk1v0h1v2j
# 输出:admin
💡 注意:
main方法逻辑有 bug!应该是args[0],不是args[1]。正确写法:
javapublic static void main(String[] args) { if (args.length == 0) return; String input = args[0]; if (input.startsWith(__OBFUSCATE)) { System.out.println(deobfuscate(input)); } else { System.out.println(obfuscate(input)); } }
2. 在系统属性中自动解混淆
java
PasswordUtil.deobfuscateSystemProps();
→ 遍历所有 -Dkey=value 系统属性,如果 value 以 OBF: 开头,就自动替换成明文。
例如启动 Java 应用:
bash
java -Dtomcat.password=OBF:1v2j... MyApp
程序内调用 deobfuscateSystemProps() 后,System.getProperty("tomcat.password") 就是 "admin"。
3. 在 Tomcat Maven Plugin 中使用
你可以在 pom.xml 或 settings.xml 中写混淆密码:
xml
<!-- settings.xml -->
<server>
<id>tomcat</id>
<username>admin</username>
<password>OBF:1v2j1uum1xtv1uvk1v0h1v2j</password>
</server>
Tomcat Maven Plugin 在内部会自动调用 deobfuscate 来还原密码,用于连接远程 Tomcat Manager。
✅ 总结:关键要点
| 项目 | 说明 |
|---|---|
| 本质 | 密码混淆(Obfuscation),非加密 |
| 目的 | 防止密码被"一眼看到",提升基本安全性 |
| 安全性 | 低!仅防 casual observation,不能防攻击 |
| 可逆性 | 完全可逆,算法公开 |
| 典型用途 | Tomcat 用户密码、Maven server 密码、系统属性 |
| 前缀标识 | OBF:(代表 Obfuscated) |
| 来源 | 源自 Jetty 6,被 Tomcat Maven Plugin 引入 |
💡 最佳实践建议:
- 开发/测试环境可用
- 生产环境应使用 真正的密钥管理(如 Vault、KMS、环境变量 + 权限控制)
- 永远不要认为
OBF:是"加密"
这个工具的价值在于 平衡便利性与最低限度的隐私保护,而不是提供安全保障。