日记 - 两种HTTPS请求,以及私钥和证书的生成

开篇必看

  我本机环境:

  win7、idea2016、jdk1.8、maven、tomcat7

  我的每一个实现都包含服务端和客户端,分别为TCP实现和HTTP实现。其中TCP实现仅靠jdk本身jar包就可  实现,HTTP实现需要依赖其他jar包。

  文章中斜体字都是我添加的需要注意的额外解释。

  首先,添加maven依赖。

<dependency>
   <groupId>commons-httpclient</groupId>
   <artifactId>commons-httpclient</artifactId>
   <version>3.1</version>
</dependency>
<dependency>
   <groupId>com.google.code.gson</groupId>
   <artifactId>gson</artifactId>
   <version>2.8.0</version>
</dependency>

TCP实现

  参考文章:http://kingj.iteye.com/blog/2103662。

  该文章是我的主要学习资料,但是我实现后根据其他文章改动了一些地方,并对实现本身进行了重构(精简),当然也可以完全依照该文章进行TCP的HTTPS实现。

  首先打开cmd去生成私钥库,命令行:keytool -genkey -alias password -keypass password -keyalg RSA -keysize 1024 -keystore https.keystore -storepass password

  命令执行后,会在命令行所处文件夹内生成私钥库‘https.keystore’。

  接着根据私钥生成证书,命令行:keytool -export -keystore https.keystore -alias password -file https.crt -storepass password

  同样的,执行后会在所处文件夹内生成证书‘https.crt’。

  注意!!命令行中password是你自己的私钥口令。该命令会生成私钥库‘https.keystore’,而私钥密码就是读取私钥库时所需的口令。这个口令由个人进行自定义,需要特别记下来。这里没有生成公钥,因为公钥可通过证书获得

  有了证书后就直接上代码了,需要注意的地方我都在代码中进行了注释

import javax.crypto.*;
import java.security.*;
import java.util.Random;

/** 加解密,字符串与字节的转换 */
public class HttpsMockBase {

    protected static PrivateKey privateKey;
protected static PublicKey publicKey;
protected static String hash;

public static boolean byteEquals(byte a[],byte[] b){
        boolean equals = true;
if(a == null || b == null){
            equals = false;
}

        if(a != null && b != null){
            if(a.length != b.length){
                equals = false;
}else{
                for(int i = 0;i<a.length;i++){
                    if(a[i] != b[i]){
                        equals = false;
break;
}
                }
            }
        }
        return equals;
}

    public static byte[] decrypt(byte data[]) throws Exception{
        // 对数据解密
Cipher cipher  =  Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
    public static byte[] decrypt(byte data[],SecureRandom seed) throws Exception{
        // 对数据解密
Cipher cipher  =  Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey,seed);
return cipher.doFinal(data);
}

    public static byte[] decryptByPublicKey(byte data[],SecureRandom seed) throws Exception{
        if(publicKey == null){
            publicKey = CertifcateUtils.readPublicKeys();
}
        // 对数据解密
Cipher cipher  =  Cipher.getInstance(publicKey.getAlgorithm());
if(seed == null){
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
}else{
            cipher.init(Cipher.DECRYPT_MODE, publicKey,seed);
}

        return cipher.doFinal(data);
}

    public static byte[] decryptByDes(byte data[],SecureRandom seed) throws Exception{
        if(publicKey == null){
            publicKey = CertifcateUtils.readPublicKeys();
}
        // 对数据解密
Cipher cipher  =  Cipher.getInstance("DES");
if(seed == null){
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
}else{
            cipher.init(Cipher.DECRYPT_MODE, publicKey,seed);
}

        return cipher.doFinal(data);
}

    public static byte[] encryptByPublicKey(byte[] data, SecureRandom seed)
            throws Exception {
        if(publicKey == null){
            publicKey = CertifcateUtils.readPublicKeys();
}
        // 对数据加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
if(seed == null){
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
}else{
            cipher.init(Cipher.ENCRYPT_MODE, publicKey,seed);
}

        return cipher.doFinal(data);
}

    public static String byte2hex(byte[] b) {
        String hs  =  "";
String stmp  =  "";
for (int n  =  0; n < b.length; n++) {
            stmp  =  (Integer.toHexString(b[n] & 0XFF));
if (stmp.length() == 1) {
                hs  =  hs + "0" + stmp;
} else {
                hs  =  hs +"  " + stmp;
}
        }
        return hs.toUpperCase();
}

