亚洲成精品动漫久久精久,九九在线精品视频播放,黄色成人免费观看,三级成人影院,久碰久,四虎成人欧美精品在永久在线

掃一掃
關注微信公眾號

對等計算實踐: P2P 遇上 SSL
2005-12-02   

對任何重要的 P2P 應用程序而言,對等機之間的安全通信都是一個核心要求。盡管安全的細節依賴于如何使用該應用程序和該應用程序將要保護什么,但通過使用現有技術,例如 SSL 實現強壯的、一般用途的安全通常是可能的。本月,Todd Sundsted 演示如何在 P2P 安全中使用 SSL(通過 JSSE)。

我們考察了 p2p 應用程序中的信任角色。信任的等級是衡量我們確信程度的尺度,即我們正與之通信的人是否是我們以為的那個人,以及我們正訪問的資源是否是我們以為的那些。我們也研究了用于在所有分布式應用程序,包括 p2p 應用程序中建立信任的三個構件:認證、授權和加密。

現在我們將通過修改我們的簡單 p2p 應用程序把上個月的課程應用到實踐中。特別地,我們將用 X.509 證書擴展該應用以支持 P2P 認證和加密。我們將在將來的文章中處理授權問題。

安全認證
一個應用程序的安全需求在很大程度上依賴于將如何使用該應用程序和該應用程序將要保護什么。不過,用現有技術實現強大的、一般用途的安全通常是可能的。認證就是一個很好的示例。

當顧客想從 Web 站點購買某個產品時,顧客和 Web 站點都要進行認證。顧客通常是以提供名字和密碼的方式來認證他自己。另一方面,Web 站點通過交換一塊簽名數據和一個有效的 X.509 證書(作為 SSL 握手的一部分)來認證它自己。顧客的瀏覽器驗證該證書并用所附的公用密鑰驗證簽名數據。一旦雙方都認證了,則交易就可以開始了。

SSL 能用相同的機制處理服務器認證(就如在上面的示例中)和客戶機認證。Web 站點典型地對客戶機認證不依賴 SSL ― 要求用戶提供密碼是較容易的。而 SSL 客戶機和服務器認證對于透明認證是完美的,對等機 ― 如 p2p 應用程序中的對等機之間一定會發生透明認證。

安全套接字層(Secure Sockets Layer(SSL)) SSL 是一種安全協議,它為網絡(例如因特網)的通信提供私密性。SSL 使應用程序在通信時不用擔心被竊聽和篡改。

SSL 實際上是共同工作的兩個協議:“SSL 記錄協議”(SSL Record Protocol)和“SSL 握手協議”(SSL Handshake Protocol)。“SSL 記錄協議”是兩個協議中較低級別的協議,它為較高級別的協議,例如 SSL 握手協議對數據的變長的記錄進行加密和解密。SSL 握手協議處理應用程序憑證的交換和驗證。

當一個應用程序(客戶機)想和另一個應用程序(服務器)通信時,客戶機打開一個與服務器相連接的套接字連接。然后,客戶機和服務器對安全連接進行協商。作為協商的一部分,服務器向客戶機作自我認證。客戶機可以選擇向服務器作或不作自我認證。一旦完成了認證并且建立了安全連接,則兩個應用程序就可以安全地進行通信。按照慣例,我將把發起該通信的對等機看作客戶機,另一個對等機則看作服務器,不管連接之后它們充當什么角色。

在 Java 應用程序如何使用 SSL
用于 Java 應用程序的 SSL 由“Java 安全套接字擴展”(Java Secure Socket Extension(JSSE))提供。JSSE 是最近發布的 JDK 1.4 Beta 測試版的一個標準部件,但對早些版本的 Java 平臺它是作為一個擴展可用的。

JSSE 用 SSL 作它的安全套接字的底層機制。JSSE 安全套接字除了支持透明認證和加密之外,其工作方式與常規套接字相似。因為它們看起來也與普通套接字(它們是類 和類 的子類)相似,所以使用 JSSE 的多數代碼不用修改。受到影響最多的代碼是那些處理安全套接字工廠(secure socket factory)的創建和初始化的代碼。

