簡析 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:"; };
通過分析該原始碼得知:
明文通過 jbcrypt 演算法得到密文 encPass
密文的格式為: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 精要概況
bcrypt 是不可逆的加密演算法,無法通過解密密文得到明文。
bcrypt 和其他對稱或非對稱加密方式不同的是,不是直接解密得到明文,也不是二次加密比較密文,而是把明文和儲存的密文一塊運算得到另一個密文,如果這兩個密文相同則驗證成功。
總結
綜上, Jenkins 專有使用者資料庫使用了 jbcrypt 加密, jbcrypt 加密是不可逆的,而且對於同一個明文的加密結果一般不同。