# Spring Boot + Jasypt:同時使用兩組加解密設定(舊/新)實作指南
> 語言:繁體中文
> 目的:同一個 Spring Boot 專案中,同時支援 **舊**(`PBEWithMD5AndDES`、無 IV)與 **新**(`PBEWithHmacSHA512AndAES_256`、隨機 IV)兩套 Jasypt 設定。舊組態僅做相容;新資料一律用 AES-256。
---
## 目標
1. 程式碼中可以選擇使用「舊」或「新」加解密。
2. `application.yml` 等組態可同時支援兩種加密字串(便於平滑換代)。
---
## 實作總覽(兩層)
- **A. 程式碼手動加解密**:宣告兩個 `StringEncryptor` Bean,使用 `@Qualifier` 精準注入。
- **B. 組態自動解密**:提供「路由型」解密(支援 `ENC_NEW(...)`、`ENC_OLD(...)` 與相容用 `ENC(...)`)。
---
## A. 程式碼中手動加解密(兩個 Encryptor Bean)
`JasyptEncryptorsConfig.java`:
```java
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JasyptEncryptorsConfig {
// 舊:僅為相容(弱加密,無 IV)
@Bean("legacyEncryptor")
public StringEncryptor legacyEncryptor(
@Value("${jasypt.encryptor.password}") String legacyPassword) {
PooledPBEStringEncryptor enc = new PooledPBEStringEncryptor();
SimpleStringPBEConfig cfg = new SimpleStringPBEConfig();
cfg.setPassword(legacyPassword);
cfg.setAlgorithm("PBEWithMD5AndDES");
cfg.setIvGeneratorClassName("org.jasypt.iv.NoIvGenerator");
cfg.setPoolSize("1");
cfg.setStringOutputType("base64");
enc.setConfig(cfg);
return enc;
}
// 新:協力商 AES-256 版本(安全組態)
@Bean("encryptorBean")
public StringEncryptor encryptorBean() {
String p1 = System.getProperty("P_ENV_1");
String p2 = System.getProperty("P_ENV_2");
if (p1 == null || p1.isBlank() || "null".equalsIgnoreCase(p1)
|| p2 == null || p2.isBlank() || "null".equalsIgnoreCase(p2)) {
p1 = System.getenv("P_ENV_1");
p2 = System.getenv("P_ENV_2");
}
String salt = (p1 == null ? "" : p1) + (p2 == null ? "" : p2);
PooledPBEStringEncryptor enc = new PooledPBEStringEncryptor();
SimpleStringPBEConfig cfg = new SimpleStringPBEConfig();
cfg.setPassword(salt);
cfg.setAlgorithm("PBEWithHmacSHA512AndAES_256");
cfg.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
cfg.setKeyObtentionIterations("1000");
cfg.setPoolSize("1");
cfg.setProviderName("SunJCE");
cfg.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
cfg.setStringOutputType("base64");
enc.setConfig(cfg);
return enc;
}
}
```
`CryptoService.java`:
```java
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class CryptoService {
private final StringEncryptor legacy;
private final StringEncryptor modern;
public CryptoService(@Qualifier("legacyEncryptor") StringEncryptor legacy,
@Qualifier("encryptorBean") StringEncryptor modern) {
this.legacy = legacy;
this.modern = modern;
}
public String encryptNew(String plain) { return modern.encrypt(plain); }
public String decryptNew(String cipher) { return modern.decrypt(cipher); }
public String decryptLegacy(String cipher) { return legacy.decrypt(cipher); }
// 簡易自動判斷:先試新,不行再回退舊
public String smartDecrypt(String cipher) {
try { return modern.decrypt(cipher); }
catch (Exception ignore) { /* fall through */ }
return legacy.decrypt(cipher);
}
}
```
---
## B. 組態層的自動解密(Routing + 多前綴)
**為何需要?** `ulisesbocchio/jasypt-spring-boot` 預設一次只會用一個 `jasypt.encryptor.bean` 去解 `ENC(...)`。若要同時支援兩種加密格式,建議引入自訂「偵測器/解析器」或「路由 Encryptor」,用**前綴**切換:
- `ENC_NEW(...)` → 用 **新** encryptor(AES-256)。
- `ENC_OLD(...)` → 用 **舊** encryptor(MD5+DES)。
- `ENC(...)` → 相容保留:先試新、再回退舊。
`JasyptRoutingConfig.java`:
```java
import com.ulisesbocchio.jasyptspringboot.detector.EncryptablePropertyDetector;
import com.ulisesbocchio.jasyptspringboot.resolver.EncryptablePropertyResolver;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JasyptRoutingConfig {
@Bean("routingEncryptor")
public StringEncryptor routingEncryptor(
@Qualifier("legacyEncryptor") StringEncryptor legacy,
@Qualifier("encryptorBean") StringEncryptor modern) {
// 供需要時直接注入;Resolver 也會自行分流
return new StringEncryptor() {
@Override public String encrypt(String message) { return modern.encrypt(message); }
@Override public String decrypt(String encryptedMessage) {
try { return modern.decrypt(encryptedMessage); }
catch (Exception ignore) {}
return legacy.decrypt(encryptedMessage);
}
};
}
@Bean
public EncryptablePropertyDetector encryptablePropertyDetector() {
return value -> value != null &&
(value.startsWith("ENC_NEW(") || value.startsWith("ENC_OLD(") || value.startsWith("ENC("));
}
@Bean
public EncryptablePropertyResolver encryptablePropertyResolver(
@Qualifier("legacyEncryptor") StringEncryptor legacy,
@Qualifier("encryptorBean") StringEncryptor modern) {
return value -> {
if (value == null) return null;
if (value.startsWith("ENC_NEW(") && value.endsWith(")")) {
String body = value.substring("ENC_NEW(".length(), value.length() - 1);
return modern.decrypt(body);
}
if (value.startsWith("ENC_OLD(") && value.endsWith(")")) {
String body = value.substring("ENC_OLD(".length(), value.length() - 1);
return legacy.decrypt(body);
}
if (value.startsWith("ENC(") && value.endsWith(")")) {
String body = value.substring("ENC(".length(), value.length() - 1);
try { return modern.decrypt(body); }
catch (Exception ignore) { return legacy.decrypt(body); }
}
return value;
};
}
}
```
`application.yml`:
```yaml
jasypt:
encryptor:
bean: routingEncryptor # 讓 Resolver/Detector 生效,並提供 routing 能力
spring:
datasource:
password: "ENC_NEW(.....)" # 新格式(AES-256)
legacy:
token: "ENC_OLD(.....)" # 舊格式(MD5+DES)
other:
secret: "ENC(.....)" # 歷史相容:未標示新舊者
```
---
## C. 產出與轉換策略(實務)
- **新資料一律用 AES-256(`encryptorBean`)產生**;避免再寫入 MD5+DES。
- 舊資料保留解密能力一段時間(`legacyEncryptor`),逐步轉檔或在讀取時即時以新格式重寫。
- 在組態中改用 `ENC_NEW(...)`;只在必要時保留 `ENC_OLD(...)`。
---
## D. 常見落坑
- 原始片段常見誤植(`sait` → `salt`、`setAlgorithm` 拼寫、全形符號),範例已修正。
- `PBEWithMD5AndDES` + `NoIvGenerator` 安全性很弱;僅限相容期使用。
- `P_ENV_1` / `P_ENV_2` 取自 **JVM 系統屬性** 或 **環境變數** 時,請處理空字串與 `"null"`。
- 舊 JDK 需注意 AES-256 的政策檔;現代 JDK 已預設開啟,但仍建議在所有環境驗證。
---
## E. 最小可用配置清單
1. 宣告 `@Bean("legacyEncryptor")` 與 `@Bean("encryptorBean")`。
2. 若組態要同時解兩種格式:加上自訂 `EncryptablePropertyDetector` / `EncryptablePropertyResolver`,支援 `ENC_NEW(...)`、`ENC_OLD(...)`、`ENC(...)`。
3. 程式碼層以 `@Qualifier` 指定使用哪一組 Encryptor。
---
## F. 簡易測試(產生/驗證密文)
`JasyptQuickTest.java`:
```java
public class JasyptQuickTest {
public static void main(String[] args) {
// 假裝已從 Spring 取出兩個 Bean:legacy 與 modern
var legacy = new org.jasypt.encryption.pbe.StandardPBEStringEncryptor();
legacy.setPassword("vu4wj/3"); // 舊密碼:來自 application 設定
legacy.setAlgorithm("PBEWithMD5AndDES");
var modern = new org.jasypt.encryption.pbe.StandardPBEStringEncryptor();
modern.setPassword(System.getenv("P_ENV_1") + System.getenv("P_ENV_2"));
modern.setAlgorithm("PBEWithHmacSHA512AndAES_256");
String plain = "s3cr3t-P@ss!";
String oldCipher = legacy.encrypt(plain);
String newCipher = modern.encrypt(plain);
System.out.println("ENC_OLD(" + oldCipher + ")");
System.out.println("ENC_NEW(" + newCipher + ")");
// 驗證
System.out.println("legacy -> " + legacy.decrypt(oldCipher));
System.out.println("modern -> " + modern.decrypt(newCipher));
}
}
```
> 提示:正式專案請以 Spring 取得 Bean 進行測試;此處為最小可行示意。
---
## 版控建議
- 先導入「路由」與雙 Encryptor,確保服務可同時解兩種格式。
- 撰寫資料轉換腳本(批次或讀取即轉),逐步消除 `ENC_OLD(...)`。
- 清查完畢後,**移除舊 Encryptor** 與 `ENC_OLD(...)` 支援,降低攻擊面。
---
## 授權與安全
- 本指南僅示範 Jasypt 用法,不包含第三方授權檔。
- 實務上請搭配祕密管理(如 KMS、Vault、Kubernetes Secret)取代硬編碼密鑰。
IyBTcHJpbmcgQm9vdCArIEphc3lwdO+8muWQjOaZguS9v+eUqOWFqee1hOWKoOino+WvhuioreWumu+8iOiIiu+8j+aWsO+8ieWvpuS9nOaMh+WNlwoKCgo+IOiqnuiogO+8mue5gemrlOS4reaWhyAgCgo+IOebrueahO+8muWQjOS4gOWAiyBTcHJpbmcgQm9vdCDlsIjmoYjkuK3vvIzlkIzmmYLmlK/mj7QgKiroiIoqKu+8iGBQQkVXaXRoTUQ1QW5kREVTYOOAgeeEoSBJVu+8ieiIhyAqKuaWsCoq77yIYFBCRVdpdGhIbWFjU0hBNTEyQW5kQUVTXzI1NmDjgIHpmqjmqZ8gSVbvvInlhanlpZcgSmFzeXB0IOioreWumuOAguiIiue1hOaFi+WDheWBmuebuOWuue+8m+aWsOizh+aWmeS4gOW+i+eUqCBBRVMtMjU244CCCgoKCi0tLQoKCgojIyDnm67mqJkKCjEuIOeoi+W8j+eivOS4reWPr+S7pemBuOaTh+S9v+eUqOOAjOiIiuOAjeaIluOAjOaWsOOAjeWKoOino+WvhuOAgiAgCgoyLiBgYXBwbGljYXRpb24ueW1sYCDnrYnntYTmhYvlj6/lkIzmmYLmlK/mj7TlhannqK7liqDlr4blrZfkuLLvvIjkvr/mlrzlubPmu5Hmj5vku6PvvInjgIIKCgoKLS0tCgoKCiMjIOWvpuS9nOe4veimve+8iOWFqeWxpO+8iQoKLSAqKkEuIOeoi+W8j+eivOaJi+WLleWKoOino+Wvhioq77ya5a6j5ZGK5YWp5YCLIGBTdHJpbmdFbmNyeXB0b3JgIEJlYW7vvIzkvb/nlKggYEBRdWFsaWZpZXJgIOeyvua6luazqOWFpeOAgiAgCgotICoqQi4g57WE5oWL6Ieq5YuV6Kej5a+GKirvvJrmj5DkvpvjgIzot6/nlLHlnovjgI3op6Plr4bvvIjmlK/mj7QgYEVOQ19ORVcoLi4uKWDjgIFgRU5DX09MRCguLi4pYCDoiIfnm7jlrrnnlKggYEVOQyguLi4pYO+8ieOAggoKCgotLS0KCgoKIyMgQS4g56iL5byP56K85Lit5omL5YuV5Yqg6Kej5a+G77yI5YWp5YCLIEVuY3J5cHRvciBCZWFu77yJCgoKCmBKYXN5cHRFbmNyeXB0b3JzQ29uZmlnLmphdmFg77yaCgoKCmBgYGphdmEKCmltcG9ydCBvcmcuamFzeXB0LmVuY3J5cHRpb24uU3RyaW5nRW5jcnlwdG9yOwoKaW1wb3J0IG9yZy5qYXN5cHQuZW5jcnlwdGlvbi5wYmUuUG9vbGVkUEJFU3RyaW5nRW5jcnlwdG9yOwoKaW1wb3J0IG9yZy5qYXN5cHQuZW5jcnlwdGlvbi5wYmUuY29uZmlnLlNpbXBsZVN0cmluZ1BCRUNvbmZpZzsKCmltcG9ydCBvcmcuc3ByaW5nZnJhbWV3b3JrLmJlYW5zLmZhY3RvcnkuYW5ub3RhdGlvbi5WYWx1ZTsKCmltcG9ydCBvcmcuc3ByaW5nZnJhbWV3b3JrLmNvbnRleHQuYW5ub3RhdGlvbi5CZWFuOwoKaW1wb3J0IG9yZy5zcHJpbmdmcmFtZXdvcmsuY29udGV4dC5hbm5vdGF0aW9uLkNvbmZpZ3VyYXRpb247CgoKCkBDb25maWd1cmF0aW9uCgpwdWJsaWMgY2xhc3MgSmFzeXB0RW5jcnlwdG9yc0NvbmZpZyB7CgoKCiAgICAvLyDoiIrvvJrlg4Xngrrnm7jlrrnvvIjlvLHliqDlr4bvvIznhKEgSVbvvIkKCiAgICBAQmVhbigibGVnYWN5RW5jcnlwdG9yIikKCiAgICBwdWJsaWMgU3RyaW5nRW5jcnlwdG9yIGxlZ2FjeUVuY3J5cHRvcigKCiAgICAgICAgICAgIEBWYWx1ZSgiJHtqYXN5cHQuZW5jcnlwdG9yLnBhc3N3b3JkfSIpIFN0cmluZyBsZWdhY3lQYXNzd29yZCkgewoKICAgICAgICBQb29sZWRQQkVTdHJpbmdFbmNyeXB0b3IgZW5jID0gbmV3IFBvb2xlZFBCRVN0cmluZ0VuY3J5cHRvcigpOwoKICAgICAgICBTaW1wbGVTdHJpbmdQQkVDb25maWcgY2ZnID0gbmV3IFNpbXBsZVN0cmluZ1BCRUNvbmZpZygpOwoKICAgICAgICBjZmcuc2V0UGFzc3dvcmQobGVnYWN5UGFzc3dvcmQpOwoKICAgICAgICBjZmcuc2V0QWxnb3JpdGhtKCJQQkVXaXRoTUQ1QW5kREVTIik7CgogICAgICAgIGNmZy5zZXRJdkdlbmVyYXRvckNsYXNzTmFtZSgib3JnLmphc3lwdC5pdi5Ob0l2R2VuZXJhdG9yIik7CgogICAgICAgIGNmZy5zZXRQb29sU2l6ZSgiMSIpOwoKICAgICAgICBjZmcuc2V0U3RyaW5nT3V0cHV0VHlwZSgiYmFzZTY0Iik7CgogICAgICAgIGVuYy5zZXRDb25maWcoY2ZnKTsKCiAgICAgICAgcmV0dXJuIGVuYzsKCiAgICB9CgoKCiAgICAvLyDmlrDvvJrljZTlipvllYYgQUVTLTI1NiDniYjmnKzvvIjlronlhajntYTmhYvvvIkKCiAgICBAQmVhbigiZW5jcnlwdG9yQmVhbiIpCgogICAgcHVibGljIFN0cmluZ0VuY3J5cHRvciBlbmNyeXB0b3JCZWFuKCkgewoKICAgICAgICBTdHJpbmcgcDEgPSBTeXN0ZW0uZ2V0UHJvcGVydHkoIlBfRU5WXzEiKTsKCiAgICAgICAgU3RyaW5nIHAyID0gU3lzdGVtLmdldFByb3BlcnR5KCJQX0VOVl8yIik7CgogICAgICAgIGlmIChwMSA9PSBudWxsIHx8IHAxLmlzQmxhbmsoKSB8fCAibnVsbCIuZXF1YWxzSWdub3JlQ2FzZShwMSkKCiAgICAgICAgIHx8IHAyID09IG51bGwgfHwgcDIuaXNCbGFuaygpIHx8ICJudWxsIi5lcXVhbHNJZ25vcmVDYXNlKHAyKSkgewoKICAgICAgICAgICAgcDEgPSBTeXN0ZW0uZ2V0ZW52KCJQX0VOVl8xIik7CgogICAgICAgICAgICBwMiA9IFN5c3RlbS5nZXRlbnYoIlBfRU5WXzIiKTsKCiAgICAgICAgfQoKICAgICAgICBTdHJpbmcgc2FsdCA9IChwMSA9PSBudWxsID8gIiIgOiBwMSkgKyAocDIgPT0gbnVsbCA/ICIiIDogcDIpOwoKCgogICAgICAgIFBvb2xlZFBCRVN0cmluZ0VuY3J5cHRvciBlbmMgPSBuZXcgUG9vbGVkUEJFU3RyaW5nRW5jcnlwdG9yKCk7CgogICAgICAgIFNpbXBsZVN0cmluZ1BCRUNvbmZpZyBjZmcgPSBuZXcgU2ltcGxlU3RyaW5nUEJFQ29uZmlnKCk7CgogICAgICAgIGNmZy5zZXRQYXNzd29yZChzYWx0KTsKCiAgICAgICAgY2ZnLnNldEFsZ29yaXRobSgiUEJFV2l0aEhtYWNTSEE1MTJBbmRBRVNfMjU2Iik7CgogICAgICAgIGNmZy5zZXRJdkdlbmVyYXRvckNsYXNzTmFtZSgib3JnLmphc3lwdC5pdi5SYW5kb21JdkdlbmVyYXRvciIpOwoKICAgICAgICBjZmcuc2V0S2V5T2J0ZW50aW9uSXRlcmF0aW9ucygiMTAwMCIpOwoKICAgICAgICBjZmcuc2V0UG9vbFNpemUoIjEiKTsKCiAgICAgICAgY2ZnLnNldFByb3ZpZGVyTmFtZSgiU3VuSkNFIik7CgogICAgICAgIGNmZy5zZXRTYWx0R2VuZXJhdG9yQ2xhc3NOYW1lKCJvcmcuamFzeXB0LnNhbHQuUmFuZG9tU2FsdEdlbmVyYXRvciIpOwoKICAgICAgICBjZmcuc2V0U3RyaW5nT3V0cHV0VHlwZSgiYmFzZTY0Iik7CgogICAgICAgIGVuYy5zZXRDb25maWcoY2ZnKTsKCiAgICAgICAgcmV0dXJuIGVuYzsKCiAgICB9Cgp9CgpgYGAKCgoKYENyeXB0b1NlcnZpY2UuamF2YWDvvJoKCgoKYGBgamF2YQoKaW1wb3J0IG9yZy5qYXN5cHQuZW5jcnlwdGlvbi5TdHJpbmdFbmNyeXB0b3I7CgppbXBvcnQgb3JnLnNwcmluZ2ZyYW1ld29yay5iZWFucy5mYWN0b3J5LmFubm90YXRpb24uUXVhbGlmaWVyOwoKaW1wb3J0IG9yZy5zcHJpbmdmcmFtZXdvcmsuc3RlcmVvdHlwZS5TZXJ2aWNlOwoKCgpAU2VydmljZQoKcHVibGljIGNsYXNzIENyeXB0b1NlcnZpY2UgewoKCgogICAgcHJpdmF0ZSBmaW5hbCBTdHJpbmdFbmNyeXB0b3IgbGVnYWN5OwoKICAgIHByaXZhdGUgZmluYWwgU3RyaW5nRW5jcnlwdG9yIG1vZGVybjsKCgoKICAgIHB1YmxpYyBDcnlwdG9TZXJ2aWNlKEBRdWFsaWZpZXIoImxlZ2FjeUVuY3J5cHRvciIpIFN0cmluZ0VuY3J5cHRvciBsZWdhY3ksCgogICAgICAgICAgICAgICAgICAgICAgICAgQFF1YWxpZmllcigiZW5jcnlwdG9yQmVhbiIpIFN0cmluZ0VuY3J5cHRvciBtb2Rlcm4pIHsKCiAgICAgICAgdGhpcy5sZWdhY3kgPSBsZWdhY3k7CgogICAgICAgIHRoaXMubW9kZXJuID0gbW9kZXJuOwoKICAgIH0KCgoKICAgIHB1YmxpYyBTdHJpbmcgZW5jcnlwdE5ldyhTdHJpbmcgcGxhaW4pIHsgcmV0dXJuIG1vZGVybi5lbmNyeXB0KHBsYWluKTsgfQoKICAgIHB1YmxpYyBTdHJpbmcgZGVjcnlwdE5ldyhTdHJpbmcgY2lwaGVyKSB7IHJldHVybiBtb2Rlcm4uZGVjcnlwdChjaXBoZXIpOyB9CgoKCiAgICBwdWJsaWMgU3RyaW5nIGRlY3J5cHRMZWdhY3koU3RyaW5nIGNpcGhlcikgeyByZXR1cm4gbGVnYWN5LmRlY3J5cHQoY2lwaGVyKTsgfQoKCgogICAgLy8g57Ch5piT6Ieq5YuV5Yik5pa377ya5YWI6Kmm5paw77yM5LiN6KGM5YaN5Zue6YCA6IiKCgogICAgcHVibGljIFN0cmluZyBzbWFydERlY3J5cHQoU3RyaW5nIGNpcGhlcikgewoKICAgICAgICB0cnkgeyByZXR1cm4gbW9kZXJuLmRlY3J5cHQoY2lwaGVyKTsgfQoKICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGlnbm9yZSkgeyAvKiBmYWxsIHRocm91Z2ggKi8gfQoKICAgICAgICByZXR1cm4gbGVnYWN5LmRlY3J5cHQoY2lwaGVyKTsKCiAgICB9Cgp9CgpgYGAKCgoKLS0tCgoKCiMjIEIuIOe1hOaFi+WxpOeahOiHquWLleino+Wvhu+8iFJvdXRpbmcgKyDlpJrliY3ntrTvvIkKCioq54K65L2V6ZyA6KaB77yfKiogYHVsaXNlc2JvY2NoaW8vamFzeXB0LXNwcmluZy1ib290YCDpoJDoqK3kuIDmrKHlj6rmnIPnlKjkuIDlgIsgYGphc3lwdC5lbmNyeXB0b3IuYmVhbmAg5Y676KejIGBFTkMoLi4uKWDjgILoi6XopoHlkIzmmYLmlK/mj7TlhannqK7liqDlr4bmoLzlvI/vvIzlu7rorbDlvJXlhaXoh6roqILjgIzlgbXmuKzlmagv6Kej5p6Q5Zmo44CN5oiW44CM6Lev55SxIEVuY3J5cHRvcuOAje+8jOeUqCoq5YmN57a0KirliIfmj5vvvJoKCgoKLSBgRU5DX05FVyguLi4pYCDihpIg55SoICoq5pawKiogZW5jcnlwdG9y77yIQUVTLTI1Nu+8ieOAggoKLSBgRU5DX09MRCguLi4pYCDihpIg55SoICoq6IiKKiogZW5jcnlwdG9y77yITUQ1K0RFU++8ieOAggoKLSBgRU5DKC4uLilgIOKGkiDnm7jlrrnkv53nlZnvvJrlhYjoqabmlrDjgIHlho3lm57pgIDoiIrjgIIKCgoKYEphc3lwdFJvdXRpbmdDb25maWcuamF2YWDvvJoKCgoKYGBgamF2YQoKaW1wb3J0IGNvbS51bGlzZXNib2NjaGlvLmphc3lwdHNwcmluZ2Jvb3QuZGV0ZWN0b3IuRW5jcnlwdGFibGVQcm9wZXJ0eURldGVjdG9yOwoKaW1wb3J0IGNvbS51bGlzZXNib2NjaGlvLmphc3lwdHNwcmluZ2Jvb3QucmVzb2x2ZXIuRW5jcnlwdGFibGVQcm9wZXJ0eVJlc29sdmVyOwoKaW1wb3J0IG9yZy5qYXN5cHQuZW5jcnlwdGlvbi5TdHJpbmdFbmNyeXB0b3I7CgppbXBvcnQgb3JnLnNwcmluZ2ZyYW1ld29yay5iZWFucy5mYWN0b3J5LmFubm90YXRpb24uUXVhbGlmaWVyOwoKaW1wb3J0IG9yZy5zcHJpbmdmcmFtZXdvcmsuY29udGV4dC5hbm5vdGF0aW9uLkJlYW47CgppbXBvcnQgb3JnLnNwcmluZ2ZyYW1ld29yay5jb250ZXh0LmFubm90YXRpb24uQ29uZmlndXJhdGlvbjsKCgoKQENvbmZpZ3VyYXRpb24KCnB1YmxpYyBjbGFzcyBKYXN5cHRSb3V0aW5nQ29uZmlnIHsKCgoKICAgIEBCZWFuKCJyb3V0aW5nRW5jcnlwdG9yIikKCiAgICBwdWJsaWMgU3RyaW5nRW5jcnlwdG9yIHJvdXRpbmdFbmNyeXB0b3IoCgogICAgICAgICAgICBAUXVhbGlmaWVyKCJsZWdhY3lFbmNyeXB0b3IiKSBTdHJpbmdFbmNyeXB0b3IgbGVnYWN5LAoKICAgICAgICAgICAgQFF1YWxpZmllcigiZW5jcnlwdG9yQmVhbiIpIFN0cmluZ0VuY3J5cHRvciBtb2Rlcm4pIHsKCiAgICAgICAgLy8g5L6b6ZyA6KaB5pmC55u05o6l5rOo5YWl77ybUmVzb2x2ZXIg5Lmf5pyD6Ieq6KGM5YiG5rWBCgogICAgICAgIHJldHVybiBuZXcgU3RyaW5nRW5jcnlwdG9yKCkgewoKICAgICAgICAgICAgQE92ZXJyaWRlIHB1YmxpYyBTdHJpbmcgZW5jcnlwdChTdHJpbmcgbWVzc2FnZSkgeyByZXR1cm4gbW9kZXJuLmVuY3J5cHQobWVzc2FnZSk7IH0KCiAgICAgICAgICAgIEBPdmVycmlkZSBwdWJsaWMgU3RyaW5nIGRlY3J5cHQoU3RyaW5nIGVuY3J5cHRlZE1lc3NhZ2UpIHsKCiAgICAgICAgICAgICAgICB0cnkgeyByZXR1cm4gbW9kZXJuLmRlY3J5cHQoZW5jcnlwdGVkTWVzc2FnZSk7IH0KCiAgICAgICAgICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGlnbm9yZSkge30KCiAgICAgICAgICAgICAgICByZXR1cm4gbGVnYWN5LmRlY3J5cHQoZW5jcnlwdGVkTWVzc2FnZSk7CgogICAgICAgICAgICB9CgogICAgICAgIH07CgogICAgfQoKCgogICAgQEJlYW4KCiAgICBwdWJsaWMgRW5jcnlwdGFibGVQcm9wZXJ0eURldGVjdG9yIGVuY3J5cHRhYmxlUHJvcGVydHlEZXRlY3RvcigpIHsKCiAgICAgICAgcmV0dXJuIHZhbHVlIC0+IHZhbHVlICE9IG51bGwgJiYKCiAgICAgICAgICAgICAgICAodmFsdWUuc3RhcnRzV2l0aCgiRU5DX05FVygiKSB8fCB2YWx1ZS5zdGFydHNXaXRoKCJFTkNfT0xEKCIpIHx8IHZhbHVlLnN0YXJ0c1dpdGgoIkVOQygiKSk7CgogICAgfQoKCgogICAgQEJlYW4KCiAgICBwdWJsaWMgRW5jcnlwdGFibGVQcm9wZXJ0eVJlc29sdmVyIGVuY3J5cHRhYmxlUHJvcGVydHlSZXNvbHZlcigKCiAgICAgICAgICAgIEBRdWFsaWZpZXIoImxlZ2FjeUVuY3J5cHRvciIpIFN0cmluZ0VuY3J5cHRvciBsZWdhY3ksCgogICAgICAgICAgICBAUXVhbGlmaWVyKCJlbmNyeXB0b3JCZWFuIikgU3RyaW5nRW5jcnlwdG9yIG1vZGVybikgewoKCgogICAgICAgIHJldHVybiB2YWx1ZSAtPiB7CgogICAgICAgICAgICBpZiAodmFsdWUgPT0gbnVsbCkgcmV0dXJuIG51bGw7CgoKCiAgICAgICAgICAgIGlmICh2YWx1ZS5zdGFydHNXaXRoKCJFTkNfTkVXKCIpICYmIHZhbHVlLmVuZHNXaXRoKCIpIikpIHsKCiAgICAgICAgICAgICAgICBTdHJpbmcgYm9keSA9IHZhbHVlLnN1YnN0cmluZygiRU5DX05FVygiLmxlbmd0aCgpLCB2YWx1ZS5sZW5ndGgoKSAtIDEpOwoKICAgICAgICAgICAgICAgIHJldHVybiBtb2Rlcm4uZGVjcnlwdChib2R5KTsKCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGlmICh2YWx1ZS5zdGFydHNXaXRoKCJFTkNfT0xEKCIpICYmIHZhbHVlLmVuZHNXaXRoKCIpIikpIHsKCiAgICAgICAgICAgICAgICBTdHJpbmcgYm9keSA9IHZhbHVlLnN1YnN0cmluZygiRU5DX09MRCgiLmxlbmd0aCgpLCB2YWx1ZS5sZW5ndGgoKSAtIDEpOwoKICAgICAgICAgICAgICAgIHJldHVybiBsZWdhY3kuZGVjcnlwdChib2R5KTsKCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGlmICh2YWx1ZS5zdGFydHNXaXRoKCJFTkMoIikgJiYgdmFsdWUuZW5kc1dpdGgoIikiKSkgewoKICAgICAgICAgICAgICAgIFN0cmluZyBib2R5ID0gdmFsdWUuc3Vic3RyaW5nKCJFTkMoIi5sZW5ndGgoKSwgdmFsdWUubGVuZ3RoKCkgLSAxKTsKCiAgICAgICAgICAgICAgICB0cnkgeyByZXR1cm4gbW9kZXJuLmRlY3J5cHQoYm9keSk7IH0KCiAgICAgICAgICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGlnbm9yZSkgeyByZXR1cm4gbGVnYWN5LmRlY3J5cHQoYm9keSk7IH0KCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIHJldHVybiB2YWx1ZTsKCiAgICAgICAgfTsKCiAgICB9Cgp9CgpgYGAKCgoKYGFwcGxpY2F0aW9uLnltbGDvvJoKCgoKYGBgeWFtbAoKamFzeXB0OgoKICBlbmNyeXB0b3I6CgogICAgYmVhbjogcm91dGluZ0VuY3J5cHRvciAgICMg6K6TIFJlc29sdmVyL0RldGVjdG9yIOeUn+aViO+8jOS4puaPkOS+myByb3V0aW5nIOiDveWKmwoKCgpzcHJpbmc6CgogIGRhdGFzb3VyY2U6CgogICAgcGFzc3dvcmQ6ICJFTkNfTkVXKC4uLi4uKSIgICAjIOaWsOagvOW8j++8iEFFUy0yNTbvvIkKCgoKbGVnYWN5OgoKICB0b2tlbjogIkVOQ19PTEQoLi4uLi4pIiAgICAgICAgIyDoiIrmoLzlvI/vvIhNRDUrREVT77yJCgoKCm90aGVyOgoKICBzZWNyZXQ6ICJFTkMoLi4uLi4pIiAgICAgICAgICAgIyDmrbflj7Lnm7jlrrnvvJrmnKrmqJnnpLrmlrDoiIrogIUKCmBgYAoKCgotLS0KCgoKIyMgQy4g55Si5Ye66IiH6L2J5o+b562W55Wl77yI5a+m5YuZ77yJCgotICoq5paw6LOH5paZ5LiA5b6L55SoIEFFUy0yNTbvvIhgZW5jcnlwdG9yQmVhbmDvvInnlKLnlJ8qKu+8m+mBv+WFjeWGjeWvq+WFpSBNRDUrREVT44CCICAKCi0g6IiK6LOH5paZ5L+d55WZ6Kej5a+G6IO95Yqb5LiA5q615pmC6ZaT77yIYGxlZ2FjeUVuY3J5cHRvcmDvvInvvIzpgJDmraXovYnmqpTmiJblnKjoroDlj5bmmYLljbPmmYLku6XmlrDmoLzlvI/ph43lr6vjgIIgIAoKLSDlnKjntYTmhYvkuK3mlLnnlKggYEVOQ19ORVcoLi4uKWDvvJvlj6rlnKjlv4XopoHmmYLkv53nlZkgYEVOQ19PTEQoLi4uKWDjgIIKCgoKLS0tCgoKCiMjIEQuIOW4uOimi+iQveWdkQoKLSDljp/lp4vniYfmrrXluLjopovoqqTmpI3vvIhgc2FpdGAg4oaSIGBzYWx0YOOAgWBzZXRBbGdvcml0aG1gIOaLvOWvq+OAgeWFqOW9ouespuiZn++8ie+8jOevhOS+i+W3suS/ruato+OAgiAgCgotIGBQQkVXaXRoTUQ1QW5kREVTYCArIGBOb0l2R2VuZXJhdG9yYCDlronlhajmgKflvojlvLHvvJvlg4XpmZDnm7jlrrnmnJ/kvb/nlKjjgIIgIAoKLSBgUF9FTlZfMWAgLyBgUF9FTlZfMmAg5Y+W6IeqICoqSlZNIOezu+e1seWxrOaApyoqIOaIliAqKueSsOWig+iuiuaVuCoqIOaZgu+8jOiri+iZleeQhuepuuWtl+S4suiIhyBgIm51bGwiYOOAgiAgCgotIOiIiiBKREsg6ZyA5rOo5oSPIEFFUy0yNTYg55qE5pS/562W5qqU77yb54++5LujIEpESyDlt7LpoJDoqK3plovllZ/vvIzkvYbku43lu7rorbDlnKjmiYDmnInnkrDlooPpqZforYnjgIIKCgoKLS0tCgoKCiMjIEUuIOacgOWwj+WPr+eUqOmFjee9rua4heWWrgoKMS4g5a6j5ZGKIGBAQmVhbigibGVnYWN5RW5jcnlwdG9yIilgIOiIhyBgQEJlYW4oImVuY3J5cHRvckJlYW4iKWDjgIIgIAoKMi4g6Iul57WE5oWL6KaB5ZCM5pmC6Kej5YWp56iu5qC85byP77ya5Yqg5LiK6Ieq6KiCIGBFbmNyeXB0YWJsZVByb3BlcnR5RGV0ZWN0b3JgIC8gYEVuY3J5cHRhYmxlUHJvcGVydHlSZXNvbHZlcmDvvIzmlK/mj7QgYEVOQ19ORVcoLi4uKWDjgIFgRU5DX09MRCguLi4pYOOAgWBFTkMoLi4uKWDjgIIgIAoKMy4g56iL5byP56K85bGk5LulIGBAUXVhbGlmaWVyYCDmjIflrprkvb/nlKjlk6rkuIDntYQgRW5jcnlwdG9y44CCCgoKCi0tLQoKCgojIyBGLiDnsKHmmJPmuKzoqabvvIjnlKLnlJ/vvI/pqZforYnlr4bmlofvvIkKCgoKYEphc3lwdFF1aWNrVGVzdC5qYXZhYO+8mgoKCgpgYGBqYXZhCgpwdWJsaWMgY2xhc3MgSmFzeXB0UXVpY2tUZXN0IHsKCgoKICAgIHB1YmxpYyBzdGF0aWMgdm9pZCBtYWluKFN0cmluZ1tdIGFyZ3MpIHsKCiAgICAgICAgLy8g5YGH6KOd5bey5b6eIFNwcmluZyDlj5blh7rlhanlgIsgQmVhbu+8mmxlZ2FjeSDoiIcgbW9kZXJuCgogICAgICAgIHZhciBsZWdhY3kgPSBuZXcgb3JnLmphc3lwdC5lbmNyeXB0aW9uLnBiZS5TdGFuZGFyZFBCRVN0cmluZ0VuY3J5cHRvcigpOwoKICAgICAgICBsZWdhY3kuc2V0UGFzc3dvcmQoInZ1NHdqLzMiKTsgLy8g6IiK5a+G56K877ya5L6G6IeqIGFwcGxpY2F0aW9uIOioreWumgoKICAgICAgICBsZWdhY3kuc2V0QWxnb3JpdGhtKCJQQkVXaXRoTUQ1QW5kREVTIik7CgoKCiAgICAgICAgdmFyIG1vZGVybiA9IG5ldyBvcmcuamFzeXB0LmVuY3J5cHRpb24ucGJlLlN0YW5kYXJkUEJFU3RyaW5nRW5jcnlwdG9yKCk7CgogICAgICAgIG1vZGVybi5zZXRQYXNzd29yZChTeXN0ZW0uZ2V0ZW52KCJQX0VOVl8xIikgKyBTeXN0ZW0uZ2V0ZW52KCJQX0VOVl8yIikpOwoKICAgICAgICBtb2Rlcm4uc2V0QWxnb3JpdGhtKCJQQkVXaXRoSG1hY1NIQTUxMkFuZEFFU18yNTYiKTsKCgoKICAgICAgICBTdHJpbmcgcGxhaW4gPSAiczNjcjN0LVBAc3MhIjsKCgoKICAgICAgICBTdHJpbmcgb2xkQ2lwaGVyID0gbGVnYWN5LmVuY3J5cHQocGxhaW4pOwoKICAgICAgICBTdHJpbmcgbmV3Q2lwaGVyID0gbW9kZXJuLmVuY3J5cHQocGxhaW4pOwoKCgogICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbigiRU5DX09MRCgiICsgb2xkQ2lwaGVyICsgIikiKTsKCiAgICAgICAgU3lzdGVtLm91dC5wcmludGxuKCJFTkNfTkVXKCIgKyBuZXdDaXBoZXIgKyAiKSIpOwoKCgogICAgICAgIC8vIOmpl+itiQoKICAgICAgICBTeXN0ZW0ub3V0LnByaW50bG4oImxlZ2FjeSAtPiAiICsgbGVnYWN5LmRlY3J5cHQob2xkQ2lwaGVyKSk7CgogICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbigibW9kZXJuIC0+ICIgKyBtb2Rlcm4uZGVjcnlwdChuZXdDaXBoZXIpKTsKCiAgICB9Cgp9CgpgYGAKCgoKPiDmj5DnpLrvvJrmraPlvI/lsIjmoYjoq4vku6UgU3ByaW5nIOWPluW+lyBCZWFuIOmAsuihjOa4rOippu+8m+atpOiZleeCuuacgOWwj+WPr+ihjOekuuaEj+OAggoKCgotLS0KCgoKIyMg54mI5o6n5bu66K2wCgotIOWFiOWwjuWFpeOAjOi3r+eUseOAjeiIh+mbmSBFbmNyeXB0b3LvvIznorrkv53mnI3li5nlj6/lkIzmmYLop6PlhannqK7moLzlvI/jgIIgIAoKLSDmkrDlr6vos4fmlpnovYnmj5vohbPmnKzvvIjmibnmrKHmiJboroDlj5bljbPovYnvvInvvIzpgJDmraXmtojpmaQgYEVOQ19PTEQoLi4uKWDjgIIgIAoKLSDmuIXmn6XlroznlaLlvozvvIwqKuenu+mZpOiIiiBFbmNyeXB0b3IqKiDoiIcgYEVOQ19PTEQoLi4uKWAg5pSv5o+077yM6ZmN5L2O5pS75pOK6Z2i44CCCgoKCi0tLQoKCgojIyDmjojmrIroiIflronlhagKCi0g5pys5oyH5Y2X5YOF56S656+EIEphc3lwdCDnlKjms5XvvIzkuI3ljIXlkKvnrKzkuInmlrnmjojmrIrmqpTjgIIgIAoKLSDlr6bli5nkuIroq4vmkK3phY3npZXlr4bnrqHnkIbvvIjlpoIgS01T44CBVmF1bHTjgIFLdWJlcm5ldGVzIFNlY3JldO+8ieWPluS7o+ehrOe3qOeivOWvhumRsOOAggo=