java rsa 的是_Java RSA非对称加密

最近的一个项目中,agent和master双方需要远程通信,但是需要双方认证以及传输的信息加密,因此就选择了RSA这个非对称加密算法实现了netty的handler。

##实现思路

简要的描述一下实现思路:首先生成一对公钥和私钥

所有的master都使用这个私钥进行加密、解密

所有的agent都使用这个公钥进行加密和解密

master发给agent的信息,使用私钥加密,master收到agent的信息,使用私钥解密

agent发给master的信息,使用公钥加密,agent收到master的信息,使用公钥解密

无论是agent还是master,对收到的信息,只要解密失败,那么就丢弃

这样相当于实现了agent和master的认证,以及消息的加密传输。挺有意思的。

##生成公钥私钥

###使用java代码生成:private static final String RSA = "RSA";

public static KeyPair buildKeyPair() throws NoSuchAlgorithmException {

final int keySize = 2048;

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA);

keyPairGenerator.initialize(keySize);

return keyPairGenerator.genKeyPair();

}

KeyPair keyPair = buildKeyPair();

PublicKey pubKey = keyPair.getPublic();

PrivateKey privateKey = keyPair.getPrivate();

###shell生成

我在这个项目实现中,是别生成了公钥文件和私钥文件,作为了工程的配置文件来用的,因此使用了shell的命令:ssh-keygen -t rsa -b 2048 -C "any string"

openssl pkcs8 -topk8 -inform PEM -outform DER -in id_rsa -out private_key.der -nocrypt

openssl rsa -in id_rsa -pubout -outform DER -out public_key.der

命令解释:第一条命令会生成2048位的rsa的公钥和私钥文件。这个2048可以自己修改,但是最小是1024,位数越大,加密越好,但是加密和解密的速度会更慢。在生成的过程中会询问你文件的路径,我是存放在了当前目录,因此当前目录下会有:id_rsa和id_rsa.pub这两个文件。

第二条命令生成java程序可用的私钥文件,其实就是将id_rsa转换为PKCS#8格式,这样java程序能够读取它

第三条命令生成java程序可用的公钥文件,其实以DER格式输出公钥部分,这样java程序可用读取它

这3条命令执行以后,当前目录下会有4个文件:id_rsa id_rsa.pub private_key.der public_key.der

我们程序中使用的是后面2个,将其放在代码的resources目录下。

###RSA实现

关于rsa的java实现,直接给出代码吧:package com.qunar.qcloudagent.common.encrypt;

import com.google.common.base.Throwables;

import javax.crypto.BadPaddingException;

import javax.crypto.Cipher;

import javax.crypto.IllegalBlockSizeException;

import java.nio.file.Files;

import java.nio.file.Paths;

import java.security.KeyFactory;

import java.security.PrivateKey;

import java.security.PublicKey;

import java.security.spec.PKCS8EncodedKeySpec;

import java.security.spec.X509EncodedKeySpec;

public class RSAUtil {

private static final String RSA = "RSA";

private static final PrivateKey PRIVATE_KEY = readPrivateKey(

RSAUtil.class.getResource("/").getPath().concat("private_key.der"));

private static Cipher cipher;

private final static PublicKey PUBLIC_KEY = readPublicKey(

RSAUtil.class.getResource("/").getPath().concat("public_key.der"));

static {

try {

cipher = Cipher.getInstance(RSA);

} catch (Exception e) {

throw Throwables.propagate(e);

}

}

public static byte[] encryptWithPrivateKey(byte[] message) throws Exception {

cipher.init(Cipher.ENCRYPT_MODE, PRIVATE_KEY);

return blockCipher(message, Cipher.ENCRYPT_MODE);

}

public static byte[] decryptWithPrivateKey(byte[] encrypted) throws Exception {

cipher.init(Cipher.DECRYPT_MODE, PRIVATE_KEY);

return blockCipher(encrypted, Cipher.DECRYPT_MODE);

}

public static byte[] encryptWithPublicKey(byte[] message) throws Exception {

cipher.init(Cipher.ENCRYPT_MODE, PUBLIC_KEY);

return blockCipher(message, Cipher.ENCRYPT_MODE);

}

public static byte[] decryptWithPublicKey(byte[] encrypted) throws Exception {

cipher.init(Cipher.DECRYPT_MODE, PUBLIC_KEY);

return blockCipher(encrypted, Cipher.DECRYPT_MODE);

}

private static PublicKey readPublicKey(String filename) {

try {

byte[] keyBytes = Files.readAllBytes(Paths.get(filename));

X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);

KeyFactory kf = KeyFactory.getInstance(RSA);

return kf.generatePublic(spec);

} catch (Exception e) {

throw Throwables.propagate(e);

}

}

private static PrivateKey readPrivateKey(String filename) {

try {

byte[] keyBytes = Files.readAllBytes(Paths.get(filename));

PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);

KeyFactory kf = KeyFactory.getInstance(RSA);

return kf.generatePrivate(spec);

} catch (Exception e) {

throw Throwables.propagate(e);

}

}

/**

* 具体为啥这么做,请看文章:{@see http://coding.westreicher.org/?p=23}

*/

private static byte[] blockCipher(byte[] bytes, int mode) throws IllegalBlockSizeException, BadPaddingException {

// string initialize 2 buffers.

// scrambled will hold intermediate results

byte[] scrambled = new byte[0];

// toReturn will hold the total result

byte[] toReturn = new byte[0];

// if we encrypt we use 100 byte long blocks. Decryption requires 128 byte long blocks (because of RSA)

int length = (mode == Cipher.ENCRYPT_MODE) ? 100 : 128;

// another buffer. this one will hold the bytes that have to be modified in this step

byte[] buffer = new byte[length];

for (int i = 0; i < bytes.length; i++) {

// if we filled our buffer array we have our block ready for de- or encryption

if ((i > 0) && (i % length == 0)) {

//execute the operation

scrambled = cipher.doFinal(buffer);

// add the result to our total result.

toReturn = append(toReturn, scrambled);

// here we calculate the length of the next buffer required

int newlength = length;

// if newlength would be longer than remaining bytes in the bytes array we shorten it.

if (i + length > bytes.length) {

newlength = bytes.length - i;

}

// clean the buffer array

buffer = new byte[newlength];

}

// copy byte into our buffer.

buffer[i % length] = bytes[i];

}

// this step is needed if we had a trailing buffer. should only happen when encrypting.

// example: we encrypt 110 bytes. 100 bytes per run means we "forgot" the last 10 bytes. they are in the buffer array

scrambled = cipher.doFinal(buffer);

// final step before we can return the modified data.

toReturn = append(toReturn, scrambled);

return toReturn;

}

private static byte[] append(byte[] toReturn, byte[] scrambled) {

byte[] destination = new byte[toReturn.length + scrambled.length];

System.arraycopy(toReturn, 0, destination, 0, toReturn.length);

System.arraycopy(scrambled, 0, destination, toReturn.length, scrambled.length);

return destination;

}

}

关于上面的代码有一点需要指出的是:RSA加密明文最大长度117字节,解密要求密文最大长度为128字节,所以在加密和解密的过程中需要分块进行。 RSA加密对明文的长度是有限制的,如果加密数据过大会抛出如下异常:Exception in thread “main” javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes。因此上面的blockCipher是用来专门处理这种情况的。

##参考文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值