    public static byte[] cactHash(byte[] bytes) {
        byte[] _bytes  =  null;
try {
            MessageDigest md  =  MessageDigest.getInstance(hash);
md.update(bytes);
_bytes  =  md.digest();
} catch (NoSuchAlgorithmException ex) {
            ex.printStackTrace();
}
        return _bytes;
}

    static String random() {
        StringBuilder builder = new StringBuilder();
Random random = new Random();
int seedLength = 10;
for(int i = 0;i<seedLength;i++){
            builder.append(digits[random.nextInt(seedLength)]);
}

        return builder.toString();
}

    static char[] digits = {
            '0','1','2','3','4',
'5','6','7','8','9',
'a','b','c','d','e',
'f','g','h','i','j'
};
}

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.security.Key;
import java.security.SecureRandom;

/** 客户端 */
public class HttpsMockClient extends  HttpsMockBase {

    static DataInputStream in;
static DataOutputStream out;
static Key key;

//启动客户端,连接服务端
public static void main(String args[]) throws  Exception{
        int port = 80;
Socket socket = new Socket("localhost",port);
socket.setReceiveBufferSize(102400);
socket.setKeepAlive(true);
in = new DataInputStream(socket.getInputStream());
out = new DataOutputStream(socket.getOutputStream());
shakeHands();

System.out.println("------------------------------------------------------------------");
String name = "duck";
writeBytes(name.getBytes());

int len = in.readInt();
byte[] msg = readBytes(len);
System.out.println("服务器反馈消息:" + byte2hex(msg));
Thread.sleep(1000*100);
}

    //握手
private static void shakeHands() throws Exception {
        //第一步 客户端发送自己支持的hash算法
hash = "SHA1";
int length = hash.getBytes().length;
out.writeInt(length);
SocketUtils.writeBytes(out, hash.getBytes(), length);

//第二步 客户端验证服务器端证书是否合法
int skip = in.readInt();
byte[] certificate = SocketUtils.readBytes(in,skip);
java.security.cert.Certificate cc = CertifcateUtils.createCertiface(certificate);
publicKey = cc.getPublicKey();
cc.verify(publicKey);
System.out.println("客户端校验服务器端证书是否合法:" + true);

//第三步  生成随机数密码并用公钥加密后进行发送
SecureRandom seed = new SecureRandom();
int seedLength = 2;
byte seedBytes[] = seed.generateSeed(seedLength);
System.out.println("生成的seed随机数为 : " + byte2hex(seedBytes));
System.out.println("将seed随机数用公钥加密后发送到服务器");
byte[] encrptedSeed = encryptByPublicKey(seedBytes, null);
SocketUtils.writeBytes(out,encrptedSeed,encrptedSeed.length);
System.out.println("加密后的seed值为 :" + byte2hex(encrptedSeed));

//第四步  客户端生成消息,用随机数密码加密后发送给服务端,并将消息生成的摘要再次发送给服务端
String message = random();
System.out.println("客户端生成消息为:" + message);
System.out.println("使用随机数并用公钥对消息加密");
byte[] encrpt = encryptByPublicKey(message.getBytes(),seed);
System.out.println("加密后消息位数为 : " + encrpt.length);
SocketUtils.writeBytes(out,encrpt,encrpt.length);
System.out.println("客户端使用SHA1计算消息摘要");
byte hash[] = cactHash(message.getBytes());
System.out.println("摘要信息为:"+byte2hex(hash));
System.out.println("消息加密完成,摘要计算完成,发送服务器");
SocketUtils.writeBytes(out,hash,hash.length);

//第五步  接受服务端发送来的加密消息和摘要
int serverMessageLength = in.readInt();
byte[] serverMessage = SocketUtils.readBytes(in,serverMessageLength);
System.out.println("服务器端的消息内容为 :" + byte2hex(serverMessage));
System.out.println("开始用之前生成的随机密码和DES算法解密消息,密码为:"+byte2hex(seedBytes));
byte[] desKey =  DesCoder.initSecretKey(new SecureRandom(seedBytes));
key = DesCoder.toKey(desKey);
byte[] decrpytedServerMsg = DesCoder.decrypt(serverMessage, key);
System.out.println("解密后的消息为:"+byte2hex(decrpytedServerMsg));
int serverHashLength = in.readInt();
byte[] serverHash = SocketUtils.readBytes(in,serverHashLength);
System.out.println("开始接受服务器端的摘要消息:"+byte2hex(serverHash));
byte[] serverHashValues = cactHash(decrpytedServerMsg);
System.out.println("计算服务器端发送过来的消息的摘要 : " +byte2hex(serverHashValues));

//第六步  判断服务器端发送过来的hash摘要是否和计算出的摘要一致
boolean isHashEquals = byteEquals(serverHashValues,serverHash);
if(isHashEquals){
            System.out.println("验证完成,握手成功");
}else{
            System.out.println("验证失败,握手失败");
}
    }


