最后更新於2017年01月24日
一、為什么
為什么寫這篇文章?ca是什么?數字證書是什么?ejbca又是什么?
讓我們從http與https說起。http是超文本傳輸協議(HyperText Transfer Protocol)的縮寫,也是互聯網的基石,互聯網絕大部分數據傳輸都是基於http協議,但是,http本身有個致命的缺點,一個早期並不引人注目,如今不得不重視的缺點,就是明文傳輸。
當你訪問一個http站點,例如http://www.login.com/
輸入用戶名與密碼,例如admin/123456,點擊“Sign in”按鈕登錄,數據將被發往服務器,期間登錄的個人數據“admin/123456”仍以明文形式在網絡上傳輸,如果有人抓到這個數據包,你的賬號密碼就有泄漏的風險。今天看到一則新聞,google chrome 56將把http站點要求輸入賬戶密碼的情況標記為不安全,相信未來http將成為歷史。
https很好的解決了這個問題,以www.baidu.com為例,早期使用http://www.biadu.com,如今已更換為https://www.baidu.com,訪問https://www.baidu.com可以一把綠色的鎖,點擊就出現了如下信息,此時,與服務器之間的通信連接是加密的,即使被他人截獲數據包,也很難知道傳輸的內容
https與http有啥區別?除了多一個“s”其實沒啥區別(開玩笑),這個“s”至關重要,使用https必須在服務器上安裝數字證書,上圖中點擊“查看證書”就可以看到服務器證書信息
有了數字證書,https還能實現另一個非常重要的功能:身份驗證。還是以www.baidu.com為例,早期的http://www.biadu.com沒有數字證書,所以可能會出現這樣的情況,你的網關被人劫持,所有你發往www.baidu.com的信息都被人截獲,同時,他對你的請求作出響應,以此冒充真正的www.baidu.com,也就是說,你訪問的www.baidu.com並不是真正的www.baidu.com,而是一個代理網站,它的行為表現與www.baidu.com很像。
https://www.baidu.com就不會出現這種情況了,因為它需要數字證書,而數字證書在全球范圍內都是唯一並不可偽造的
二、密碼學基礎
理解數字證書需要先了解對稱加密與非對稱加密,我們從普通加密通信模型開始
我向朋友發送一條消息“有時間一起吃飯嗎”,實際在網絡中傳輸的是加密后的數據,不是原文,只是加密和解密過程對我和朋友是透明的
對稱加密
非對稱加密
上述通信過程中,如果加密與解密使用相同的密鑰,稱為對稱加密,如果使用不同的密鑰,稱為非對稱加密
對稱加密的特點是速度快,可加密的數據量大;非對稱加密的特點是速度慢,因此只用來加密少量數據,但極難破解
非對稱加密過程中使用的兩個不同的密鑰,一個稱為“私鑰”,一個稱為“公鑰”,它們一一對應,稱為“密鑰對”,“公鑰”可以給任何人使用,但“私鑰”必須自己保持,一旦“私鑰”泄露,這個密鑰對就應該被拋棄
密鑰對有兩個非常重要的特點:1、“私鑰”可以導出“公鑰”,但“公鑰”無法導出“私鑰”;2、經“私鑰”加密的內容只能由“公鑰”解密,經“公鑰”加密的內容也只能由“私鑰”解密。
這兩個特點有兩個重要用途:1、“私鑰”持有者用“私鑰”加密內容,發送給“公鑰”持有者解密,驗證“私鑰”持有者的身份。因為“公鑰”能解密的內容,只能是由“私鑰”加密的;2、“公鑰”持有者用“公鑰”加密內容,發送給“私鑰”持有者解密,保證內容安全。因為只有“私鑰”能解密,即使內容被截獲,截獲者也無法知道內容是什么
摘要與簽名算法
數字證書是一個文件,包含了使用者的身份信息、以及權威機構(CA)的數字簽名,就向我們的居民身份證一樣,數字證書是“網絡身份證”,用於驗證互聯網上證書持有者的身份
頒發數字證書的機構叫CA,全世界只有少數權威的CA,因為頒發出去的證書它們是要負法律責任的,所以向它們申請證書也要繳費,我們自己搭建的ejbca頒發的證書只適合在企業內部使用,這個CA是沒有權利向互聯網上其他商業公司頒發證書的
有了數字證書,只要配置服務開啟https就可以使用數字證書了
https對於數字證書的認證包括“單向認證”與“雙向認證”,“單向認證”只由客戶端驗證服務器,“雙向認證”則兩者相互驗證,只要驗證不通過,通信就自動中斷,下面介紹通信的流程以及如何在tomcat中配置
三、數字證書
那么,數字證書從何而來?數字證書由專門的機構頒發,這種機構稱為CA(Certificate Authority),ejbca則是CA的一種java實現
四、https原理
https單向認證
“單向認證”的兩個實體
“單向認證”的流程如下圖
簡要說明如下
- 客戶端訪問服務器
- 服務器響應客戶端,發送服務器證書給客戶端
- 客戶端查詢“受信任根證書頒發機構”,驗證服務器證書
- 客戶端驗證完服務器證書,生成“密鑰對”及會話密鑰,與服務器協商會話密鑰
- 會話密鑰協商完成,開始安全加密通信
tomcat配置https單向認證
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="D:\tomcat.jks" keystorePass="123456" />
nginx配置https單向認證
server { listen 443 ssl; server_name localhost; ssl_certificate cert.pem; #服務器證書公鑰部分 ssl_certificate_key cert.key; #服務器證書私鑰 ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; location / { proxy_pass http://127.0.0.1:4000; #代理tomcat的http服務 } }
https雙向認證
“雙向認證”的兩個實體
“雙向認證”的流程如下圖
簡要說明如下
- 客戶端訪問服務器
- 服務器響應客戶端,發送服務器證書給客戶端
- 客戶端查詢“受信任根證書頒發機構”,驗證服務器證書
- 驗證完服務器證書,客戶端發送客戶端證書給服務器
- 服務器查詢“信任庫”或通過證書鏈,驗證客戶端證書
- 客戶端與服務器協商會話密鑰加密方案
- 客戶端與服務器協商會話密鑰
- 會話密鑰協商完成,開始安全加密通信
tomcat配置https雙向認證
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="true" sslProtocol="TLS" keystoreFile="D:\tomcat.jks" keystorePass="123456" truststoreFile="D:\tomcat.jks" truststorePass="123456"/>
nginx配置https雙向認證
server { listen 443 ssl; server_name localhost; ssl_certificate cert.pem; #服務器證書公鑰部分 ssl_certificate_key cert.key; #服務器證書私鑰 ssl_verify_client on; #開啟瀏覽器認證 ssl_client_certificate ca.pem; #CA根證書 ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; location / { proxy_pass http://127.0.0.1:4000; #代理tomcat的http服務 proxy_set_header client-cert $ssl_client_cert; #將瀏覽器證書傳遞給tomcat,tomcat通過header拿到證書 } }
五、linux環境下安裝ejbca
1、安裝jdk
檢查已安裝jdk,如果有,先刪除
rpm -qa|grep java rpm -e --nodeps filename
從oracle官方網站下載jdk安裝包:jdk-8u111-linux-x64.rpm,放在/usr/file目錄
rpm -ivh jdk-8u111-linux-x64.rpm
默認安裝在/usr/java目錄,安裝完后配置環境變量
vi /etc/profile
追加如下內容
#java conf export JAVA_HOME=/usr/java/jdk1.8.0_111 export PATH=$PATH:$JAVA_HOME/bin export CLASSPATH=:$JAVA_HOME/lib
使配置立即生效,然后檢查安裝結果
source /etc/profile java -version
2、安裝ant
從ant官方網站下載ant安裝包:apache-ant-1.9.7-bin.tar.gz,解壓
tar xvf apache-ant-1.9.7-bin.tar.gz -C /usr/java/
配置環境變量
vi /etc/profile
追加如下內容
#ant conf export ANT_HOME=/usr/java/apache-ant-1.9.7 export PATH=$PATH:$ANT_HOME/bin
使配置立即生效,然后檢查安裝結果
source /etc/profile ant -version
3、安裝mysql
安裝前查看已安裝的mysql或mysql庫,並刪除它們
rpm -qa|grep -i mysql rpm -e --nodeps filename
如果重裝mysql,查找安裝mysql產生的文件,並刪除它們
find / -name mysql rm -fr filename
安裝mysql所需的perl依賴
yum install -y perl-Module-Install.noarch
從mysql官方網站下載Red Hat Enterprise Linux 6 / Oracle Linux 6 (x86, 64-bit)對應5.6版本的安裝包:MySQL-client-5.6.33-1.el6.x86_64.rpm、MySQL-server-5.6.33-1.el6.x86_64.rpm,依次安裝
rpm -ivh MySQL-server-5.6.33-1.el6.x86_64.rpm rpm -ivh MySQL-client-5.6.33-1.el6.x86_64.rpm
安裝完成,啟動mysql服務,並設置為開機啟動
service mysql start chkconfig mysql on
mysql安裝完成后為root賬戶生成隨機密碼,位於/root目錄下的.mysql_secret文件中,用這個密碼登錄root
cat /root/.mysql_secret mysql -uroot -pLTHVNyKmmg7jwiLk
修改root密碼並配置遠程訪問
set password=password('root'); grant all privileges on *.* to 'root'@'%' identified by 'root';
4、安裝jboss
從jboss官方網站下載jboss安裝包:jboss-as-7.1.1.Final.tar.gz,解壓並配置環境變量
tar xvf jboss-as-7.1.1.Final.tar.gz -C /usr/java vi /etc/profile
追加內容
#jboss conf export JBOSS_HOME=/usr/java/jboss-as-7.1.1.Final
使配置立即生效
source /etc/profile
啟動jboss,注意最后的&符號,待啟動完成再運行“exit”
sh /usr/java/jboss-as-7.1.1.Final/bin/standalone.sh & exit
這樣jboss就運行在后台了,以下命令查看jboss進程並關閉
ps -ef|grep jboss kill -9 進程號
5、配置jboss的mysql數據源
創建目錄,然后在該目錄下創建module.xml
mkdir -p /usr/java/jboss-as-7.1.1.Final/modules/com/mysql/main cd /usr/java/jboss-as-7.1.1.Final/modules/com/mysql/main vi module.xml
module.xml內容如下
<?xml version="1.0" encoding="UTF-8"?> <module xmlns="urn:jboss:module:1.0" name="com.mysql"> <resources> <resource-root path="mysql-connector-java-5.1.27.jar"/> </resources> <dependencies> <module name="javax.api"/> <module name="javax.transaction.api"/> </dependencies> </module>
下載mysql的驅動包mysql-connector-java-5.1.27.jar,放在/usr/file目錄,然后拷貝到當前目錄
cp /usr/file/mysql-connector-java-5.1.27.jar ./
打開新的shell窗口,運行
sh /usr/java/jboss-as-7.1.1.Final/bin/jboss-cli.sh -c
如果是“disconnect”狀態,先輸入“connect”,多回車幾次后,運行下面命令
/subsystem=datasources/jdbc-driver=com.mysql.jdbc.Driver:add(driver-name=com.mysql.jdbc.Driver,driver-class-name=com.mysql.jdbc.Driver,driver-module-name=com.mysql,driver-xa-datasource-class-name=com.mysql.jdbc.jdbc.jdbc2.optional.MysqlXADataSource) :reload
6、配置ejbca
從ejbca官方網站下載ejbca安裝包:ejbca_ce_6_3_1_1.zip,放在/usr/file目錄,解壓,准備修改配置
unzip /usr/file/ejbca_ce_6_3_1_1.zip -d /usr/java cd /usr/java mv ejbca_ce_6_3_1_1 ejbca-ce-6.3.1.1 cd /usr/java/ejbca-ce-6.3.1.1/conf/
1、修改ejbca.properties
mv ejbca.properties.sample ejbca.properties vi ejbca.properties
修改如下內容
appserver.home=/usr/java/jboss-as-7.1.1.Final appserver.type=jboss
2、修改database.properties
mv database.properties.sample database.properties vi database.properties
修改如下內容
# dataSource datasource.jndi-name=jboss/datasources/MySqlDS # mysql info database.name=mysql database.url=jdbc:mysql://127.0.0.1:3306/ejbca?characterEncoding=UTF-8 database.driver=com.mysql.jdbc.Driver database.username=root database.password=root
3、修改install.properties
mv install.properties.sample install.properties vi install.properties
修改如下內容
#設置ca名稱 ca.name=test #設置ca信息 ca.dn=CN=test,O=test,C=cn
4、修改cesecore.properties、jaxws.properties,不需要修改內容
mv cesecore.properties.sample cesecore.properties mv jaxws.properties.sample jaxws.properties
5、修改web.properties
mv web.properties.sample web.properties vi web.properties
修改如下內容
superadmin.password=ejbca superadmin.cn=superadmin httpsserver.hostname=ca.test.com httpsserver.dn=CN=${httpsserver.hostname},O=test,C=cn
7、部署ejbca到jboss
首先,在配置的mysql中創建“ejbca”數據庫,編碼“utf-8”,然后正式用ant構建ejbca並安裝到jboss
cd /usr/java/ejbca-ce-6.3.1.1 ant clean deploy ant install ant deploy-keystore
deploy用ant部署,install生成證書,deploy-keystore將證書部署到jboss,前兩步所需時間較長,過程中如需輸入,請直接回車
8、配置jboss的https
打開新的shell窗口,運行
sh /usr/java/jboss-as-7.1.1.Final/bin/jboss-cli.sh -c
如果是“disconnect”狀態,運行“connect”,多回車幾次,准備運行下面4部分配置
第一部分(配置任意主機可訪問)
/interface=http:add(inet-address="0.0.0.0") /interface=httpspub:add(inet-address="0.0.0.0") /interface=httpspriv:add(inet-address="0.0.0.0") /socket-binding-group=standard-sockets/socket-binding=http:add(port="8080",interface="http") /subsystem=undertow/server=default-server/http-listener=http:add(socket-binding=http) /subsystem=undertow/server=default-server/http-listener=http:write-attribute(name=redirect-socket, value="httpspriv") :reload
第二部分(配置證書)
/core-service=management/security-realm=SSLRealm:add() /core-service=management/security-realm=SSLRealm/server-identity=ssl:add(keystore-path="${jboss.server.config.dir}/keystore/keystore.jks", keystore-password="serverpwd", alias="prod-ica1") /core-service=management/security-realm=SSLRealm/authentication=truststore:add(keystore-path="${jboss.server.config.dir}/keystore/truststore.jks", keystore-password="changeit") /socket-binding-group=standard-sockets/socket-binding=httpspriv:add(port="8443",interface="httpspriv") /socket-binding-group=standard-sockets/socket-binding=httpspub:add(port="8442", interface="httpspub") :reload
第三部分(配置ssl)
/subsystem=undertow/server=default-server/https-listener=httpspriv:add(socket-binding=httpspriv, security-realm="SSLRealm", verify-client=REQUIRED) /subsystem=undertow/server=default-server/https-listener=httpspub:add(socket-binding=httpspub, security-realm="SSLRealm") :reload
第四部分(配置web service)
/system-property=org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH:add(value=true) /system-property=org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH:add(value=true) /system-property=org.apache.catalina.connector.URI_ENCODING:add(value="UTF-8") /system-property=org.apache.catalina.connector.USE_BODY_ENCODING_FOR_QUERY_STRING:add(value=true) /subsystem=webservices:write-attribute(name=wsdl-host, value=jbossws.undefined.host) /subsystem=webservices:write-attribute(name=modify-wsdl-address, value=true) :reload
9、瀏覽器訪問
拷貝ejbca根目錄下的p12目錄下的superadmin.p12文件到windows,推薦xftp工具,或者訪問
http://172.17.210.124:8080/ejbca/doc/superadmin.p12
下載該文件到windows,雙擊開始安裝,密碼“ejbca”,安裝完,訪問
https://172.17.210.124:8443/ejbca/adminweb
也就是ejbca的管理后台
六、使用ejbca管理數字證書
假設安裝ejbca系統的服務器地址為
172.17.210.124
編輯windows的“C:\Windows\System32\drivers\etc”下的hosts文件,加入一行
172.17.210.124 ca.xmyself.com
ejbca提供了兩個操作界面
管理員界面(需要superadmin證書)
https://ca.xmyself.com:8443/ejbca/adminweb/
用戶界面(不需要證書)
http://ca.xmyself.com:8080/ejbca/
1、注冊用戶
ejbca管理員界面,打開“RA Functions”—“Add End Entity”菜單,填寫以下“Required”列打勾的項。
用戶模板選擇“EMPTY”
輸入用戶名與密碼
Common name,如果是服務器用證書,這里請填寫域名
填寫證書信息,證書模板選擇“ENDUSER”,CA選擇“dev”,Token選擇“P12 file”
點擊“Add”注冊
2、下載證書
ejbca用戶界面,打開“Enroll”—“Create Browser Certificate”菜單
輸入用戶名和密碼,點擊“OK”按鈕
“Key length”選擇“2048 bits”;“Certificate profile”選擇“ENDUSER”,點擊“Enroll”按鈕下載證書
3、吊銷證書
ejbca管理員界面,打開“RA Functions”—“Search End Entities”菜單。“Search end entities with status”處下拉框選擇“All”,點擊右邊的“Search”按鈕查看用戶信息(下圖省略其他列)
勾選需要吊銷的用戶,點擊表格下方的“Revoke Selected”按鈕
4、更新證書
ejbca管理員界面,打開“RA Functions”—“Search End Entities”菜單。“Search end entities with status”處下拉框選擇“All”,點擊右邊的“Search”按鈕查看用戶信息(下圖省略其他列)
點擊需要更新證書用戶的最右邊列中的“Edit End Entity”超鏈接,編輯用戶
設置“Status”為“New”,點擊右邊的“Save”按鈕。然后輸入新密碼,其他項保持不變,點擊頁面最下方的“Save”按鈕保存設置
5、根證書
ejbca用戶界面,打開“Retrieve”—“Fetch CA Certificates”菜單,可以下載不同格式的根證書
6、tomcat服務器證書
用戶注冊時,證書模板選擇“SERVER”,CA選擇“dev”,Token選擇“JKS file”,其他項不變
下載證書時,在ejbca用戶界面中,打開“Enroll”—“Create Keystore”菜單,輸入用戶名與密碼,進入下面的頁面
“Key length”選擇“2048 bits”;“Certificate profile”選擇“SERVER”,點擊“Enroll”按鈕下載證書
七、調用ejbca接口實現私有CA
ejbca系統裝好了,可以管理數字證書了,但是,所有操作都在ejbca界面中執行,先不說全是英文,單單它里面配置項就多的人眼花繚亂,很多配置項要么是固定的,要么是不需要的,因此,最合理的做法是在ejbca之上構建中間層,用戶訪問中間層提供的服務,中間層調用ejbca的web service interface
1、superadmin.jks證書
ejbca提供的web service需要證書認證,證書格式:jks。對superadmin用戶執行更新操作,修改“Token”值為“JKS file”
保存,然后按照下載普通用戶證書的步驟下載superadmin的證書
2、初始化web service連接
將web service所需的jar包添加到工程中,這些jar包是下面兩個目錄下的所有jar
/usr/java/ejbca-ce-6.3.1.1/dist/ejbca-ws-cli/lib /usr/java/ejbca-ce-6.3.1.1/dist/ejbca-ws-cli
初始化web service
public void init() { if (!new File(certPath).exists()) return; CryptoProviderTools.installBCProvider(); System.setProperty("javax.net.ssl.trustStore", "d:/superadmin.jks"); System.setProperty("javax.net.ssl.trustStorePassword", "123456"); System.setProperty("javax.net.ssl.keyStore", "d:/superadmin.jks"); System.setProperty("javax.net.ssl.keyStorePassword", "123456"); QName qname = new QName("http://ws.protocol.core.ejbca.org/", "EjbcaWSService"); try { EjbcaWSService service = new EjbcaWSService(new URL("https://ca.xmyself.com:8443/ejbca/ejbcaws/ejbcaws?wsdl"), qname); EjbcaWS ejbcaWS = service.getEjbcaWSPort(); } catch (Exception e) { } }
注意:連接地址只能是域名,因此,連接ejbca的web service的機器要配置hosts
172.17.210.124 ca.xmyself.com
3、數字證書管理
查看用戶是否已經注冊
private boolean isExist(String username) throws Exception { UserMatch usermatch = new UserMatch(); usermatch.setMatchwith(UserMatch.MATCH_WITH_USERNAME); usermatch.setMatchtype(UserMatch.MATCH_TYPE_EQUALS); usermatch.setMatchvalue(username); try { List<UserDataVOWS> users = ejbcaWS.findUser(usermatch); if (users != null && users.size() > 0) { return true; } else { return false; } } catch (Exception e) { throw new Exception("檢查用戶 " + username() + " 是否存在時出錯:" + e.getMessage()); } }
用戶注冊與更新,用的都是editUser()方法,因此要先判斷是否存在
public void editUser() throws Exception { UserDataVOWS userData = new UserDataVOWS(); userData.setUsername("testname");//用戶名 userData.setPassword("123456");//密碼 userData.setClearPwd(false);//默認 userData.setSubjectDN("CN=" + "testname" + ",OU=" + "testou" + ",O=" + "testo" + ",C=cn" + ",telephoneNumber=" + "1234567890" );//設置唯一甄別名 String pattern = "yyyy-MM-dd HH:mm:ssZZ"; // ISO 8601標准時間格式 userData.setStartTime(DateFormatUtils.format(new Date(),pattern));//證書有效起始日期 userData.setEndTime(DateFormatUtils.format(DateUtils.addDays(new Date(), 100), pattern));//結束日期 userData.setCaName("test");//ca名稱,ejbca的名稱 userData.setSubjectAltName(null); userData.setEmail("test@test.com");//郵件地址 userData.setStatus(UserDataVOWS.STATUS_NEW);//狀態為new userData.setTokenType(UserDataVOWS.TOKEN_TYPE_P12);//設置p12格式證書 userData.setEndEntityProfileName("user");//終端實體模板 userData.setCertificateProfileName("user");//證書模板 try { ejbcaWS.editUser(userData); } catch (Exception e) { throw new Exception(e.getMessage()); } }
代碼中有幾處值得注意的,終端實體模板“user”和證書模板“user”需要在ejbca管理員界面中配置,並且終端實體模板“user”中要配置開啟“SubjectDN”的屬性如CN、OU、O、C、telephoneNumber等,還要允許修改startTime和endTime
吊銷證書
public void revoke(String username) throws ServiceException { try { ejbcaWS.revokeUser(username, RevokedCertInfo.REVOCATION_REASON_UNSPECIFIED, false); } catch (Exception e) { } }
創建證書
private void createCert(String username, String password, String path) throws Exception { FileOutputStream fileOutputStream = null; try { // 創建證書文件 KeyStore ksenv = ejbcaWS.pkcs12Req(username, password, null, "2048", AlgorithmConstants.KEYALGORITHM_RSA); java.security.KeyStore ks = KeyStoreHelper.getKeyStore(ksenv.getKeystoreData(), "PKCS12", password); fileOutputStream = new FileOutputStream(path + File.separator + username + ".p12"); ks.store(fileOutputStream, password.toCharArray()); // 創建密碼文件 File pwdFile = new File(path + File.separator + username + ".pwd"); pwdFile.createNewFile(); BufferedWriter out = new BufferedWriter(new FileWriter(pwdFile)); out.write(password); out.flush(); out.close(); } catch (Exception e) { throw new Exception("用戶 " + username + " 證書創建失敗:" + e.getMessage()); } finally { if (fileOutputStream != null) { try { fileOutputStream.close(); } catch (IOException e) { } } } }
證書創建在服務器上,用戶調用下載證書的接口服務,返回下載地址,因此,這里需要一個下載服務器,下面介紹將nginx配置為下載服務器,文件存放的目錄是/var/tmp + /download/
location ^~ /download/{ root /var/tmp; if ($request_filename ~* ^.*?\.(txt|doc|pdf|rar|gz|zip|docx|exe|xlsx|ppt|pptx)$){ add_header Content-Disposition: 'attachment;'; } }