/*
Copyright 2012-2013, Polyvi Inc. (http://polyvi.github.io/openxface)
This program is distributed under the terms of the GNU General Public License.
This file is part of xFace.
xFace is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
xFace is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with xFace. If not, see <http://www.gnu.org/licenses/>.
*/
package com.polyvi.xface.ssl;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import android.content.Context;
import com.polyvi.xface.configXml.XCertificateConifgParser;
import com.polyvi.xface.configXml.XXmlOperatorFactory;
import com.polyvi.xface.util.XConstant;
import com.polyvi.xface.util.XLog;
/**
* 管理与https有关证书库(KeyStore)及context对象
*/
public class XSSLManager {
private static final String CLASS_NAME = XSSLManager.class.getSimpleName();
private static final String CLIENT_CERTIFICATE_NAME = "client.p12";
private static final String CERTIFACATE_ALGORITHM = "PKCS12";
private static final String CERTIFICATE_FORMAT = "X509";
private static final String CERTIFICATE_KEY_FILENAME = "CertificateKey.xml";
private static final String TLS_PROTOCAL_NAME = "TLS";
protected static XSSLManager instance = null;
private Context mContext;
// 保存客户端证书信息
private XClientCertificate mClientCert;
private SSLContext mSslContext;
// 存储客户端证书的证书库
private KeyStore mKeyStore;
public static void createInstance(Context ctx) {
if (instance == null) {
instance = new XSSLManager(ctx);
}
}
public static XSSLManager getInstace() {
return instance;
}
protected XSSLManager(Context ctx) {
this.mContext = ctx;
init();
}
/**
* 初始化客户端证书 指定客户端证书进行身份验证.
*/
private void init() {
if (!isClientAuthenticationNeeded()) {
return;
}
String password = getCertificatePassword();
if (null == password) {
XLog.e(CLASS_NAME, "client certificate password cant't be null");
return;
}
this.mClientCert = new XClientCertificate(CLIENT_CERTIFICATE_NAME,
password, CERTIFACATE_ALGORITHM);
mKeyStore = this.createKeyStore(this.mClientCert);
mSslContext = this.createSSLContext();
try {
this.configureHttpsConnection();
} catch (IllegalArgumentException e) {
e.printStackTrace();
XLog.e(CLASS_NAME, "initClientCertificate fail.");
} catch (ClassNotFoundException e) {
e.printStackTrace();
XLog.e(CLASS_NAME,
"initClientCertificate fail caused by class not found.");
} catch (IllegalAccessException e) {
e.printStackTrace();
XLog.e(CLASS_NAME,
"initClientCertificate fail caused by field access failed.");
}
}
private String getCertificatePassword() {
XCertificateConifgParser parser = XXmlOperatorFactory
.createCertificateConifgParser();
byte[] buffer = new byte[2*XConstant.BUFFER_LEN];
try {
InputStream is = mContext.getAssets()
.open(CERTIFICATE_KEY_FILENAME);
int length = is.read(buffer);
byte[] decryptData = decryptKey(buffer, length);
parser.setInput(new ByteArrayInputStream(decryptData));
String password = parser.parseConfig();
is.close();
return password;
} catch (IOException e) {
XLog.e(CLASS_NAME, e.getMessage());
return null;
}
}
/**
* 判断是否需要支持客户端验证
*
* @return
*/
private boolean isClientAuthenticationNeeded() {
try {
for (String fileName : mContext.getAssets().list("")) {
if (fileName.equals(CLIENT_CERTIFICATE_NAME)) {
return true;
}
}
} catch (IOException e) {
return false;
}
return false;
}
/**
* 创建证书
*
* @param certificate
* @return
*/
private KeyStore createKeyStore(XClientCertificate certificate) {
InputStream certificateFileStream = getCertificateFileStream(certificate
.getCertificateName());
if (null == certificateFileStream) {
return null;
}
try {
KeyStore keyStore = KeyStore
.getInstance(certificate.getAlgorithm());
String password = certificate.getPassword();
keyStore.load(certificateFileStream,
password != null ? password.toCharArray() : null);
return keyStore;
} catch (KeyStoreException e) {
XLog.e(CLASS_NAME, e.getMessage());
return null;
} catch (NoSuchAlgorithmException e) {
XLog.e(CLASS_NAME, e.getMessage());
return null;
} catch (CertificateException e) {
XLog.e(CLASS_NAME, e.getMessage());
return null;
} catch (IOException e) {
XLog.e(CLASS_NAME, e.getMessage());
return null;
} finally {
try {
if (null != certificateFileStream) {
certificateFileStream.close();
}
} catch (IOException e) {
XLog.e(CLASS_NAME, e.getMessage());
return null;
}
}
}
/**
* 获取证书对应的流
*
* @param certificateName
* 证书名称
* @return
*/
private InputStream getCertificateFileStream(String certificateName) {
try {
InputStream certificateFileStream = mContext.getAssets().open(
certificateName);
return certificateFileStream;
} catch (IOException e) {
e.printStackTrace();
XLog.e(CLASS_NAME,
"getCertificateFileStream failed" + e.getMessage());
return null;
}
}
/**
* 创建SSL工作环境
*
* @return
*/
private SSLContext createSSLContext() {
try {
KeyManagerFactory keyManagerFactory = KeyManagerFactory
.getInstance(CERTIFICATE_FORMAT);
String password = mClientCert.getPassword();
keyManagerFactory.init(mKeyStore,
password != null ? password.toCharArray() : null);
SSLContext sslContext = SSLContext.getInstance(TLS_PROTOCAL_NAME);
sslContext.init(keyManagerFactory.getKeyManagers(),
mClientCert.getTrustManagers(), null);
return sslContext;
} catch (NoSuchAlgorithmException e) {
XLog.e(CLASS_NAME, e.getMessage());
return null;
} catch (KeyStoreException e) {
XLog.e(CLASS_NAME, e.getMessage());
return null;
} catch (UnrecoverableKeyException e) {
XLog.e(CLASS_NAME, e.getMessage());
return null;
} catch (KeyManagementException e) {
XLog.e(CLASS_NAME, e.getMessage());
return null;
}
}
/**
* 配置HttpsConnection对应SSL工作环境<br/>
* (android4.0以下有效)
*/
private void configureHttpsConnection() throws ClassNotFoundException,
IllegalArgumentException, IllegalAccessException {
Class<?> httpsConnection = Class
.forName("android.net.http.HttpsConnection");
Field[] fieldlist = httpsConnection.getDeclaredFields();
for (int i = 0; i < fieldlist.length; i++) {
Field field = fieldlist[i];
if (field.getName().equals("mSslSocketFactory")) {
field.setAccessible(true);
field.set(null, mSslContext.getSocketFactory());
}
}
}
public SSLContext getSslContext() {
return mSslContext;
}
public KeyStore getKeyStore() {
return mKeyStore;
}
/**
* 对byte[]进行解密
* @param data:要解密的数据
* @param length:解密数据的长度
* @return :解密后的数据
*/
protected byte[] decryptKey(byte[] data, int length) {
return data;
}
}