    public static byte[] readBytes(int length) throws  Exception{
        byte[] undecrpty = SocketUtils.readBytes(in,length);
System.out.println("读取未解密消息:"+byte2hex(undecrpty));
return DesCoder.decrypt(undecrpty,key);
}

    public static void writeBytes(byte[] data) throws  Exception{
        byte[] encrpted = DesCoder.encrypt(data,key);
System.out.println("写入加密后消息:"+byte2hex(encrpted));
SocketUtils.writeBytes(out,encrpted,encrpted.length);
}

}

import javax.net.ServerSocketFactory;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.Key;
import java.security.SecureRandom;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/** 服务端 */
public class HttpsMockServer extends HttpsMockBase {

    static DataInputStream in;
static DataOutputStream out;
static String hash;
static Key key;
static ExecutorService executorService = Executors.newFixedThreadPool(20);

//启动服务端
public static void main(String args[]) throws Exception{
        int port = 80;
ServerSocket ss = ServerSocketFactory.getDefault().createServerSocket(port);
ss.setReceiveBufferSize(102400);
ss.setReuseAddress(false);
while(true){
            try {
                final Socket s = ss.accept();
doHttpsShakeHands(s);
executorService.execute(new Runnable() {
//                    @Override
public void run() {
                        doSocketTransport(s);
}
                });
}catch (Exception e){
                e.printStackTrace();
}
        }
    }

    //这里是握手成功后进行的消息发送,根据需要自定义
private static void doSocketTransport(Socket s){
        try{
            System.out.println("--------------------------------------------------------");
int length = in.readInt();
byte[] clientMsg = readBytes(length);
System.out.println("客户端指令内容为:" + byte2hex(clientMsg));
writeBytes("服务器已经接受请求".getBytes());
} catch (Exception ex) {
            ex.printStackTrace();
}
    }

    public static byte[] readBytes(int length) throws Exception{
        byte[] undecrpty = SocketUtils.readBytes(in,length);
System.out.println("读取未解密消息:"+byte2hex(undecrpty));
return DesCoder.decrypt(undecrpty,key);
}

    public static void writeBytes(byte[] data) throws  Exception{
        byte[] encrpted = DesCoder.encrypt(data,key);
System.out.println("写入加密后消息:"+byte2hex(encrpted));
SocketUtils.writeBytes(out,encrpted,encrpted.length);
}

