`

使用 HTTPS 编写客户端程序

阅读更多

使用 HTTPS 编写客户端程序
如何在标准 URL 类中使用 HTTPS 协议

By Matt Towers
摘要
使用 HTTPS(Hypertext Transfer Protocol Secure 安全超文本传输协议)并非你所想的那样简单直接。如果你曾经尝试在 Java 客户端和 HTTPS 服务器之间进行安全的通讯,也许会注意到标准的 java.net.URL 类并不支持 HTTPS协议。这篇文章将向你展示,如何使用 JDK 1.2-compatible 虚拟机或微软的 JDK 1.1-compatible JView 来克服这些限制。
如果你曾经尝试在 Java 客户机和 HTTPS(安全超文本传输协议)服务器之间进行安全的通讯,也许会注意到标准的 java.net.URL 类并不支持 HTTPS 协议。服务端解决此问题的方法是非常简单明了的。因为现今几乎所有的Web服务器都使用 HTTPS 协议来提供查询数据的机制。一旦配置好你的服务器,任何浏览器只要简单地将 URL 地址中的协议指定成 HTTPS ,就能够在你的服务器上安全地进行信息查询。如果你没有搭建起 HTTPS 服务器,则可以在互联网上几乎所有 HTTPS 网页中测试你的客户端代码。在资料部分给出了一个列表,里面列出若干可供你进行 HTTPS 通讯测试的服务器地址。
然而从客户端的角度来看,在熟悉的 HTTP 后面简单的加上“S”就能够安全通信。这种简单性充满了迷惑性。事实上,浏览器在后台做了大量的工作,以保证没有任何人篡改或你所发送的请求数据。然而 HTTPS 协议用来加密的算法是 RSA Security 所拥有的专利(这种状况至少还要持续几个月)。该加密算法得到了浏览器制造商的许可,但 Sum Microsystems 公司却不同意将它绑定到标准的 Java URL 类实现中。这就导致当你创建 URL 对象时,若将协议指定为 HTTPS,就会抛出一个 MalformedURLException 异常。

幸运的是,为了解决这个局限,Java规格说明书提供为 URL 类选择一个代替的流句柄的能力。然而当你使用不同的虚拟机( virtual machine )时,此技术的实现方法也是不同的。在微软的 JDK 1.1-compatible 虚拟机 JView 中,微软许可该加密算法并提供了一个 HTTPS 流句柄作为它的 wininet 包的一部分。而SUN最近为它的 JDK 1.2-compatible 虚拟机发布了 Java Secure Sockets Extension(JSSE),在 JSSE 里许可并提供了 HTTPS 流句柄。本文将具体阐述如何使用 JSSE 和微软的 wininet 包来实现 HTTPS 流句柄。

JDK 1.2-compatible 虚拟机
在 JDK 1.2-compatible 虚拟机中使用 HTTPS 的技术主要依赖于 Java Secure Sockets Extension(JSSE)1.0.1 版本。你必须先安装 JSSE 并且将它添加到客户端虚拟机的类路径中,才能够使用这项技术。

当你安装好 JSSE 之后,你必须设置一项系统属性,将一个新的安全提供者添加到 Security 类对象。完成这项要求有若干种方法。鉴于这篇文章的目的,在这里介绍一种实用方法:

System.setProperty("java.protocol.handler.pkgs",
"com.sun.net.ssl.internal.[url]www.protocol[/url]");
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());


完成以上两个函数调用之后,运行如下所示代码时将不再抛出 MalformedURLException 异常:

URL url = new URL("https://[your server]");


如果连接标准的SSL端口443,可以忽略在URL字符串的后面添加端口号这一选项。但倘若你所连接的的Web服务器采用非标准端口进行SSL通讯,就需要像下面这样,在URL字符串后面添加该端口号:

URL url = new URL("https://[your server]:7002");


有些服务器拥有的可能是无签名或非法的 SSL 证明书。倘若涉及这类服务器的URL,使用此方法就需要注意。这种情况下,如果试图从URL的连接对象中检索输入或输出流时(例如运行以下代码),就会抛出一个 SSLException 异常,并显示 "untrusted server cer chain." 消息。如果该服务器拥有合法且有签名的证明书,则不会抛出任何异常。

URL url = new URL("https://[your server]");
URLConnection con = URL.openConnection();
//SSLException thrown here if server certificate is invalid
con.getInputStream();


最显而易见的解决上述问题的方法就是为你的服务器取得一个签名证明书。而 Java Developer Connectin forums 中为此问题成立了一个工作区,可以在这个URL地址中找到它们的相关信息:[url]http://forum.java.sun.com/forum?14@@.787ad8de.[/url]

Microsoft JView
由于微软和Sun公司关于在 Windows 平台上使用 Java 的许可问题有争论,微软的 JView 虚拟机现在仅仅是基于 JDK 1.1-compliant 的。而 JSSE 需要 1.2.2-compatible 或以上版本的虚拟机,因此上述的方法并不适用于在 JView 上运行的客户机。但是微软同样也提供了足够方便的 HTTPS 流句柄,将其作为 com.ms.net.wininet 包的一部分。

在 JView 环境中,只要为 URL 类调用一个简单静态函数就能够设置 HTTPS 流句柄:

URL.setURLStreamHandlerFactory(new
com.ms.net.wininet.WininetStreamHandlerFactory());


执行以上的函数调用之后,再运行下面的代码就不会抛出 MalformedURLException 异常:

URL url = new URL("https://[your server]");


使用这种方法需要注意两点。首先,根据JDK的文档,serURLStreamHandlerFactory 函数在一个虚拟机上最多只能被调用一次。之后的调用将会产生 Error。其次,正如在1.2虚拟机解决方案中所说,使用那些指向无签名或非法证明书的服务器的 URL 时必须要谨慎。同前所述,这种情况下试图向该 URL 地址的连接对象检索输入或输出数据流时,就会出问题。不过微软的流句柄抛出的是一个标准 IOExceptiony 异常,而不是 SSLException。

URL url = new URL("https://[your server]");
URLConnection con = url.openConnection();
//IOException thrown here if server certificate is invalid
con.getInputStream();


同样,解决此问题最显而易见的方法就是仅和那些拥有签名和合法证明书的服务器进行通讯。不过JView还提供了另外一个选择。将要向 URL 连接目标检索输入输出流之前,你可以先为 connection 对象调用 setAllowUserInteraction(true) 函数。JView 在运行时就会显示消息,向用户警告该服务器的证明书是非法的,用户可以选择是否继续。始终要记住的是,这样的消息在桌面应用程序中是合乎情理的,但是除了调试的目的外,让你的服务器任何情况下都弹出消息框可能是不可取的。

注意:你也可以在 JDK 1.2-compatible 版本的虚拟机中调用 setAllowUserInteraction() 函数。不过,在Sun的1.2虚拟机上(测试以下代码),即使将该函数的参数设成true,也不会显示消息框。

URL url = new URL("https://[your server]");
URLConnection con = url.openConnection();
//causes the VM to display a dialog when connecting
//to untrusted servers
con.setAllowUserInteraction(true);
con.getInputStream();


在 Windows NT4.0 ,Windows2000 和 Windows9x 操作系统中,com.ms.net.wininet 包被缺省安装到系统的类路径下。此外,根据微软的JDK文档,WinInetStreamHandlerFactory 是"… the same handler that is installed by default when running applets.",即运行applet时,同样的流句柄也会被缺省安装。

平台独立性
尽管上述的两种方法覆盖了大部分Java客户程序可能运行的平台,你的Java客户程序也许需要在 JDK 1.1 和 JDK 1.2-compliant 虚拟机上都可以正确运行。"写一次,在任何地方运行,"还记得吗?很自然会想到将这两种方法结合起来,根据运行的虚拟机执行相应的处理句柄。下面的代码展示了一种达到此目的的方法:

String strVendor = System.getProperty("java.vendor");
String strVersion = System.getProperty("java.version");
//Assumes a system version string of the form:
//[major].[minor].[release] (eg. 1.2.2)
Double dVersion = new Double(strVersion.substring(0, 3));

//If we are running in a MS environment, use the MS stream handler.
if( -1 < strVendor.indexOf("Microsoft") )
{
try
{
Class clsFactory =
Class.forName("com.ms.net.wininet.WininetStreamHandlerFactory" );
if ( null != clsFactory )
URL.setURLStreamHandlerFactory(
(URLStreamHandlerFactory)clsFactory.newInstance());
}
catch( ClassNotFoundException cfe )
{
throw new Exception("Unable to load the Microsoft SSL " +
"stream handler. Check classpath." + cfe.toString());
}
//If the stream handler factory has
//already been successfully set
//make sure our flag is set and eat the error
catch( Error err ){m_bStreamHandlerSet = true;}
}
//If we are in a normal Java environment,
//try to use the JSSE handler.
//NOTE: JSSE requires 1.2 or better
else if( 1.2 <= dVersion.doubleValue() )
{
System.setProperty("java.protocol.handler.pkgs",
"com.sun.net.ssl.internal.[url]www.protocol[/url]");
try
{
//if we have the JSSE provider available,
//and it has not already been
//set, add it as a new provide to the Security class.
Class clsFactory = Class.forName("com.sun.net.ssl.internal.ssl.Provider");
if( (null != clsFactory) && (null == Security.getProvider("SunJSSE")) )
Security.addProvider((Provider)clsFactory.newInstance());
}
catch( ClassNotFoundException cfe )
{
throw new Exception("Unable to load the JSSE SSL stream handler." +
"Check classpath." + cfe.toString());
}
}


关于applets
在 applet 中进行基于 HTTPS 的通讯,看起来似乎是上述内容的自然扩展。事实上,在大多数情况下applet中的HTTPS通讯更易于实现。在 Netscape Navigator 和 Internet Explorer 的4.0或更高版本中,它们各自的虚拟机都缺省许可HTTPS协议。因此,倘若你要在applet代码中创建一个HTTPS连接,只要在创建 URL实例时将协议名称指定为"HTTPS"便可。

URL url = new URL("https://[your server]");


如果客户端浏览器运行的是Sun公司的Java 2插件,那么当你使用 HTTPS 时还会遇到一些其他限制。关于在Java 2插件中使用 HTTPS 的详细讨论可以在Sun公司站点上找到(参看本文末的资料)。

结论
在应用程序中使用 HTTPS 协议,是一种快速而高效地在通讯中获得足够的安全性的方法。不幸的是,更多地出于法律而不是技术方面的原因,它没有被标准 Java 规格说明书所支持。无论如何,随着 JSSE 的产生以及微软 com.ms.net.wininet 包的使用,在大多数的平台上只需要少许几行代码就能够实现安全通讯。


关于作者
Matt Towers, 自称为eBozo,最近离开了他在Visio的职位。此后加入华盛顿西雅图的一个互联网公司PredictPoint.com ,在那里从事全职的Java开发工作。

资料

在本文中所描述的跨平台实现代码,是在一个叫做HttpsMessage 类中实现的。HttpsMessage 是HttpMessage 类的子类。HttpMessage 类的作者是Jason Hunter,即Java Servlet Programming(O'Reilly & Associates) 一书的作者. 在他即将出版的该书第二版中,你可以找到HttpsMessage 类。如果想要继承此类,必须下载并安装com.oreily.servlets 包。这个包以及相关子源代码可以在Hunter的站点上找到:
[url]http://www.servlets.com[/url]
你也可以下载HttpsMessage 类源码的压缩文件:
HttpsMessage.zip
以下是若干用于测试HTTPS通讯的网页地址:

[url]https://www.verisign.com/[/url]
[url]https://happiness.dhs.org/[/url]
[url]https://www.microsoft.com[/url]
[url]https://www.sun.com[/url]
[url]https://www.ftc.gov[/url]
更多关于JSSE、可下载位和安装指令的信息可以在Sun公司的站点上找到:
[url]http://java.sun.com/products/jsse/.[/url]
关于如何使用一些JSSE服务的描述,包括上文所提到的方法,都可以在O'Reilly网站上Jonathan Knudsen 所编写的"Secure Networking in Java"中找到:
[url]http://java.oreilly.com/bite-size/java_1099.html[/url]
更多关于WininetStreamHandlerFactory 类的信息可以在微软的JSDK文档中找到:[url]http://www.microsoft.com/java/sdk/[/url]。此外,Microsoft knowledge base还出版了"PRB: Allowing the URL class to access HTTPS in Applications":
[url]http://support.microsoft.com/support/kb/articles/Q191/1/20.ASP[/url]
关于在Java 2插件中使用HTTPS的更多信息可以在Sun站点的"How HTTPS Works in Java Plug-In "中找到:
[url]http://java.sun.com/products/plugin/1.2/docs/https.html[/url]

分享到:
评论

相关推荐

    使用 HTTPS 编写客户端程序.pdf

    使用HTTPS编写客户端程序是网络安全中的一个重要环节,特别是在涉及到敏感数据传输时,如在线支付、用户登录等场景。HTTPS协议基于HTTP,通过SSL/TLS协议提供加密处理、服务器身份验证和消息完整性检查,确保数据在...

    使用 HTTPS 编写客户端程序.docx

    标题中的“使用 HTTPS 编写客户端程序”表明了本文档主要关注的是如何使用HTTPS协议来构建客户端应用程序。HTTPS,全称Hypertext Transfer Protocol Secure,是一种安全的超文本传输协议,它结合了HTTP协议和SSL/TLS...

    C++编写客户端程序

    本文将深入探讨如何使用C++来编写客户端程序,以及涉及的相关知识点。 首先,客户端是用户与服务交互的一端,而服务器则是提供服务的另一端。在C++中,实现客户端程序通常涉及到网络编程的基本概念,如套接字...

    java编写的FTP客户端程序

    Java编写的FTP客户端程序是一种基于Java编程语言实现的软件,用于与FTP(文件传输协议)服务器进行交互。FTP是互联网上广泛使用的标准协议,用于在客户端和服务器之间传输文件。以下是一些关于Java FTP客户端程序的...

    如何编写DLL客户端程序(C#)

    本教程将指导你如何编写一个DLL客户端程序,通过实例源码深入理解这一过程。 首先,了解DLL的工作原理至关重要。DLL文件包含一组预编译的函数和类,可以在运行时被多个应用程序加载和调用,减少了内存占用并提高了...

    C#编写Modbus TCP客户端程序

    在本文中,我们将深入探讨如何使用C#语言编写一个Modbus TCP客户端程序,以及相关的技术要点。 首先,了解Modbus协议是至关重要的。Modbus是一种简单且广泛使用的通信协议,最初设计用于串行通信,但现在已扩展到...

    在力LINUX下用C编写MYSQL的客户端程序

    通过编写客户端程序,可以实现与MYSQL数据库的连接和交互,从而实现数据的读取、写入和修改等操作。此外,编写客户端程序还可以实现对数据库的管理和维护,包括创建表、删除表、添加索引等功能。因此,学习如何在...

    socket C语言编写 客户端和服务器端程序

    本文将深入讲解如何使用C语言来编写客户端和服务器端的socket程序,以便实现两者间的通信。 首先,我们要理解socket的基本概念。Socket,又称为套接字,是网络通信的一种接口,它允许两个进程(可以是同一台机器上...

    使用 PyQt5 编写的 TCP客户端程序

    TCP客户端连接程序是一个使用PyQt5编写的简单网络应用程序。它旨在通过TCP协议连接到指定的服务器,并能够向服务器发送数据并接收来自服务器的数据。该程序提供了一个用户友好的图形用户界面,允许用户输入服务器的...

    FTP客户端程序,c语言编写

    基于ftp协议编写的ftp客户端,支持多线程下载,断点下载等功能

    用C语言编写mysql客户端程序

    - **安全连接**:介绍如何编写客户端程序,使用 Secure Sockets Layer (SSL) 协议与服务器进行安全通信。 - **嵌入式服务器库**:演示如何编写使用 libmysqld 的应用程序,libmysqld 是一个嵌入式服务器库。 - **...

    如何编写DLL客户端程序

    以上就是关于如何编写DLL客户端程序的详细步骤,希望对你理解DLL的使用有所帮助。在实际开发中,根据具体需求,可能还需要处理异常处理、线程安全、版本兼容等问题,这些都是进一步学习和实践的方向。

    用MFC创建胖客户端应用程序范例

    本篇文章将探讨如何使用MFC创建胖客户端应用程序,同时也会提及相关的技术,如ATL、Setup和WebService。 首先,我们从标题"用MFC创建胖客户端应用程序范例"出发,理解其核心内容。MFC提供了许多面向对象的类,这些...

    Delphi编写soap服务器与客户端程序

    在本文中,我们将详细介绍使用 Delphi 编写 SOAP 服务器与客户端程序的过程。SOAP(Simple Object Access Protocol)是一种基于 XML 的协议,用于在不同平台之间交换结构化的信息。Delphi 作为一款功能强大的开发...

    Delphi编写soap服务器与客户端程序[收集].pdf

    Delphi编写soap服务器与客户端程序 在Delphi中编写soap服务器与客户端程序是比较常见的需求,以下是从标题、描述、标签和部分内容中生成的相关知识点: 一、SOAP服务器的建立 在Delphi中,建立SOAP服务器可以通过...

    Qt编写TCP客户端与服务器端

    Qt编写TCP通讯程序 客户端与服务器端超级详细教程,适合于可快速理解开发

    通过java编写客户端,服务器程序,实现聊天功能

    本教程将详细阐述如何通过Java编写客户端和服务器程序来实现这一功能。 首先,我们要理解客户端与服务器端的基本概念。客户端是发起请求的一方,而服务器端则是响应请求并提供服务的一方。在网络通信中,通常使用...

    mfc编写的ftp客户端程序 欢迎下载

    在本项目中,"mfc编写的ftp客户端程序"就是利用MFC库来实现一个FTP客户端应用。 FTP客户端程序通常包含以下核心功能: 1. **连接与断开**:客户端首先需要连接到FTP服务器,这涉及到解析服务器地址和端口号,建立...

    Delphi7.0开发OPC客户端程序(Siemens_Opc_client)[借鉴].pdf

    在编写客户端程序时,需要使用 Delphi(Pascal)代码。例如,连接 OPC 服务器可以使用以下代码: ```delphi OPCServer1.Connect1('OPC服务名称 (Intellution.OPCiFIX.1)', '结点名称 (OPC主机名称)'); ``` 设置更新...

    python 聊天客户端程序

    Python 提供了两个基本的 socket 模块。 第一个是 Socket,它提供了标准的 BSD Sockets API。 第二个是 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。

Global site tag (gtag.js) - Google Analytics