如果您想在早于版本 1.4 的 Java 平臺中使用 JSSE,那么您將不得不自己去下載并安裝 JSSE 擴展。安裝說明非常簡單,所以我不想在這里重復。

模型
圖 1 說明在對等機之間的通信中 SSL 將充當的角色。

圖 1. 工作中的 SSL
圖 1. 工作中的 SSL

名為 A 和 B 的兩臺對等機想安全地進行通信。 在我們簡單的 p2p 應用程序的環境中,對等機 A 想查詢對等機 B 上的一個資源。

每個對等機都有包含其專用密鑰的一個數據庫(名為 keystore)和包含其公用密鑰的證書。密碼保護數據庫的內容。該數據庫還包含一個或多個來自被信任的對等機的自簽名證書。

對等機 A 發起這項事務,每臺對等機相互認證,兩臺對等機協商采用的密碼及其長度并建立一個安全通道。完成這些操作之后,每個對等機都知道它正在跟誰交談并且知道通道是安全的。

初始化
因為 JSSE 和 SSL 的介紹對初始化代碼有很大影響,所以讓我們來考察對等機 A 中負責初始化的代碼。

清單 1. 安全初始化代碼

   // Each peer has an identity that must be locally (but not globally)
   // unique.  This identity and its associated public and private keys
   // are stored in a keystore and protected by a password.  Each
   // peer also has a name that must be globally unique.
   String stringIdentity = null;
   String stringPassword = null;
   String stringName = null;

   // The code that prompts the user for his/her identity
   // and password goes here.  the user's name is
   // generated (if necessary) later.

   // Create home directory.  This is a very portable way
   // to create a home directory, but it has its problems --
   // the various flavors of Microsoft Windows put the directory
   // in widely different locations in the directory hierarchy.
   String stringHome = System.getProperty("user.home") + File.separator + "p2p";
   File fileHome = new File(stringHome);
   if (fileHome.exists() == false)
     fileHome.mkdirs();

   // Create keystore.  We must run an external process to create the
   // keystore, because the security APIs don't expose enough
   // functionality to do this inline.  I haven't tested this widely enough
   // to know how portable this code is, but it works on everything I
   // tried it on.
   String stringKeyStore = stringHome + File.separator + "keystore";
   File fileKeyStore = new File(stringKeyStore);
   if (fileKeyStore.exists() == false)
   {
     System.out.println("Creating keystore...");
     byte [] arb = new byte [16];
     SecureRandom securerandom = SecureRandom.getInstance("SHA1PRNG");
     securerandom.nextBytes(arb);
     stringName = new String(Base64.encode(arb));
     String [] arstringCommand = new String []
     {
       System.getProperty("java.home") + File.separator + "bin" + File.separator + "keytool",
       "-genkey",
       "-alias", stringIdentity,
       "-keyalg", "RSA",
       "-keysize", "1024",
       "-dname", "CN=" + stringName,
       "-keystore", stringHome + File.separator + "keystore",
       "-keypass", stringPassword,
       "-storetype", "JCEKS",
       "-storepass", stringPassword
     };
     Process process = Runtime.getRuntime().exec(arstringCommand);
     process.waitFor();
     InputStream inputstream2 = process.getInputStream();
     IOUtils.copy(inputstream2, System.out);
     InputStream inputstream3 = process.getErrorStream();
     IOUtils.copy(inputstream3, System.out);
     if (process.exitValue() != 0)
       System.exit(-1);
   }

   // Once the application has created/located the keystore, it
   // opens it and creates a KeyStore instance from the data
   // in it.
   char [] archPassword = stringPassword.toCharArray();
   FileInputStream fileinputstream = new FileInputStream(stringHome + File.separator +

     "keystore");
   KeyStore keystore = KeyStore.getInstance("JCEKS");
   try
   {
     keystore.load(fileinputstream, archPassword);
   }
   catch (IOException ioexception)
   {
     System.out.println("Cannot load keystore.  Password may be wrong.");
     System.exit(-3);
   }
   if (keystore.containsAlias(stringIdentity) == false)
   {
     System.out.println("Cannot locate identity.");
     System.exit(-2);
   }

   // Create key manager.  The key manager holds this peer's
   // private key.
   KeyManagerFactory keymanagerfactory = KeyManagerFactory.getInstance("SunX509");
   keymanagerfactory.init(keystore, archPassword);
   KeyManager [] arkeymanager = keymanagerfactory.getKeyManagers();

   // Create trust manager.  The trust manager hold other peers'
   // certificates.
   TrustManagerFactory trustmanagerfactory = TrustManagerFactory.getInstance("SunX509");
   trustmanagerfactory.init(keystore);
   TrustManager [] artrustmanager = trustmanagerfactory.getTrustManagers();

   // Create SSL context.
   SSLContext sslcontext = SSLContext.getInstance("SSL");
   SecureRandom securerandom = SecureRandom.getInstance("SHA1PRNG");
   sslcontext.init(arkeymanager, artrustmanager, securerandom);

   // Create factories.
   m_socketfactory = sslcontext.getSocketFactory();
   m_serversocketfactory = sslcontext.getServerSocketFactory();
   m_keystore = keystore;

