|
概念
JAVA使用keystore文件来存储所有KEY,keystore文件可以存放多个KEY,访问它需要密码。
下面我介绍下如何将用OpenSSL做自签名的证书一文中介绍的OpenSSL产生的KEY与JAVA的KEY转换后使用,从而达到JAVA与OpenSSL通信的目的。
用OpenSSL生成CA根证书,即(P1,V1)
此步骤参见用OpenSSL做自签名的证书一文
在JAVA环境下生成自己的KEY,即(P2,V2)
keytool -genkey -alias clientapp -keystore mycerts
注意:这里会提示输入访问keystore的密码,以及组织、城市、省份,一定要与CA根证书的一致,否则不能被CA签名。
从keystore中导出public key,即P2
keytool -keystore mycerts -certreq -alias clientapp -file clientapp.crs
用CA根证书为这个public key进行签名,即用V1给P2加密
openssl ca -out clientapp.pem -config ./openssl.cnf -infiles clientapp.crs
转换PEM到DER格式
openssl x509 -in clientapp.pem -out clientapp.der -outform DER
导入CA证书,即P1
keytool -keystore mycerts -alias systemca -import -file cacert.pem
导入用户证书,即被V1加密过的P2
keytool -keystore mycerts -alias clientapp -import -file clientapp.der
注意:这里一定要先导入CA证书再导入用户证书,否则会报错。
现在我们就生成了JAVA服务器使用的所有KEY了,在程序中将mycerts这个keystore导入就可以了。
如果客户端是使用OpenSSL的程序,那么用CA证书cacert.pem就能正常通信了,如果也是JAVA程序,那么我们需要将CA证书也转换成keystore:
keytool -import -keystore clikeystore -import -trustcacerts -file cacert.pem
生成的clikeystore供JAVA客户端使用,就能通信。
再附上SVR和CLI的JAVA程序,我已经用上面的KEY都测试通过:
SVR端:
import java.io.
* ;
import java.net.
* ;
import com.sun.net.ssl.KeyManagerFactory;
import com.sun.net.ssl.KeyManager;
import com.sun.net.ssl.TrustManagerFactory;
import com.sun.net.ssl.TrustManager;
import com.sun.net.ssl.SSLContext;
import javax.net.ServerSocketFactory;
import java.security.KeyStore;

