RSA加密Socket传输文件、签名
来源:互联网 发布:ubuntu官网下载地址 编辑:程序博客网 时间:2024/06/07 01:07
RSA加密Socket传输文件、签名
转自 CSDN /dyyaries
(一)
RSA加密分为公钥加密和私钥解密以及可能的数字签名。
公钥、私钥分居客户端和服务器端,分别用于加密和解密。同时,私钥还用于签名,公钥还用于验证签名。
解密加密用到JDK中Java.security、javax.crypto两个包中相关的接口和类
1.生成密钥的代码
- SecureRandom sr = new SecureRandom();
- KeyPairGenerator kg = KeyPairGenerator.getInstance(”RSA”);
- // 注意密钥大小最好为1024,否则解密会有乱码情况.
- kg.initialize(1024, sr);
- KeyPair kp = kg.generateKeyPair();
- String privateKeyName = keyFile.substring(0,keyFile.lastIndexOf(“.”))+“_private”+keyFile.substring(keyFile.lastIndexOf(“.”));
- String publicKeyName = keyFile.substring(0,keyFile.lastIndexOf(“.”))+“_public”+keyFile.substring(keyFile.lastIndexOf(“.”));
- FileOutputStream fos = new FileOutputStream(privateKeyName);
- ObjectOutputStream oos = new ObjectOutputStream(fos);
- // 生成私钥
- oos.writeObject(kp.getPrivate());
- oos.close();
- //生成公钥
- fos = new FileOutputStream(publicKeyName);
- oos = new ObjectOutputStream(fos);
- oos.writeObject(kp.getPublic());
- oos.close();
SecureRandom sr = new SecureRandom(); KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA"); // 注意密钥大小最好为1024,否则解密会有乱码情况. kg.initialize(1024, sr); KeyPair kp = kg.generateKeyPair(); String privateKeyName = keyFile.substring(0,keyFile.lastIndexOf("."))+"_private"+keyFile.substring(keyFile.lastIndexOf(".")); String publicKeyName = keyFile.substring(0,keyFile.lastIndexOf("."))+"_public"+keyFile.substring(keyFile.lastIndexOf(".")); FileOutputStream fos = new FileOutputStream(privateKeyName); ObjectOutputStream oos = new ObjectOutputStream(fos); // 生成私钥 oos.writeObject(kp.getPrivate()); oos.close(); //生成公钥 fos = new FileOutputStream(publicKeyName); oos = new ObjectOutputStream(fos); oos.writeObject(kp.getPublic()); oos.close();
2.加密、解密
(RSA加密速度比较慢,原因是每次只能为最多117 bytes加密,加密之后为128 Bytes)
- //加密
- Cipher cipher = Cipher.getInstance(”RSA/ECB/PKCS1Padding”);
- cipher.init(Cipher.ENCRYPT_MODE, (PublicKey)getKey(keyFile,”public”));
- return cipher.doFinal(text);
- //加密
- Cipher cipher = Cipher.getInstance(”RSA/ECB/PKCS1Padding”);
- cipher.init(Cipher.DECRYPT_MODE, (PrivateKey)getKey(keyFile,”private”));
- return cipher.doFinal(text);
//加密Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");cipher.init(Cipher.ENCRYPT_MODE, (PublicKey)getKey(keyFile,"public"));return cipher.doFinal(text);//加密Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");cipher.init(Cipher.DECRYPT_MODE, (PrivateKey)getKey(keyFile,"private"));return cipher.doFinal(text);
3.数字签名
用私钥进行数字签名,用于服务器端验证客户端数据是否是正确数据。
- public byte[] getSignature(byte[] cipherText){
- try {
- coder = new RSASecurityCoder();
- Signature sig = Signature.getInstance(”SHA1withRSA”);
- PrivateKey privateKey = (PrivateKey)coder.getKey(this.key, “private”);
- sig.initSign(privateKey);
- sig.update(cipherText);
- return sig.sign();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- public boolean verifySignature(byte[] cipherText,byte[] signature){
- try {
- coder = new RSASecurityCoder();
- Signature sig = Signature.getInstance(”SHA1withRSA”);
- PublicKey publicKey = (PublicKey)coder.getKey(this.key, “public”);
- sig.initVerify(publicKey);
- sig.update(cipherText);
- return sig.verify(signature);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return false;
- }
public byte[] getSignature(byte[] cipherText){ try { coder = new RSASecurityCoder(); Signature sig = Signature.getInstance("SHA1withRSA"); PrivateKey privateKey = (PrivateKey)coder.getKey(this.key, "private"); sig.initSign(privateKey); sig.update(cipherText); return sig.sign(); } catch (Exception e) { e.printStackTrace(); } return null; } public boolean verifySignature(byte[] cipherText,byte[] signature){ try { coder = new RSASecurityCoder(); Signature sig = Signature.getInstance("SHA1withRSA"); PublicKey publicKey = (PublicKey)coder.getKey(this.key, "public"); sig.initVerify(publicKey); sig.update(cipherText); return sig.verify(signature); } catch (Exception e) { e.printStackTrace(); } return false; }
数字签名是在客户端进行,所用的私钥不是和用于加密的公钥成对的。
也就是说这样的通信 要在客户端保存有 服务器端的公钥和本地的私钥,而服务器端应该保存有本地的私钥和客户端的公钥。
(二)
服务器端采用多线程方式并行处理传输请求以提高效率。
因为我想用JavaServiceWrapper将 程序作为windows服务运行,所以main方法中需接收一些参数,比如端口、密钥文件位置、文件存放路径等。
1.从main方法接受配置参数
- for(String arg : args){
- String argName = arg.substring(0,arg.indexOf(“:”)).trim();
- String argValue = arg.substring(arg.indexOf(”:”)+1).trim();
- if(argName.equals(“rsa_private”)){
- privateKey = argValue;
- }else if(argName.equals(“rsa_public”)){
- publicKey = argValue;
- }else if(argName.equals(“port”)){
- port = Integer.parseInt(argValue);
- }else if(argName.equals(“file_dir”)){
- file_dir = argValue;
- }
- }
for(String arg : args){ String argName = arg.substring(0,arg.indexOf(":")).trim(); String argValue = arg.substring(arg.indexOf(":")+1).trim(); if(argName.equals("rsa_private")){ privateKey = argValue; }else if(argName.equals("rsa_public")){ publicKey = argValue; }else if(argName.equals("port")){ port = Integer.parseInt(argValue); }else if(argName.equals("file_dir")){ file_dir = argValue; } }
2.初始化解密、签名类实例
- RSASecurityCoder coder = new RSASecurityCoder(privateKey);
- RSASecuritySignature sign = new RSASecuritySignature(publicKey);
RSASecurityCoder coder = new RSASecurityCoder(privateKey);RSASecuritySignature sign = new RSASecuritySignature(publicKey);
3.建立服务器
- private static void runServer(String[] args){
- long start = System.currentTimeMillis();
- //获取配置参数
- for(String arg : args){
- String argName = arg.substring(0,arg.indexOf(“:”)).trim();
- String argValue = arg.substring(arg.indexOf(”:”)+1).trim();
- if(argName.equals(“rsa_private”)){
- privateKey = argValue;
- }else if(argName.equals(“rsa_public”)){
- publicKey = argValue;
- }else if(argName.equals(“port”)){
- port = Integer.parseInt(argValue);
- }else if(argName.equals(“file_dir”)){
- file_dir = argValue;
- }
- }
- //配置
- coder = new RSASecurityCoder(privateKey);
- sign = new RSASecuritySignature(publicKey);
- //启动服务器
- System.out.println(”服务器启动中…”);
- try {
- ss = new ServerSocket(port);
- clientCount = 0;
- } catch (NumberFormatException e) {
- System.err.println(”端口配置错误”);
- } catch (IOException e) {
- System.err.println(”服务器在端口”+port+“启动失败”);
- }
- //文件存放目录
- dir = new File(file_dir);
- if(!dir.exists()){
- dir.mkdir();
- }
- System.out.println(”服务器已启动,端口: ”+port);
- //计算启动时间
- long end = System.currentTimeMillis();
- System.out.println(”共消耗 ”+(end-start)+“ ms.”);
- //接收数据
- while(true){
- try {
- if(ss == null){
- ss = new ServerSocket(port);
- clientCount = 0;
- }
- Socket socket = ss.accept();
- clientCount += 1;
- System.out.println(”有新的连接,当前总连接数:”+clientCount);
- Thread th = new Thread(new ServerProcessor(socket));
- th.start();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
private static void runServer(String[] args){ long start = System.currentTimeMillis(); //获取配置参数 for(String arg : args){ String argName = arg.substring(0,arg.indexOf(":")).trim(); String argValue = arg.substring(arg.indexOf(":")+1).trim(); if(argName.equals("rsa_private")){ privateKey = argValue; }else if(argName.equals("rsa_public")){ publicKey = argValue; }else if(argName.equals("port")){ port = Integer.parseInt(argValue); }else if(argName.equals("file_dir")){ file_dir = argValue; } } //配置 coder = new RSASecurityCoder(privateKey); sign = new RSASecuritySignature(publicKey); //启动服务器 System.out.println("服务器启动中..."); try { ss = new ServerSocket(port); clientCount = 0; } catch (NumberFormatException e) { System.err.println("端口配置错误"); } catch (IOException e) { System.err.println("服务器在端口"+port+"启动失败"); } //文件存放目录 dir = new File(file_dir); if(!dir.exists()){ dir.mkdir(); } System.out.println("服务器已启动,端口: "+port); //计算启动时间 long end = System.currentTimeMillis(); System.out.println("共消耗 "+(end-start)+" ms."); //接收数据 while(true){ try { if(ss == null){ ss = new ServerSocket(port); clientCount = 0; } Socket socket = ss.accept(); clientCount += 1; System.out.println("有新的连接,当前总连接数:"+clientCount); Thread th = new Thread(new ServerProcessor(socket)); th.start(); } catch (IOException e) { e.printStackTrace(); } } }
4.建立线程类ServerProcessor处理每个文件
- static class ServerProcessor extends Thread{
- private Socket socket;
- private InputStream in;
- private String filename;
- private long totalBytes;
- private File file;
- private FileOutputStream fos;
- public ServerProcessor(Socket socket){
- this.socket = socket;
- }
- @Override
- public void run(){
- try {
- in = socket.getInputStream();
- DataInputStream dis = new DataInputStream(in);
- filename = dis.readUTF(); //读取文件名
- totalBytes = dis.readLong(); //读取文件大小
- file = new File(dir,filename);
- fos = new FileOutputStream(file);
- //FileOutputStream fos = new FileOutputStream(File.createTempFile(filename, null, dir));
- //输出日志
- //RSA加密以128 bytes位单位,一次最多加密117bytes.
- byte[] buf = new byte[128];
- int available;
- while((available = in.read(buf)) != -1){
- //读取签名
- byte[] signature = buf;
- //读取数据
- buf = new byte[128];
- available = in.read(buf);
- byte[] availableBytes = null;
- if(available == buf.length){
- availableBytes = buf;
- }else{
- availableBytes = new byte[available];
- for (int i = 0; i < available; i++) {
- availableBytes[i] = (Byte) buf[i];
- }
- }
- //验证数据签名
- boolean flag = sign.verifySignature(availableBytes, signature);
- //写入数据
- if(flag){
- //count += availableBytes.length;
- fos.write(coder.decrypt(availableBytes));
- //System.out.println(“”+count+” bytes received”);
- }
- }
- fos.close();
- socket.shutdownInput();
- clientCount -= 1;
- System.out.println(”有连接断开,当前总连接数:”+clientCount);
- System.out.println(new SimpleDateFormat(“[yyyy-MM-DD HH:mm:ss ]”).format(new Date())
- +filename+” 接收完毕. 大小 ”+(totalBytes/1024)+“ kb,保存路径:”+dir.getAbsolutePath());
- } catch (NumberFormatException e) {
- System.err.println(”端口配置错误”);
- e.printStackTrace();
- } catch (FileNotFoundException e) {
- System.err.println(”文件没找到,文件名:”+filename+“,可能是因为读取socket数据失败”);
- e.printStackTrace();
- } catch (IOException e) {
- System.err.println(”文件读/写错误”);
- try {
- fos.close();
- } catch (IOException e1) {
- e1.printStackTrace();
- }
- file.delete();
- e.printStackTrace();
- }
- }
- }
static class ServerProcessor extends Thread{ private Socket socket; private InputStream in; private String filename; private long totalBytes; private File file; private FileOutputStream fos; public ServerProcessor(Socket socket){ this.socket = socket; } @Override public void run(){ try { in = socket.getInputStream(); DataInputStream dis = new DataInputStream(in); filename = dis.readUTF(); //读取文件名 totalBytes = dis.readLong(); //读取文件大小 file = new File(dir,filename); fos = new FileOutputStream(file); //FileOutputStream fos = new FileOutputStream(File.createTempFile(filename, null, dir)); //输出日志 //RSA加密以128 bytes位单位,一次最多加密117bytes. byte[] buf = new byte[128]; int available; while((available = in.read(buf)) != -1){ //读取签名 byte[] signature = buf; //读取数据 buf = new byte[128]; available = in.read(buf); byte[] availableBytes = null; if(available == buf.length){ availableBytes = buf; }else{ availableBytes = new byte[available]; for (int i = 0; i < available; i++) { availableBytes[i] = (Byte) buf[i]; } } //验证数据签名 boolean flag = sign.verifySignature(availableBytes, signature); //写入数据 if(flag){ //count += availableBytes.length; fos.write(coder.decrypt(availableBytes)); //System.out.println(""+count+" bytes received"); } } fos.close(); socket.shutdownInput(); clientCount -= 1; System.out.println("有连接断开,当前总连接数:"+clientCount); System.out.println(new SimpleDateFormat("[yyyy-MM-DD HH:mm:ss ]").format(new Date()) +filename+" 接收完毕. 大小 "+(totalBytes/1024)+" kb,保存路径:"+dir.getAbsolutePath()); } catch (NumberFormatException e) { System.err.println("端口配置错误"); e.printStackTrace(); } catch (FileNotFoundException e) { System.err.println("文件没找到,文件名:"+filename+",可能是因为读取socket数据失败"); e.printStackTrace(); } catch (IOException e) { System.err.println("文件读/写错误"); try { fos.close(); } catch (IOException e1) { e1.printStackTrace(); } file.delete(); e.printStackTrace(); } } }main方法
- public static void main(String[] args) {
- runServer(args);
- }
public static void main(String[] args) { runServer(args); }
(三)
接下来是客户端类CoderClient的编写。
1.配置信息
首先配置如下信息,包括服务器地址、端口、密钥文件位置.
conf.properties
- #configure private key on server
- rsa_private=d:/rsa1_private.key
- #public key from client
- rsa_public=d:/rsa_public.key
- #server
- server=127.0.0.1
- #server port
- port=888
#configure private key on serverrsa_private=d:/rsa1_private.key
2.发送文件
编写发送文件的方法,其中byte[] buf=new byte[117]的原因是RSA加密算法支持的最大字节数为117,而加密后变成128位,所以服务器端解密的时候可以使用128bytes的buf读取文件。
- public boolean send(String filename){
- try {
- File f = new File(filename);
- fis = new FileInputStream(filename);
- byte[] buf = new byte[117];
- int available;
- socket = new Socket(server,port);
- os = socket.getOutputStream();
- DataOutputStream dos = new DataOutputStream(os);
- dos.writeUTF(f.getName()); //写入文件名
- dos.writeLong(f.length()); //写入文件大小
- while((available = fis.read(buf)) != -1){
- byte[] availableBytes = null;
- if(available == buf.length){
- availableBytes = buf;
- }else{
- availableBytes = new byte[available];
- for (int i = 0; i < available; i++) {
- availableBytes[i] = (byte) buf[i];
- }
- }
- byte[] cipherText = coder.encrypt(availableBytes);
- byte[] signature = sign.getSignature(cipherText);
- os.write(signature);
- os.write(cipherText);
- //count += cipherText.length;
- //System.out.println(“send ”+count+” bytes”);
- }
- socket.shutdownOutput();
- fis.close();
- return true;
- } catch (FileNotFoundException e) {
- log.error(”文件”+filename+“ 未找到”);
- } catch (UnknownHostException e) {
- log.error(”未知主机:”+server);
- } catch (IOException e) {
- log.error(”文件读/写错误,可能是因为远程主机无响应”);
- }
- return false;
- }
public boolean send(String filename){ try { File f = new File(filename); fis = new FileInputStream(filename); byte[] buf = new byte[117]; int available; socket = new Socket(server,port); os = socket.getOutputStream(); DataOutputStream dos = new DataOutputStream(os); dos.writeUTF(f.getName()); //写入文件名 dos.writeLong(f.length()); //写入文件大小 while((available = fis.read(buf)) != -1){ byte[] availableBytes = null; if(available == buf.length){ availableBytes = buf; }else{ availableBytes = new byte[available]; for (int i = 0; i < available; i++) { availableBytes[i] = (byte) buf[i]; } } byte[] cipherText = coder.encrypt(availableBytes); byte[] signature = sign.getSignature(cipherText); os.write(signature); os.write(cipherText); //count += cipherText.length; //System.out.println("send "+count+" bytes"); } socket.shutdownOutput(); fis.close(); return true; } catch (FileNotFoundException e) { log.error("文件"+filename+" 未找到"); } catch (UnknownHostException e) { log.error("未知主机:"+server); } catch (IOException e) { log.error("文件读/写错误,可能是因为远程主机无响应"); } return false; }
3.多线程
为了测试多线程处理效果,可以将客户端继承于Thread,在重写的run方法中调用 send(String file)方法
- @Override
- public void run() {
- send(this.file);
- }
@Override public void run() { send(this.file); }
4.模拟多客户端
main中的测试可以这样来模拟多个客户端同时向服务器传输文件
- File dir = new File(“E://files//demo//”);
- File[] files = dir.listFiles();
- for(int i=0; i<files.length; i++){
- Thread th = new Thread(new CoderClient(files[i].getAbsolutePath()));
- th.start();
- }
File dir = new File("E://files//demo//"); File[] files = dir.listFiles(); for(int i=0; i<files.length; i++){ Thread th = new Thread(new CoderClient(files[i].getAbsolutePath())); th.start(); }
(四)
最后 用JavaServiceWrapper把服务器端程序做成windows服务自动运行。
可以到http://wrapper.tanukisoftware.com/doc/english/download.jsp这里下载到wrapper-windows-x86-32-3.2.3.zip
首先,在本地建立一个文件夹,其中建立bin、lib、conf和logs目录。
解压wrapper-windows-x86-32-3.2.3.zip到本地,暂且把解压后的文件夹叫做wrapper_home.
- 1.将wrapper_home/bin/wrapper.exe拷贝至你的bin文件夹中,然后拷贝wrapper_home/src/bin中的App.bat.in、InstallApp-NT.bat.in和UninstallApp-NT.bat.in到
- 你的bin文件夹并将.in后缀去掉。
- 2.将wrapper_homw/lib/中的wrapper.dll和wrapper.jar拷贝至你的lib文件夹,并将你的可执行服务器端jar程序放到这里,暂且假定为server.jar.
- 3.将wrapper_home/src/bin/conf中的wrapper.conf.in拷贝至你的conf文件夹并去掉.in后缀。
- 4.修改wrapper.conf配置。要修改的地方我贴到下面列表:
- # Java Application (和配置jdk环境变量类型)
- wrapper.java.command=D:/installed app/Java/jdk1.6.0_13/bin/java
- # Java Classpath (include wrapper.jar) Add class path elements as (本来是指向 ../lib/testwrapper.jar的,改成你要启动的server.jar)
- wrapper.java.classpath.1=../lib/server.jar
- # Application parameters. Add parameters as needed starting from 1 (这些是中要传入的参数,第一行是java程序入口,后面是main方法参数)
- wrapper.app.parameter.1=com.vantasia.common.server.RSAServer
- wrapper.app.parameter.2=rsa_private:d:/rsa_private.key
- wrapper.app.parameter.3=rsa_public:d:/rsa1_public.key
- wrapper.app.parameter.4=port:888
- wrapper.app.parameter.5=file_dir:d:/files/
- # 日志
- wrapper.logfile.maxsize=3m
- #服务名 描述 等
- wrapper.ntservice.name=MyServer
- wrapper.ntservice.displayname=MyServer
- wrapper.ntservice.description=socket My server
# Java Application (和配置jdk环境变量类型)wrapper.java.command=D:/installed app/Java/jdk1.6.0_13/bin/java# Java Classpath (include wrapper.jar) Add class path elements as (本来是指向 ../lib/testwrapper.jar的,改成你要启动的server.jar)wrapper.java.classpath.1=../lib/server.jar# Application parameters. Add parameters as needed starting from 1 (这些是中要传入的参数,第一行是java程序入口,后面是main方法参数)wrapper.app.parameter.1=com.vantasia.common.server.RSAServerwrapper.app.parameter.2=rsa_private:d:/rsa_private.keywrapper.app.parameter.3=rsa_public:d:/rsa1_public.keywrapper.app.parameter.4=port:888wrapper.app.parameter.5=file_dir:d:/files/# 日志wrapper.logfile.maxsize=3m #服务名 描述 等 wrapper.ntservice.name=MyServerwrapper.ntservice.displayname=MyServerwrapper.ntservice.description=socket My server最后到你的bin文件夹运行App.bat可以运行你的文件服务器。
服务器启动后 可以运行客户端 测试一下。。。
到此,一个简单的文件加密传输小例子就写好了。。。
谢谢大家~!
- RSA加密Socket传输文件、签名
- RSA加密Socket传输文件、签名
- RSA加密Socket传输文件、签名(一)
- RSA加密Socket传输文件、签名(二)
- RSA加密Socket传输文件、签名(三)
- RSA加密Socket传输文件、签名(四)
- RSA签名之:RSA加密
- java RSA加密 RSA签名
- openssl rsa加密签名
- RSA签名加密
- openssl rsa加密签名
- RSA签名加密
- RSA加密解密与签名
- RSA加密及验证签名
- rsa 签名和加密 区别
- RSA签名与RSA加密异同
- WebService RSA加密传输示例
- 密码传输中Rsa加密
- C#中的虚方法、抽象方法、抽象类、接口的联系与区别
- vue中配置mint-ui报css错误的问题
- [global::]是什么形式?什么意思?
- ASCII码排序
- 自动从数据库提取数据并发送邮件
- RSA加密Socket传输文件、签名
- 如何将图片转换成BYTE
- 九度 oj 题目1142:Biorhythms
- elasticsearch5 插件ik安装
- sql查询 group by 数字类型+null 为null 处理 ,sum()的结果作为条件再次查询。
- Thymeleaf 之 内置对象、定义变量、URL参数及标签自定义属性
- 多条数据填充List
- GIS空间索引
- ajax如何截取302响应