當用戶第一次啟動應用程序時,應用程序提示輸入一個身份(別名)和一個密碼。身份只用于對對等機進行本地識別 ― 它沒有全局意義。應用程序生成一個隨機的 128 位(16 字節)的字符串,應用程序用這個字符串對對等機進行全局識別并將它轉換成字母數字字符串。應用程序用身份、密碼和名字創建 keystore 和公開/專用密鑰對,密鑰對存儲在 keystore 中。密碼保護 keystore 中的信息。

我確信您已經注意到我創建初始 keystore 時采取的辦法。應用程序把 keytool 啟動為一個外部進程,keytool 創建 keystore。我不得不這樣做,因為公共的 Java 安全 API 不提供創建證書的工具 ― 這些功能隱藏在 JSSE 中,它的 API 未發布。啟動 keytool 來創建 keystore 的最大缺點是要冒用戶提供的密碼被公開的風險,用戶提供的密碼作為參數列表的一部分被傳遞進去。

應用程序創建了 keystore 之后,就打開 keystore 并將它裝入內存(如果我們已經能夠直接創建 keystore,則我們就可以免去這一步驟)。應用程序從 keystore 創建一個 密鑰管理器(key manager)和一個 信任管理器(trust manager)。密鑰管理器管理密鑰,這些密鑰用于在安全套接字對面相對應用程序的對等機來認證它。信任管理器管理證書,這些證書用于對位于安全套接字另一端的對等機進行認證。

最后,應用程序創建一個 實例,這個實例充當安全套接字工廠的工廠。應用程序創建 類的安全版本并將它們用于稍后的通信。

用戶界面
余下代碼的大部分跟我們的前發行版是相同的。這是 JSSE 帶來的好處之一 ― 用 JSSE 創建的安全套接字看起來和常規套接字相似,工作起來也和常規套接字相似。支持代碼也沒什么不同(除了創建和初始化代碼必須修改外)。然而,還是有和用戶界面有關的改動。大部分這些改動是受到了我想讓這個應用程序對那些只想快速地設置這個應用程序來試驗的愿望而激發的。

作為對讀者反饋的回應,我改善了命令行界面。對等機及其資源存在于一個層次結構中,用戶用命令 lscd訪問這個結構。 ls 命令列出層次結構中某個特定點的內容(與 UNIX 的 ls 命令或 DOS 的 dir命令所做的相似), cd 命令改變層次結構中的當前位置(再一次與 UNIX 和 DOS 中的同名命令相似)。 cd ..用于在層次結構中進行回退。

為使應用程序更容易使用,我已經創建了兩個 zip 文件,每一個都包含該應用程序的一個已經適當配置了的實例。一個實例在端口 7776 進行通信,另一個在端口 7777。它們也都已被配置成彼此認證,為的是能夠建立安全通道。這些 zip 文件的名稱為 peerA.zip 和 peerB.zip。“自述文件”(README)提供了更多詳細的使用說明。

結束語
在 p2p 應用程序中加入 SSL 是提供簡單卻強大的安全的極好辦法。我們將繼續我們的 P2P 之旅,考察更成熟的對等機發現方法。


熱詞搜索:

上一篇:配置Tomcat 4使用SSL
下一篇:SSL的實際運作過程

分享到: 收藏