public
class svr
implements Runnable
{
public
static final
int
PORT =
5555 ;
public
static final
String HOST =
"
localhost "
;
public
static final
String QUESTION
= "
Knock, knock. "
;
public
static final
String ANSWER =
"
Who's there? "
;

// The new constants that are used during setup.
public
static
final String KEYSTORE_FILE
=
" mycerts "
; //
"server_keystore";
public
static
final String ALGORITHM
=
" sunx509 "
;
public
static final
String PASSWORD
= "
churchillobjects "
;
public
static void
main(String[] args)
{
new Thread(
new svr()).start();
}
public
void run()
{
ServerSocket ss
=
null ;
try
{

// Local references used for clarity. Their presence
// here is part of the reason we need to import
// so many classes.
KeyManagerFactory kmf;
KeyManager[] km;
KeyStore ks;
TrustManagerFactory tmf;
TrustManager[] tm;
SSLContext sslc;
// Create a keystore that will read the JKS (Java KeyStore)
// file format which was created by the keytool utility.
ks =
KeyStore.getInstance(
" JKS "
);
// Load the keystore object with the binary keystore file and
// a byte array representing its password.
ks.load(
new FileInputStream(KEYSTORE_FILE), PASSWORD.toCharArray());
// Gives us a factory for key managers that will let
// us handle the asymetric keys we created earlier.
kmf
= KeyManagerFactory.getInstance(ALGORITHM);

// Initialize the key manager factory with the keystore object,
// again using the same password for security since it is going to
// access the private key.
kmf.init(ks, PASSWORD.toCharArray());
// Now we can get the key managers from the factory, since it knows
// what type we are using now.
km =
kmf.getKeyManagers();
// Next, create a trust manager factory using the same algorithm.
// This is to avoid using the certificates in cacerts that
// represent an authentication security risk.
tmf
= TrustManagerFactory.getInstance(ALGORITHM);
// then initialize it with the keystore object. This time we don't
// need the keystore password. This is because trusted certificates
// are not a sensitive element in the keystore, unlike the
// private keys.
tmf.init(ks);
// Once that's initialized, get the trust managers from the factory.
tm =
tmf.getTrustManagers();
// Almost done, we need a context object that will get our
// server socket factory. We specify TLS to indicate that we will
// need a server socket factory that supports SSL.
sslc
= SSLContext.getInstance(
" TLS "
);
// Initialize the context object with the key managers and trust
// managers we got earlier. The third parameter is an optional
// SecureRandom object. By passing in null, we are letting the
// context object create its own.
sslc.init(km, tm,
null );
// Finally, we get the ordinary-looking server socket factory
// from the context object.
ServerSocketFactory ssf
= sslc.getServerSocketFactory();
// From the factory, we simply ask for an ordinary-looking
// server socket on the port we wish.
ss =
ssf.createServerSocket(PORT);

listen(ss);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if (ss
!= null )
{
try
{
ss.close();
}
catch (IOException e)
{
// oh, well
}
}
System.exit(
0 );
}
}
static
void listen(ServerSocket ss)
throws Exception
{
System.out.println(
" Ready for connections.
" );
while (
true )
{
Socket s
= ss.accept();
BufferedWriter bw
=
new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
BufferedReader br
=
new BufferedReader(
new InputStreamReader(s.getInputStream()));
String q
= br.readLine();
if (
! QUESTION.equals(q))
{
throw
new RuntimeException(
" Wrong question: /
"" + q +
" / ""
);
}
System.out.println(
" Question: /
"" + q +
" / ""
);
bw.write(ANSWER
+ "
/n " );
bw.flush();
s.close();
}
}
}
CLI端程序:
import java.io.
* ;
import java.net.
* ;
import com.sun.net.ssl.KeyManagerFactory;
import com.sun.net.ssl.TrustManagerFactory;
import com.sun.net.ssl.SSLContext;
import java.security.KeyStore;
import javax.net.SocketFactory;

public
class cli
implements Runnable
{
public
static final
int
PORT =
5555 ;
public
static final
String HOST =
"
localhost "
;
public
static final
String KEYSTORE_FILE
= "
clikeystore "
; //
"client_keystore";
public
static
final String ALGORITHM
=
" sunx509 "
;
public
static final
String PASSWORD
= "
churchillobjects "
;
public
static final
String QUESTION
= "
Knock, knock. "
;
public
static final
String ANSWER =
"
Who's there? "
;
public
static void
main(String[] args)
{
new Thread(
new cli()).start();
}
public
void run()
{
Socket socket
=
null ;
try
{
KeyManagerFactory kmf;
KeyStore ks;
TrustManagerFactory tmf;
SSLContext sslc;

kmf
= KeyManagerFactory.getInstance(ALGORITHM);
ks
= KeyStore.getInstance(
" JKS
" );
ks.load(
new FileInputStream(KEYSTORE_FILE), PASSWORD.toCharArray());
kmf.init(ks, PASSWORD.toCharArray());
tmf
= TrustManagerFactory.getInstance(ALGORITHM);
tmf.init(ks);
sslc
= SSLContext.getInstance(
" TLS
" );
sslc.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
null );

// The process is different from here on the client. Instead of
// getting a ServerSocketFactory, we ask for a SocketFactory from
// the SSL context.
SocketFactory sf
= sslc.getSocketFactory();

// Then we get the socket from the factory and treat it
// as if it were a standard (plain) socket.
socket
= sf.createSocket(HOST, PORT);
doQuery(socket);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if (socket
!= null )
{
try
{
socket.close();
}
catch (IOException e)
{
// oh, well
}
}
System.exit(
0 );
}
}

private
void doQuery(Socket s)
throws Exception
{
BufferedWriter bw
=
new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
BufferedReader br
=
new BufferedReader(
new InputStreamReader(s.getInputStream()));
bw.write(QUESTION
+ "
/n " );
bw.flush();
String response
= br.readLine();
if (
! ANSWER.equals(response))
{
throw
new RuntimeException(
" Wrong answer: /
"" + response +
" / ""
);
}
System.out.println(
" Got the right answer: /
"" + response +
" /
"" );
}
}
|