    //握手
private static void doHttpsShakeHands(Socket s) throws Exception {
        in = new DataInputStream(s.getInputStream());
out = new DataOutputStream(s.getOutputStream());

//第一步  获取客户端发送的支持的验证规则,包括hash算法,这里选用SHA1作为hash,后面用来计算消息摘要
int length = in.readInt();
in.skipBytes(4);
byte[] clientSupportHash = SocketUtils.readBytes(in,length);
String clientHash = new String(clientSupportHash);
hash = clientHash;
System.out.println("客户端发送了hash算法为:" + clientHash);

//第二步  发送服务器证书到客户端,同时服务器读取私钥,公钥加密私钥解密
byte[] certificateBytes = CertifcateUtils.readCertifacates();
privateKey = CertifcateUtils.readPrivateKeys();
System.out.println("发送证书给客户端,字节长度为:"+certificateBytes.length);
System.out.println("证书内容为:" + byte2hex(certificateBytes));
SocketUtils.writeBytes(out, certificateBytes, certificateBytes.length);

//第三步  获取客户端通过公钥加密后的随机数密码,用私钥解密得到
int secureByteLength = in.readInt();
byte[] secureBytes = SocketUtils.readBytes(in, secureByteLength);
System.out.println("读取到的客户端的随机数为:" + byte2hex(secureBytes));
byte secureSeed[] = decrypt(secureBytes);
System.out.println("解密后的随机数密码为:" + byte2hex(secureSeed));

//第四步  首先获取客户端加密消息,其次获取客户端消息摘要
int skip = in.readInt();
System.out.println("第四步 获取客户端加密消息,消息长度为 :" + skip);
byte[] data = SocketUtils.readBytes(in,skip);
System.out.println("客户端发送的加密消息为 : " + byte2hex(data));
System.out.println("用私钥对消息解密,并计算SHA1的hash值");
byte message[] = decrypt(data,new SecureRandom(secureBytes));
byte[] serverHashBytes = cactHash(message);
System.out.println("获取客户端计算的SHA1摘要");
int hashSkip = in.readInt();
byte[] clientHashBytes = SocketUtils.readBytes(in,hashSkip);
System.out.println("客户端SHA1摘要为 : " + byte2hex(clientHashBytes));

//第五步  比较服务端摘要(服务端摘要通过)和客户端摘要是否一致
boolean isHashEquals = byteEquals(serverHashBytes,clientHashBytes);
System.out.println("hash值比较是否一致结果为 : " + isHashEquals);

//第六步  生成随机消息加密发送给客户端,并生成消息摘要再次发送
System.out.println("生成密码用于加密服务器端消息,secureRandom : " + byte2hex(secureSeed));
SecureRandom secureRandom = new SecureRandom(secureSeed);
String randomMessage = random();
System.out.println("服务器端生成的随机消息为 : " + randomMessage);
System.out.println("用DES算法并使用客户端生成的随机密码对消息加密");
byte[] desKey = DesCoder.initSecretKey(secureRandom);
key = DesCoder.toKey(desKey);
byte serverMessage[] = DesCoder.encrypt(randomMessage.getBytes(), <span style="color: #9876aa; font-style: itali

  这里用基本上都是原生JDK有的类

    接下来我们来看看HTTP方式的实现

HTTP实现

    http实现相对简单很多,服务器需要配置一下tomcat的server.xml即可

请注意,这段配置是不用写的,tomcat的server.xml里本来就有这些配置,只是注释掉了。只需要找到具体所在的位置然后去掉注释,接着再把自己配置公钥和私钥按照上图一般添加进去即可。

然后启动tomcat,以https访问localhost:8443端口,就会发现https生效了。

接着来看一下如何用httpClient向服务器发起https请求。接着上代码

import com.demo.service.net.utils.*;
import com.google.gson.Gson;
import com.shhxzq.common.rpc.model.CommonRequest;
import com.shhxzq.stock.levelTwo.service.request.AddL2MacsInfoRequest;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;

public class HttpsTest {

    public static String doJsonPost(String url, String params, int timeOut) throws Exception {
        ProtocolSocketFactory factory = new MySecureProtocolSocketFactory();
Protocol.registerProtocol("https", new Protocol("https", factory, 443));
HttpClient httpClient = new HttpClient();
PostMethod post = new PostMethod(url);

RequestEntity requestEntity = new StringRequestEntity(params, "application/json", "UTF-8");//[application/x-www-form-urlencoded], [multipart/form-data], [application/json], [text/xml]
post.setRequestEntity(requestEntity);
post.setRequestHeader("Connection", "close");
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(timeOut);
httpClient.getHttpConnectionManager().getParams().setSoTimeout(timeOut);

post.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(0, false));
post.getParams().setParameter("http.protocol.cookie-policy", CookiePolicy.BROWSER_COMPATIBILITY);
httpClient.executeMethod(post);
return getResponseString(post);
}

    private static String getResponseString(PostMethod post) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream in = post.getResponseBodyAsStream();
byte[] bys = new byte[1024];
for (int n = -1; (n = in.read(bys)) != -1;) {
            out.write(bys, 0, n);
}
        return new String(out.toByteArray(), "utf-8");
}

    public static void main(String[] args) throws Exception {
        String account = "l22017022330927589";
String sitePoint = "sdfsadf";
Long loginTime = 1234567890223L;
String token = TokenUtil.generateToken(account, sitePoint, loginTime.toString());
System.out.println("token:" + token);

//https发送
String result = doJsonPost("https://127.0.0.1:8443/updateLevel2Token.service", token, 30 * 1000);

System.out.println(result);
}
}

代码贴完可以测试一下

猜你喜欢

转载自lcl088005.iteye.com/blog/2371658