android 系统中使用的证书要求以BKS的库文件结构保存,通常情况下,我们使用java的keytool只能生成jks的证书库,如果生成BKS的则需要下载BC库,如是JDK1.6则下载bcprov-jdk16-141.jar,且将该文件放到jdk1.6.0_03\jre\lib\ext目录下,然后运行以下命令即可以生成BKS的证书库和相应的证书。
keytool -genkey -alias <别名> -keypass <密钥口令> -keyalg RSA -keysize 1024 -validity 365 -keystore <库文件名,如runcerts.keystore> -storepass <证书库密码> -dname "cn=runtestuser3, ou=vpn, o=run, c=CN, l=shanghai" -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider
Android client端
1. 该android客户端生成一个私钥文件
Keytools –genkey –alias clientkey –keystorebksclient.keystore –storetype BKS
2. 由kserver.keystore导出证书(BKS格式)
keytool -exportcert -alias kserverkey -keystorekserver.keystore -storetype BKS -filebksserver.crt
3. 从android的/system/etc/security中提取cacerts.bks信任证书,该证书是BKS格式的
4. 把向cacerts.bks导入证书(BKS)
keytool -importcert -keystorecacerts.bks -storetypeBKS -filebksserver.crt -providerorg.bouncycastle.jce.provider.BouncyCastleProvider -trustcacerts
5. 可以keytool –list –keystore“keystore文件名” 查看证书的信息
6. 将bksclient.keystore和cacerts.bks放到res/raw目录下
7. android代码:
public class AndroidSslActivity extends Activity{
private static final int SERVER_PORT =4444;//端口号
private static final String SERVER_IP = "服务器ip地址";//连接IP
private static final String CLIENT_KET_PASSWORD = "123456";//私钥密码
private static final String CLIENT_TRUST_PASSWORD = "changeit";//信任证书密码,该证书默认密码是changeit
private static final String CLIENT_AGREEMENT = "TLS";//使用协议
private static final String CLIENT_KEY_MANAGER = "X509";//密钥管理器
private static final String CLIENT_TRUST_MANAGER = "X509";//
private static final String CLIENT_KEY_KEYSTORE = "BKS";//密库,这里用的是BouncyCastle密库
private static final String CLIENT_TRUST_KEYSTORE = "BKS";//
private static final String ENCONDING = "utf-8";//字符集
private SSLSocket Client_sslSocket;
private Log tag;
private TextView tv;
private Button btn;
private Button btn2;
private Button btn3;
private EditText et;
@Override
public void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tv = (TextView)findViewById(R.id.textview01);
et = (EditText)findViewById(R.id.edittext01);
btn = (Button)findViewById(R.id.button01);
btn2 = (Button)findViewById(R.id.button02);
btn3 = (Button)findViewById(R.id.button03);
btn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0){
if(null != Client_sslSocket){
getOut(Client_sslSocket, et.getText().toString());
getIn(Client_sslSocket);
et.setText("here");
}
}
});
btn2.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0){
if (Client_sslSocket != null){
try {
Client_sslSocket.close();
Client_sslSocket = null;
} catch (IOException e){
e.printStackTrace();
}
}
}
});
btn3.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0){
init();
if (Client_sslSocket != null){
getIn(Client_sslSocket);
}else {
tag.i("AndroidSslActivity", "sslsocket= null");
}
}
});
}
public void init(){
try {
//取得SSL的SSLContext实例
SSLContext sslContext =SSLContext.getInstance(CLIENT_AGREEMENT);
//取得KeyManagerFactory和TrustManagerFactory的X509密钥管理器实例
KeyManagerFactory keyManager =KeyManagerFactory.getInstance(CLIENT_KEY_MANAGER);
TrustManagerFactory trustManager =TrustManagerFactory.getInstance(CLIENT_TRUST_MANAGER);
//取得BKS密库实例
KeyStore kks=KeyStore.getInstance(CLIENT_KEY_KEYSTORE);
KeyStore tks =KeyStore.getInstance(CLIENT_TRUST_KEYSTORE);
//加客户端载证书和私钥,通过读取资源文件的方式读取密钥和信任证书
kks.load(getBaseContext()
.getResources()
.openRawResource(R.raw.bksclient),CLIENT_KET_PASSWORD.toCharArray());
tks.load(getBaseContext()
.getResources()
.openRawResource(R.raw.cacerts),CLIENT_TRUST_PASSWORD.toCharArray());
//初始化密钥管理器
keyManager.init(kks,CLIENT_KET_PASSWORD.toCharArray());
trustManager.init(tks);
//初始化SSLContext
sslContext.init(keyManager.getKeyManagers(),trustManager.getTrustManagers(),null);
Log.i("AndroidSslActivity", "startssl connecting");
//生成SSLSocket
Client_sslSocket = (SSLSocket)sslContext.getSocketFactory().createSocket(SERVER_IP,SERVER_PORT);
Log.i("AndroidSslActivity", "sslconnected");
} catch (Exception e){
tag.e("AndroidSslActivity",e.getMessage());
}
}
public void getOut(SSLSocketsocket,String message){
PrintWriter out;
try {
out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream()
)
),true);
out.println(message);
} catch (IOException e){
e.printStackTrace();
}
}
public void getIn(SSLSocketsocket){
BufferedReader in= null;
String str = null;
try {
in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
str = new String(in.readLine().getBytes(),ENCONDING);
Log.i("AndroidSslActivity", "gettedstring : " + str);
} catch (UnsupportedEncodingExceptione) {
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
}
new AlertDialog
.Builder(AndroidSslActivity.this)
.setTitle("服务器消息")
.setNegativeButton("确定", null)
.setIcon(android.R.drawable.ic_menu_agenda)
.setMessage(str)
.show();
}
}
四、Trouble shooting
Connect refuse绝绝连接:ip地址不正确,用127.0.0.1也不行
Integrality checkedfail完整性检查失败:证书格式不对,android端用到的证书都要是BKS格式的,生成私钥以及想信任证书导入服务端证书时都要记得加上BKS和provider的参数
参考
http://aquariusoft.org/page/android/certs/
http://www.sunchis.com/html/java/javaweb/2010/0314/71.html
http://eddie.iteye.com/blog/78274
http://hi.baidu.com/renyijiu/blog/item/7e57a8a465ed60ff9052ee90.html
http://www.iteye.com/topic/88918
http://www.unix-center.net/bbs/archiver/?tid-4759.html
http://www.cnblogs.com/feisky/archive/2010/01/10/1643460.html
http://www.save-info.com/classic/2011/03/18/951
http://hi.baidu.com/bluewhale84/blog/item/f3bb20a1f538a9884710648c.html
http://www.blogjava.net/hadeslee/archive/2008/05/31/204867.html
http://qingzuochen.iteye.com/blog/404307
http://mywayscut.iteye.com/blog/671560
http://book.51cto.com/art/200707/51714.htm
http://soft.zdnet.com.cn/software_zone/2007/0829/476766.shtml
http://www.cublog.cn/u2/86974/showart_2197014.html
http://blog.youkuaiyun.com/hfeng101/article/details/10163627