十一 072015

Startssl 提供免费 SSL 证书,我们可以用其搭建 https 网站,过程简单记录如下:

1. 打开 http://www.startssl.com/ ,注册一个用户,注意这个网站是用证书验证用户身份的,所以证书一定备份好。
2. 登陆后进入 Validations Wizard,验证你要添加 ssl 支持的域名。
3. 进入 Certificates Wizard,并选择 Web Servers SSL/TLS Certificates。
4. 创建一个 private key,连同密码一起保管好。(也可以在服务器上用 openssl 创建,这里点skip就行了)
5. 选择一个域名,以及子域名。
6. 网站会给出证书,保存为 .csr 文件。
7. 使用 Tool Box 里面的 Decrypt Private Key 或者用 openssl 将第4步生成的 private key 解密,并将结果保存为 .key 文件。 Continue reading »

如何让GAE支持Https协议

Posted by 冰河 at 15:27 4 Responses » 25,455 Views
132010

以前上传Google Application Engine的程序都是使用Google App Engine Launcher,后来很久不用GAE就把Launcher卸载了,再后来就一直用SDUpload上传GAE的程序了。今天上传sa3upload程序(菜园子,就是大菠萝相册的作者写的网盘程序)的时候报错:

Error parsing yaml file:
Unexpected attribute ‘secure’ for object of type <class ‘google.appengine.api.appinfo.URLMap’>.
没办法只好再把Launcher装上,然后上传sa3upload程序就OK了。至于原因我想应该是SDUpload不支持attribute ‘secure’吧,毕竟SDUpload这是一个简易的第三方上传程序,在解析yaml文件的时候还是有所欠缺的。

以前没接触过secure属性(out了。。。),顺手查了查,原来是GAE的ssl协议(GAE真好啊,加一行代码就支持ssl了。独立的php服务器可是花十几$买ssl证书啊)。例如sa3upload程序的yaml文件如下:

application: ******
version: 1
runtime: python
api_version: 1

handlers:
- url: /favicon.ico
static_files: static/favicon.ico
upload: static/favicon.ico
mime_type: image/x-icon
expiration : “1d”

- url: /robots.txt
static_files: static/robots.txt
upload: static/robots.txt

- url: /static
static_dir: static
expiration : “1d”
secure: optional

- url: /rpc/.*
script: rpc.py
secure: optional

- url: /admin/.*
script: admin.py
secure: optional

- url: .*
script: main.py
secure: optional

其中secure: optional就代表支持https协议。secure共有三个属性,never、always、optional。

  1. never: 当我们使用https前缀访问时,会自动重定向到http。
  2. always: 当使用http访问时,会自动重定向到https。
  3. optional: 自由选择,http and https均可使用。

这样就可以使用https://访问了。https协议的作用相信大家都很清楚,尤其Gmail和Google Reader,访问的时候千万要用https。

222010

加密版Google搜索终于上线了,地址当然就是https://www.google.com。通过加密搜索,一切你发送出去的搜索关键字都会以加密方式传输,特别适合在公众网络和安全性不高的网络里使用Google搜索,以降低安全风险。

不过你也许注意到了,Google SSL搜索logo里有beta字样,也就是说Google还在进行完善。目前加密搜索只覆盖Google网页搜索,所以在这里你看不到指向地图搜索和图 片搜索的链接,因为它们还都不支持加密搜索,当然Google未来会逐步让其它搜索产品也支持加密。另外,由于SSL连接需要进行加密和解密,所以搜索速 度可能会有所下降。

另外需要注意的是,所谓的加密只是针对网络传输的过程加密,Google那边依然会存储你搜索的内容,以提升搜索质量。

