逻辑是这样的:
逻辑解释:
1.OpenSSL生成RSA私钥和公钥,公钥存客户端,私钥存服务端;
2.客户端与服务端的DH分别生成自己的公钥和私钥,并且使用相同的公钥和私钥算法;
3.首先客户端把DH生成的公钥使用RSA加密,通过HTTP协议,发送到服务端,服务端把先解密RSA,然后把自己DH模块的公钥发给客户端;
4.客户端和服务端都拿到了对方的公钥,再与自己的私钥,通过密钥公式,算出AES加密密钥。后面发送数据时都通过AES加密。
程序运行截图如下:
关键源码如下:
服务端(IDEA普通Java项目):
Main.java
package cn.it1995;
import cn.it1995.http.HttpCallback;
import cn.it1995.http.HttpServer;
import cn.it1995.tool.AES;
import cn.it1995.tool.DH;
import cn.it1995.tool.DataSecret;
import cn.it1995.tool.RSA;
import java.io.UnsupportedEncodingException;
import java.util.Map;
public class Main {
private static final String PUB_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCjuN97tkBc0QcKGU9oXydaQN7q" +
"wZnThxTOmdIc8O1yuA9FrDZpZ3Sz908vTqM/YPZkOUaYrGwsBO7FeQovoX7nQPKu" +
"YQpRDqt7OKzhwPavyynH0Jz38PDyCBw45zwl4Ux8BtsggTrGVxAqNjO4KkuyL1QS" +
"8amn4Fzl1CBre8Y0gQIDAQAB";
private static final String PRI_KEY = "MIICdAIBADANBgkqhkiG9w0BAQEFAASCAl4wggJaAgEAAoGBAKO433u2QFzRBwoZ" +
"T2hfJ1pA3urBmdOHFM6Z0hzw7XK4D0WsNmlndLP3Ty9Ooz9g9mQ5RpisbCwE7sV5" +
"Ci+hfudA8q5hClEOq3s4rOHA9q/LKcfQnPfw8PIIHDjnPCXhTHwG2yCBOsZXECo2" +
"M7gqS7IvVBLxqafgXOXUIGt7xjSBAgMBAAECgYAgp0uzcdsOaAY+ZmPnDitcHdoX" +
"+jsC7EsjFZzJduf03G73V2yWwzKMfkPFzKpUhrM9tAq3gpQkh2tT6Vs1usEDbrDd" +
"e08Cc3D5YWK7HuTfO3kkJTSNOK0JfuT1UQaTjCmDQWWjPmYbu6UN0UDv4Mo26dgl" +
"cp9h0wlaPsSFN3O97QJBANgj+4i7ABAdbTuUfq6Tcr38E7wYSFTUeL5jTd4sO636" +
"+KJ6zx4uTXAmxnt9OdIY1ihLRqDh8VbiorqOelGf6iMCQQDB6jZver3mTMe6ntSY" +
"hx3JWOCDM3ULkPvOWGrAcRsNKjyIuo37taXa7fuqmVQQq9MJYikJfilMhkgwkV/X" +
"ihcLAkBsOvBskj9A0ottJzmcT4dIbR6wtHQbzl078NwAIaQsxZyVN+vY0BTE0RXY" +
"pmc6tmcevDr8uscv28Liqg/EKdCDAkAjL9C44djblUsYvgFtu/bXtlzm8ctnUeOf" +
"ScP1L5DtDqD1XoStDAUQeOaVykTK0aL1rO4tXss3q5Yl2fs+LTyJAj8f7N5kzw08" +
"9S5OWCHoEm5UOeAvjFh//cjXi6lyqetBW3w17muZ2OumUAbKJXksyzmT6/QRpkX3" +
"S0w/tqdJXLs=";
private static final String HANDHAKE = "handshake";
public static void main(String[] args) throws UnsupportedEncodingException {
// String content = "123456";
// String encrypted = RSA.encrypt(content, PUB_KEY);
// System.out.println(encrypted);
// System.out.println(RSA.decrypt(encrypted, PRI_KEY));
//查密钥
// DH dhC = new DH();
// DH dhS = new DH();
//
// int publicKeyC = dhC.getPublicKey();
// int publicKeyS = dhS.getPublicKey();
//
// byte[] secretC = dhC.getSecretKey(publicKeyS);
// byte[] secretS = dhS.getSecretKey(publicKeyC);
//
// System.out.println("client's secrete is : ");
// for(byte i : secretC){
//
// System.out.print(i + " ");
// }
// System.out.println("\nserver's secrete is : ");
// for(byte i : secretS){
//
// System.out.print(i + " ");
// }
// AES aes = new AES();
// byte[] encrypted = aes.encrypt(content);
// System.out.println(new String(encrypted));
// byte[] decrypt = aes.decrypt(encrypted);
// System.out.println(new String(decrypt));
//
System.out.println("start http server");
HttpServer server = new HttpServer(new HttpCallback() {
private DH mDh = new DH();
private AES mAes = new AES();
@Override
public byte[] onResponse(String request) {
if(isHandshake(request)){
//握手
Map<String, String> header = HttpServer.getHeader(request);
String handshake = header.get(HANDHAKE);
System.out.println("Handshake为:" + handshake);
int dhPubKey = Integer.valueOf(RSA.decrypt(handshake, PRI_KEY));
//设置为AES密钥
mAes.setKey(mDh.getSecretKey(dhPubKey));
System.out.println("解密后,客户端dhPubKey:" + dhPubKey);
return DataSecret.int2Byte(mDh.getPublicKey());
}
byte[] result = mAes.encrypt("Hello World");
return result;
}
});
server.startHttpServer();
}
private static boolean isHandshake(String request){
return (request != null && request.contains(HANDHAKE));
}
}
DH.java
package cn.it1995.tool;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
public class DH {
private static final int dhP = 45;
private static final int dhG = 9;
private int mPriKey;
//构造函数随机私钥
public DH(){
Random r = new Random();
mPriKey = r.nextInt(20);
System.out.println("dh priKey is : " + mPriKey);
}
//公钥计算公式计算出公钥
public int getPublicKey(){
return (int) (Math.pow(dhG, mPriKey) % dhP);
}
//使用对方公钥与自己私钥生成密钥
//在把结果转256字节,AES密钥需要128字节或256字节
public byte[] getSecretKey(long publicKey){
int buf = (int) (Math.pow(publicKey, mPriKey) % dhP);
return sha256(buf);
}
//转换成byte[256]类型,作为AES密钥
private byte[] sha256(int data){
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(DataSecret.int2Byte(data));
return messageDigest.digest();
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return new byte[]{-1};
}
}
HttpServer.java
package cn.it1995.http;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class HttpServer {
private boolean mRunning;
private HttpCallback mCallback;
public HttpServer(HttpCallback Callback) {
this.mCallback = Callback;
}
public void startHttpServer(){
if(mRunning){
return;
}
mRunning = true;
try {
ServerSocket serverSocket = new ServerSocket(80);
while (mRunning){
Socket accept = serverSocket.accept();
ExecutorService threadPool = Executors.newCachedThreadPool();
threadPool.execute(new HttpThread(accept, mCallback));
}
}
catch (IOException e) {
e.printStackTrace();
}
}
public static Map<String, String> getHeader(String request){
Map<String, String> header = new HashMap<>();
BufferedReader reader = new BufferedReader(new StringReader(request));
//逐行解析
try{
String line = reader.readLine();
while (line != null && !line.trim().isEmpty()){
int p = line.indexOf(":");
if(p >= 0){
header.put(line.substring(0, p).trim().toLowerCase(),
line.substring(p + 1).trim());
}
line = reader.readLine();
}
}
catch (Exception e){
e.printStackTrace();
}
return header;
}
}
HttpThread.java
package cn.it1995.http;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class HttpThread implements Runnable{
private Socket mSocket;
private HttpCallback mCallback;
public HttpThread(Socket socket, HttpCallback httpCallback){
mSocket = socket;
mCallback = httpCallback;
}
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
String content;
StringBuffer request = new StringBuffer();
while((content = reader.readLine()) != null
&& !content.trim().isEmpty()){
request.append(content).append("\n");
}
System.out.println("request:\n" + request);
byte[] response = new byte[0];
if(mCallback != null){
response = mCallback.onResponse(request.toString());
}
String responseFirstLine = "HTTP/1.1 200 OK \r\n";
String responseHead = "Content-type:" + "text/html" + "\r\n";
OutputStream outputStream = mSocket.getOutputStream();
outputStream.write(responseFirstLine.getBytes());
outputStream.write(responseHead.getBytes());
outputStream.write("\r\n".getBytes());
outputStream.write(response);
mSocket.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
客户端(Android项目):
MainActivity.java
package com.example.myclient;
import android.os.Bundle;
import com.example.myclient.http.HttpRequest;
import com.example.myclient.tool.AES;
import com.example.myclient.tool.DH;
import com.example.myclient.tool.RSA;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
private static final String PUB_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCjuN97tkBc0QcKGU9oXydaQN7q" +
"wZnThxTOmdIc8O1yuA9FrDZpZ3Sz908vTqM/YPZkOUaYrGwsBO7FeQovoX7nQPKu" +
"YQpRDqt7OKzhwPavyynH0Jz38PDyCBw45zwl4Ux8BtsggTrGVxAqNjO4KkuyL1QS" +
"8amn4Fzl1CBre8Y0gQIDAQAB";
private static final String URL = "http://192.168.20.59/";
private byte[] mAesKey;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
final HttpRequest request = new HttpRequest(URL);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mAesKey == null || mAesKey.length <= 0){
final DH dh = new DH();
int pubKey = dh.getPublicKey();
Log.d("test", "dh 公钥为:" + dh.getPublicKey());
try {
request.handshake(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d("error", "onFailure: 握手失败");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
byte[] pubKey = response.body().bytes();
mAesKey = dh.getSecretKey(pubKey);
Log.d("success", "success: 握手成功aes密钥为:" + new String(mAesKey));
}
}, RSA.encrypt(String.valueOf(pubKey), PUB_KEY));
}
catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
else {
request.request(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("error", "请求失败");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
byte[] bytes = response.body().bytes();
AES aes = new AES(mAesKey);
String content = new String(aes.decrypt(bytes));
Log.e("success", "请求成功:" + content);
}
});
}
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
源码打包下载地址:
https://github.com/fengfanchen/Java/tree/master/AES%26RSA%26DHDemo