### Wang Donghui

a long-time Jenkins user and contributor

# 简析 Jenkins 专有用户数据库加密算法

本文对 Jenkins 专有用户数据库加密算法进行简要分析

### 认识Jenkins专有用户数据库

Jenkins 访问控制分为：安全域（即认证）与授权策略。

其中，安全域可以采用三种形式，分别为：Jenkins 专有用户数据库、LDAP、Servlet 容器代理。

### 在哪里看到加密后的用户密码信息？

Jenkins 专有用户的数据信息存放位置：$JENKINS_HOME/users/

每个用户的相关信息存放在各自的 config.xml 文件中： $JENKINS_HOME/users/$user/config.xml

在 config.xml 文件中的 passwordHash 节点可以看到用户密码加密后的密文哈希值：

### 用户密码是用什么算法加密的呢？

那么问题来了，用户密码是用何种加密方式加密的呢？可否通过解密密文得到明文呢？

在 GitHub 上查看其源码，通过关键字 #jbcrypt 搜索定位到 HudsonPrivateSecurityRealm.java 这个文件。 HudsonPrivateSecurityRealm.java 具体路径是：jenkins/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java

源码片段如下：

        /**
         * {@link PasswordEncoder} that uses jBCrypt.
         */
        private static final PasswordEncoder JBCRYPT_ENCODER = new PasswordEncoder() {
            public String encodePassword(String rawPass, Object _) throws DataAccessException {
                return BCrypt.hashpw(rawPass,BCrypt.gensalt());
            }

            public boolean isPasswordValid(String encPass, String rawPass, Object _) throws DataAccessException {
                return BCrypt.checkpw(rawPass,encPass);
            }
        };
        /**
         * Combines {@link #JBCRYPT_ENCODER} and {@link #CLASSIC} into one so that we can continue
         * to accept {@link #CLASSIC} format but new encoding will always done via {@link #JBCRYPT_ENCODER}.
         */
        public static final PasswordEncoder PASSWORD_ENCODER = new PasswordEncoder() {
            /*
                CLASSIC encoder outputs "salt:hash" where salt is [a-z]+, so we use unique prefix '#jbcyrpt"
                to designate JBCRYPT-format hash.
                '#' is neither in base64 nor hex, which makes it a good choice.
             */
            public String encodePassword(String rawPass, Object salt) throws DataAccessException {
                return JBCRYPT_HEADER+JBCRYPT_ENCODER.encodePassword(rawPass,salt);
            }

            public boolean isPasswordValid(String encPass, String rawPass, Object salt) throws DataAccessException {
                if (encPass.startsWith(JBCRYPT_HEADER))
                    return JBCRYPT_ENCODER.isPasswordValid(encPass.substring(JBCRYPT_HEADER.length()),rawPass,salt);
                else
                    return CLASSIC.isPasswordValid(encPass,rawPass,salt);
            }

            private static final String JBCRYPT_HEADER = "#jbcrypt:";
        };

通过分析该源码得知： 1. 明文通过 jbcrypt 算法得到密文 encPass 2. 密文的格式为：salt: encPass，其中以 #jbcrypt 表示 salt 作为数据头

### jbcrypt 是什么？

jbcrypt 是 bcrypt 加密工具的 java 实现。 它的 API 非常简单，DEMO 如下，在 HudsonPrivateSecurityRealm.java 中可以看到加密和校验时使用了如下 API：

    // Hash a password for the first time
    String hashed = BCrypt.hashpw(password, BCrypt.gensalt());

    // gensalt's log_rounds parameter determines the complexity the work factor is 2**log_rounds, and the default is 10
    String hashed = BCrypt.hashpw(password, BCrypt.gensalt(12));

    // Check that an unencrypted password matches one that has previously been hashed
    if (BCrypt.checkpw(candidate, hashed))
      System.out.println("It matches");
    else
      System.out.println("It does not match");

经验证，用 jbcrypt 对同一个明文加密后因为 salt 一般不同，加密后的密文一般不同。

### bcrypt 精要概况

1. bcrypt 是不可逆的加密算法，无法通过解密密文得到明文。
2. bcrypt 和其他对称或非对称加密方式不同的是，不是直接解密得到明文，也不是二次加密比较密文，而是把明文和存储的密文一块运算得到另一个密文，如果这两个密文相同则验证成功。

### 总结

综上， Jenkins 专有用户数据库使用了 jbcrypt 加密， jbcrypt 加密是不可逆的，而且对于同一个明文的加密结果一般不同。

######
“简析 Jenkins 专有用户数据库加密算法” 更新于：April 12, 2019
原文作者：donhui

改善此页

- Tags:
- jenkins
- Jenkins 2.176~2.178版本更新
- Related entries:
- 如何对 Jenkins 共享库进行单元测试
- 基于 Jenkins + JaCoCo 实现功能测试代码覆盖率统计
- 使用 Jenkins + Ansible 实现 Spring Boot 自动化部署101
- 从 Jenkins 到 Jenkins X
- 19年 GSoC 中 Jenkins 的七个项目
- 基于 Jenkins 的 DevOps 平台应该如何设计凭证管理
- Jenkins 公众号送书福利
- Jenkins 版本发布
- Jenkins 插件开发之旅：两天内从 idea 到发布(下篇)
- Jenkins 自动化安装插件
- Jenkins 插件开发之旅：两天内从 idea 到发布(上篇)
- 使用 Jenkins X 渐进式交付：自动化金丝雀部署
- 使用 Jenkins X 渐进式交付
- 使用 Jenkins + Ansible 实现自动化部署 Nginx
- 关于 Jenkins master 共享 JENKINS_HOME 目录的实验
- 持续交付的商业价值
- 使用 Zabbix 监控 Jenkins
- Java 应用使用 Docker 的入门指南：建立一个 CI/CD 流水线
- 介绍：成为一名 Jenkins 贡献者的旅程
- Electron 应用的流水线设计
- 为 Continuous Delivery Foundation 的成立感到兴奋
- MPL - 模块化的流水线库
- 批量修改 Jenkins 任务的技巧
- 在安全防火墙内通过 WebHook 触发构建