完整的代码如下:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
/**
* JAVA操作SSL协议,通过Socket访问Https的程序代码例子。
*
*
*/
public class ReadHttpsURL {
// 默认的HTTPS 端口      visit
static final int HTTPS_PORT = 443;
public static void main(String argv[]) throws Exception {
// 受访主机
String host = “www.google.com”;
// 受访的页面
String url = “/adsense/?sourceid=aso&subid=ZH_CN-ET-AS-ADSBY6&medium=link&hl=zh_CN”;
// 自定义的管理器
X509TrustManager xtm = new Java2000TrustManager();
TrustManager mytm[] = { xtm };
// 得到上下文
SSLContext ctx = SSLContext.getInstance(“SSL”);
// 初始化
ctx.init(null, mytm, null);
// 获得工厂
SSLSocketFactory factory = ctx.getSocketFactory();
// 从工厂获得Socket连接
Socket socket = factory.createSocket(host, HTTPS_PORT);
// 剩下的就和普通的Socket操作一样了
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out.write(“GET ” + url + ” HTTP/1.0

“);
out.flush();
System.out.println(“start   work!”);
String line;
StringBuffer sb = new StringBuffer();
while ((line = in.readLine()) != null) {
sb.append(line + ”
“);
}
out.close();
in.close();
System.out.println(sb.toString());
}
}
/**
* 自定义的认证管理类。
*
*
*/
class Java2000TrustManager implements X509TrustManager {
Java2000TrustManager() {
// 这里可以进行证书的初始化操作
}
// 检查客户端的可信任状态
public void checkClientTrusted(X509Certificate chain[], String authType) throws CertificateException {
System.out.println(“检查客户端的可信任状态…”);
}
// 检查服务器的可信任状态
public void checkServerTrusted(X509Certificate chain[], String authType) throws CertificateException {
System.out.println(“检查服务器的可信任状态”);
}
// 返回接受的发行商数组
public X509Certificate[] getAcceptedIssuers() {
System.out.println(“获取接受的发行商数组…”);
return null;
}
}

在web应用交互过程中,有很多场景需要保证通信数据的安全;在前面也有好多篇文章介绍了在Web Service调用过程中用WS-Security来保证接口交互过程的安全性,值得注意的是,该种方式基于的传输协议仍然是Http,采用这种方式可扩 展性和数据交互效率比较高;另外一种实现方式就是用Https,他是在协议层对Http的再次封装,加入了SSL/TLS,采用该协议进行通信的数据全部 都会被加密,由于目前Web开发编程中对此都有了一定程度的封装,所以采用Https对外提供服务,除了证书以外,对编程能力的要求并不高,相对于前者门 槛较低,但是由于对双方通信的所有数据都进行加密,而且交互过程中还有多次握手等,所以效率较低;以下就介绍下在Java中访问Https链接时会出现的 一些问题;

在Java中要访问Https链接时,会用到一个关键类HttpsURLConnection;参见如下实现代码:

