加密和解密
| 要使用加密和解密功能,您需要在 JVM 中安裝完整強度的 JCE(預設不包含)。您可以從 Oracle 下載“Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files”,並按照安裝說明操作(實質上,您需要將 JRE lib/security 目錄中的兩個策略檔案替換為您下載的檔案)。這僅適用於舊版本的 Java。從 Java 9 開始,以及從 Java 8u161 及更高版本開始,無限強度加密預設啟用。 |
如果遠端屬性源包含加密內容(以 {cipher} 開頭的值),它們會在透過 HTTP 傳送給客戶端之前被解密。這種設定的主要優點是屬性值在“靜止”狀態時(例如,在 git 倉庫中)不需要是明文。如果某個值無法解密,它將從屬性源中移除,並新增一個具有相同鍵但字首為 invalid 的附加屬性,其值為“不適用”(通常為 <n/a>)。這主要是為了防止密文被用作密碼並意外洩露。
如果您為配置客戶端應用程式設定了一個遠端配置倉庫,它可能包含一個類似以下的 application.yml:
spring:
datasource:
username: dbuser
password: '{cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ'
application.properties 檔案中的加密值不能用引號括起來。否則,該值將不被解密。以下示例顯示了可行的值:
spring.datasource.username: dbuser
spring.datasource.password: {cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ
您可以安全地將此明文推送到共享 git 倉庫,並且秘密密碼仍然受到保護。
伺服器還暴露了 /encrypt 和 /decrypt 端點(假設這些端點是安全的,並且只有經過授權的代理才能訪問)。如果您編輯遠端配置檔案,您可以使用配置伺服器透過 POST 到 /encrypt 端點來加密值,如下例所示:
$ curl localhost:8888/encrypt -s -d mysecret 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
如果您使用 curl 進行測試,請使用 --data-urlencode(而不是 -d),並用 = 作為加密值的字首(curl 需要此操作),或者設定一個顯式的 Content-Type: text/plain,以確保在存在特殊字元(特別是 '+' 字元很棘手)時 curl 能正確編碼資料。 |
請確保不要將任何 curl 命令統計資訊包含在加密值中,這就是示例中使用 -s 選項來抑制它們的原因。將值輸出到檔案可以幫助避免此問題。 |
透過 /decrypt 也可以執行反向操作(前提是伺服器配置了對稱金鑰或完整的金鑰對),如下例所示:
$ curl localhost:8888/decrypt -s -d 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda mysecret
獲取加密值,並在將其放入 YAML 或屬性檔案之前,以及在提交併將其推送到遠端(可能不安全)儲存之前,新增 {cipher} 字首。
/encrypt 和 /decrypt 端點也都接受 /*/{application}/{profiles} 形式的路徑,當客戶端呼叫主環境資源時,可用於按應用程式(名稱)和按配置檔案控制加密。
要以這種細粒度的方式控制加密,您還必須提供一個 TextEncryptorLocator 型別的 @Bean,它為每個名稱和配置檔案建立不同的加密器。預設提供的那個不會這樣做(所有加密都使用相同的金鑰)。 |
Spring 命令列客戶端(安裝了 Spring Cloud CLI 擴充套件)也可以用於加密和解密,如下例所示:
$ spring encrypt mysecret --key foo 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda $ spring decrypt --key foo 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda mysecret
要在檔案中使用金鑰(例如用於加密的 RSA 公鑰),請在金鑰值前加上 "@" 並提供檔案路徑,如下例所示:
$ spring encrypt mysecret --key @${HOME}/.ssh/id_rsa.pub
AQAjPgt3eFZQXwt8tsHAVv/QHiY5sI2dRcR+...
--key 引數是強制性的(儘管有 -- 字首)。 |
解密錯誤
當配置伺服器未能解密某個值時,它將在 HTTP 響應中建立一個 invalid 屬性。
例如:
{
"label": null,
"name": "application",
"profiles": [
"prd"
],
"propertySources": [
{
"name": "file:/demo/configserver/application-prd.yaml",
"source": {
"invalid.SharedPassword": "<n/a>"
}
},
{
"name": "file:/demo/configserver/application.yaml",
"source": {
"SharedPassword": "Fill_me_in"
}
}
],
"state": null,
"version": null
}
在上面的示例中,配置伺服器無法解密 application-prd.yaml 中 SharedPassword 的值,因此配置伺服器將屬性名稱加上了 invalid 字首。
如果配置客戶端收到此響應,並將其新增到應用程式的 Environment 中,然後客戶端請求 SharedPassword 的值,它將得到 Fill_me_in。
如果您不希望配置伺服器對無法解密的屬性加上 invalid 字首,您可以將 spring.cloud.config.server.encrypt.prefix-invalid-properties 設定為 false。如果您這樣做,那麼來自配置伺服器的相同響應將如下所示:
"label": null,
"name": "application",
"profiles": [
"prd"
],
"propertySources": [
{
"name": "file:/demo/configserver/application-prd.yaml",
"source": {
"SharedPassword": "AYBKlpcZpaR36OcRDQjNIQl6fmnddAQhetMw/uyTpnn5fDj+unJ9QOEbqiPc9fX0N+CC8i+EJiN6nlH9Xqu6sH1tX/P6zg1CIy+ct/1RWGNbmQ256jc6vQaXhiN8sA8Mr6QiqYnMoBd+Jni/Miir5G3a7G9MmjbEUASKJOhUlIFKqL1IqB81RBT/cv0bg9kAiy5VBF1WppxP/PwtjECzbeUi2Y1jbpYb98rnc/qmRO3ZJam9fDNcPpW09qGFhGgJIujca257F7G4guS2w/7haVzNoyRiwHzZ14oL8AIxHLMBSJJF19ULlsMAkROj9o9TnwhL9r4rX9sAWk28c5eq77+iVpmlT3yoRdZqvMqffzKiibDlzz95Gmms7V7mctxrhNVOOWTwMSJvk94Y9ZPenljKgPJIV3Z1cqqx+W8JxFFeelOuYvMEe4bOVBh1TepGzzdWVdYbylgXJy35uRTZ2drybUe5+jc0hiAuujHz0zdY1FwOHfwzSsSidlYn4syPeuytnxTzn7fbWXeXetTTtDlmLRf8MBSzXzDFWNH0cNGOCQ=="
}
},
{
"name": "file:/demo/configserver/application.yaml",
"source": {
"SharedPassword": "Fill_me_in"
}
}
],
"state": null,
"version": null
}
在這種情況下,如果配置客戶端收到上述響應並從 Environment 請求 SharedPassword 的值,它將獲得加密值,而不是 Fill_me_in。