// 创建URL对象
URL myURL = new URL(“https://www.sun.com”);

// 创建HttpsURLConnection对象,并设置其SSLSocketFactory对象
HttpsURLConnection httpsConn = (HttpsURLConnection) myURL
.openConnection();

// 取得该连接的输入流,以读取响应内容
InputStreamReader insr = new InputStreamReader(httpsConn
.getInputStream());

// 读取服务器的响应内容并显示
int respInt = insr.read();
while (respInt != -1) {
System.out.print((char) respInt);
respInt = insr.read();
}

在取得connection的时候和正常浏览器访问一样,仍然会验证服务端的证书是否被信任(权威机构发行或者被权威机构签名);如果服务端证书不被信任,则默认的实现就会有问题,一般来说,用SunJSSE会抛如下异常信息:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

上面提到SunJSSE,JSSE(Java Secure Socket Extension)是实现Internet安全通信的一系列包的集合。它是一个SSL和TLS的纯Java实现,可以透明地提供数据加密、服务器认证、 信息完整性等功能,可以使我们像使用普通的套接字一样使用JSSE建立的安全套接字。JSSE是一个开放的标准,不只是Sun公司才能实现一个 SunJSSE,事实上其他公司有自己实现的JSSE,然后通过JCA就可以在JVM中使用。
关于JSSE的详细信息参考官网Reference:http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html
以及Java Security Guide:http://java.sun.com/j2se/1.5.0/docs/guide/security/

在深入了解JSSE之前,需要了解一个有关Java安全的概念:客户端的TrustStore文件。客户端的TrustStore文件中保存着被客 户端所信任的服务器的证书信息。客户端在进行SSL连接时,JSSE将根据这个文件中的证书决定是否信任服务器端的证书。在SunJSSE中,有一个信任 管理器类负责决定是否信任远端的证书,这个类有如下的处理规则:
1、若系统属性javax.net.sll.trustStore指定了TrustStore文件,那么信任管理器就去jre安装路径下的lib/security/目录中寻找并使用这个文件来检查证书。
2、若该系统属性没有指定TrustStore文件,它就会去jre安装路径下寻找默认的TrustStore文件,这个文件的相对路径为:lib/security/jssecacerts
3、若jssecacerts不存在,但是cacerts存在(它随J2SDK一起发行,含有数量有限的可信任的基本证书),那么这个默认的TrustStore文件就是lib/security/cacerts

那遇到这种情况,怎么处理呢?有以下两种方案:
1、按照以上信任管理器的规则,将服务端的公钥导入到jssecacerts,或者是在系统属性中设置要加载的trustStore文件的路径;证书导入可以用如下命令:keytool -import -file src_cer_file –keystore dest_cer_store;至于证书可以通过浏览器导出获得;
2、实现自己的证书信任管理器类,比如MyX509TrustManager,该类必须实现X509TrustManager接口中的三个method;然后在HttpsURLConnection中加载自定义的类,可以参见如下两个代码片段,其一为自定义证书信任管理器,其二为connect时的代码:

package test;

import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public class MyX509TrustManager implements X509TrustManager {

/*
* The default X509TrustManager returned by SunX509.  We’ll delegate
* decisions to it, and fall back to the logic in this class if the
* default X509TrustManager doesn’t trust it.
*/
X509TrustManager sunJSSEX509TrustManager;

MyX509TrustManager() throws Exception {
// create a “default” JSSE X509TrustManager.

KeyStore ks = KeyStore.getInstance(“JKS”);
ks.load(new FileInputStream(“trustedCerts”),
“passphrase”.toCharArray());

TrustManagerFactory tmf =
TrustManagerFactory.getInstance(“SunX509″, “SunJSSE”);

tmf.init(ks);

TrustManager tms [] = tmf.getTrustManagers();

/*
* Iterate over the returned trustmanagers, look
* for an instance of X509TrustManager.  If found,
* use that as our “default” trust manager.
*/
for (int i = 0; i < tms.length; i++) {
if (tms[i] instanceof X509TrustManager) {
sunJSSEX509TrustManager = (X509TrustManager) tms[i];
return;
}
}

/*
* Find some other way to initialize, or else we have to fail the
* constructor.
*/
throw new Exception(“Couldn’t initialize”);
}

/*
* Delegate to the default trust manager.
*/
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
sunJSSEX509TrustManager.checkClientTrusted(chain, authType);
} catch (CertificateException excep) {
// do any special handling here, or rethrow exception.
}
}

/*
* Delegate to the default trust manager.
*/
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
sunJSSEX509TrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException excep) {
/*
* Possibly pop up a dialog box asking whether to trust the
* cert chain.
*/
}
}

/*
* Merely pass this through.
*/
public X509Certificate[] getAcceptedIssuers() {
return sunJSSEX509TrustManager.getAcceptedIssuers();
}
}

// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance(“SSL”, “SunJSSE”);

sslContext.init(null, tm, new java.security.SecureRandom());

// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();

// 创建URL对象
URL myURL = new URL(“https://ebanks.gdb.com.cn/sperbank/perbankLogin.jsp”);

// 创建HttpsURLConnection对象,并设置其SSLSocketFactory对象
HttpsURLConnection httpsConn = (HttpsURLConnection) myURL.openConnection();
httpsConn.setSSLSocketFactory(ssf);

// 取得该连接的输入流,以读取响应内容
InputStreamReader insr = new InputStreamReader(httpsConn.getInputStream());

// 读取服务器的响应内容并显示
int respInt = insr.read();
while (respInt != -1) {
System.out.print((char) respInt);
respInt = insr.read();
}

对于以上两种实现方式,各有各的优点,第一种方式不会破坏JSSE的安全性,但是要手工导入证书,如果服务器很多,那每台服务器的JRE都必须做相同的操作;第二种方式灵活性更高,但是要小心实现,否则可能会留下安全隐患;

© 2009 - 2018 冰河的博客