关于java RSA密钥的长度问题

最近在搞udp可靠通信(不单单是丢失重传),为了进行密钥传输学习一下密钥长度的一些知识,mark一下

java默认的rsa填充方案为RSA/ECB/PKCS1Padding

一般说的rsa密钥长度单位是bit,本文所有长度单位均为byte,除非另有说明

java的实现中

设:密钥模数长度(与密钥长度相等)lm,公钥e长度lpube,私钥e长度lprie

一、密钥长度=lm

二、lpube=17bit,具体值为 1|00000000|00000001,所以发布公钥时只发布模数即可

三、lprie与指定的密钥长度相关,不是固定值

不进行分段加密

设:明文长度为d,密文长度为e,密钥长度为l

一、d<=l-11,有时大于此值也可以加密但为了不出现随机性必须保证加密的数据符合此关系

二、e=l

进行分段加密时有如下公式(须要自行实现,我的方案仅供大家参考)

设:明文长度ld,密文长度le,密钥模数长度(与密钥长度相等)lm,公钥e长度lpube,私钥e长度lprie

一、密文长度

a=lm-11

num=int(ld/a)

hasMore=ld%a

num+=hasMore?1:0

le=num*lm

二、明文长度。(非函数关系,但有相关性)

blockNum=le/lm

(blockNum-1)*(le-11)<ld<=blockNum*(lm-11)

三、给出byte数组(长度为capacity)计算最大可容纳的加密内容大小

blockNum=capacity/lm

ld=(lm-11)*blockNum

 

进行签名时有如下关系:(使用sha1WithRSA签名算法)

设数据长度ld,签名长度le,密钥模数长度(与密钥长度相等)lm

一、le=lm

二、对ld没有要求

最后附上分段加密的实现

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
/*
 注意:
 	a经实验publickey的exponent始终为[1,0,1]
    b不保证线程安全
 目的:
	a针对每个对端机保存一个密钥对
	b实现分段加密
	c为各种加/解密和签名操作提供便利
 **/
public class RsaTool{
	//推荐密钥长度
	public static final int advance_key_size=512;
	public static final String algorithm="rsa";
	public static final String signature_alghorithm="sha1WithRSA";
	private RSAPrivateKey priKey;
	private RSAPublicKey pubKey;
	private Cipher encrypt;
	private Cipher decrypt;
	public RsaTool() {
	}
	/*
	 * 指定密钥长度生成随机密钥
	 */
	public RsaTool(int keyLength){
		KeyPairGenerator kpg=null;
		try{
			kpg=KeyPairGenerator.getInstance(algorithm);
			kpg.initialize(advance_key_size);
			encrypt=Cipher.getInstance(algorithm);
			decrypt=Cipher.getInstance(algorithm);
		}catch(NoSuchAlgorithmException e){
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		}
		kpg.initialize(keyLength);
		KeyPair kp=kpg.generateKeyPair();
		setPrivateKey((RSAPrivateKey)kp.getPrivate());
		setPublicKey((RSAPublicKey)kp.getPublic());
	}
	/*
	 * 指定编码后的密钥以还原密钥
	 */
	public RsaTool(byte[]pubKey,byte[]priKey)throws InvalidKeySpecException{
		try{
			encrypt=Cipher.getInstance(algorithm);
			decrypt=Cipher.getInstance(algorithm);
		}catch(Exception e){
			e.printStackTrace();
		}
		setPrivateKey(priKey);
		setPublicKey(pubKey);
	}

	/*
	 * 指定公钥或私钥的e和m以还原密钥,使密钥构建更加灵活节省存储空间
	 */
	public RsaTool(byte[]pubKey,byte[]priKey,byte[]modulus)throws InvalidKeySpecException{
		try{
			encrypt=Cipher.getInstance(algorithm);
			decrypt=Cipher.getInstance(algorithm);
		}catch(Exception e){
			e.printStackTrace();
		}
		setPrivateKey(priKey,modulus);
		setPublicKey(pubKey,modulus);
	}
	/**
	 * 由于密钥长度不是常量所以只能通过计算获得
	 * 对于给出的capacity(c)计算一个值(d),使encrypt(d)<=c
	 */
	public int getThisBufferCapacity(int capacity){
		//计算公钥加密结果的块大小
		int resultUnitLen=pubKey.getModulus().bitLength()/8;
		//计算指定容量可容纳的块数量
		int num=capacity/resultUnitLen;
		//数量乘以最大明文块大小
		return (resultUnitLen-11)*num;
	}
	/**
	 * 计算给出的密文长度解密后的最大文明长度
	 * 具有一定的函数关系性质
	 * 	设解密后的数据为data 解密前为encrypted
	 * 	data.length<=calcDecryptedMaxLength(encrypted.length)
	 * 	即计算出的结果一定大于等于实际解密后的长度
	 */
	public int calcDecryptedMaxLength(int length){
		int resultUnitLen=priKey.getModulus().bitLength()/8;
		//大于此长度有时也可以加密但为确保成功使用最小值
		int dataBlockNum=length/resultUnitLen;
		return (resultUnitLen-11)*dataBlockNum;
	}
	/**
	 * 计算给出长度的明文加密后的长度
	 * 是函数关系
	 */
	public int calcEncryptedLength(int length){
		int resultUnitLen=pubKey.getModulus().bitLength()/8;
		//大于此长度有时也可以加密但为确保成功使用最小值
		int dataUnitLen=resultUnitLen-11;
		int dataBlockNum=length/dataUnitLen;
		if(length%dataUnitLen==0){
			dataBlockNum+=1;
		}
		return resultUnitLen*dataBlockNum;
	}
	//可能同一个类表示的并不是同一个密钥对,所以单独提供获取公钥modulus长度的方法
	public int getPublicKeyModulusLength() {
		return pubKey.getModulus().bitLength()/8;
	}
	public RSAPublicKey getPublicKey() {
		return pubKey;
	}
	public PrivateKey getPrivateKey() {
		return priKey;
	}
	public byte[]getEncodedPrivateKey(){
		return priKey.getEncoded();
	}
	public byte[]getEncodedPublicKey(){
		return pubKey.getEncoded();
	}
	public void getEncodedPrivateKey(ByteBuffer buf){
		buf.put(getEncodedPrivateKey());
	}
	public void getEncodedPublicKey(ByteBuffer buf) {
		buf.put(getEncodedPublicKey());
	}
	public void setPrivateKey(byte[]key)throws InvalidKeySpecException{
		PKCS8EncodedKeySpec kps=new PKCS8EncodedKeySpec(key);
		try{
			KeyFactory kf=KeyFactory.getInstance(algorithm);
			RSAPrivateKey pri=(RSAPrivateKey)kf.generatePrivate(kps);
			setPrivateKey(pri);
		}catch(NoSuchAlgorithmException e){
			e.printStackTrace();
		}
	}
	public void setPrivateKey(RSAPrivateKey pk){
		priKey=pk;
		try{
			decrypt.init(Cipher.DECRYPT_MODE,pk);
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	public void setPrivateKey(byte[]bigIntegerE,byte[]bigIntegerM) throws InvalidKeySpecException{
		RSAPrivateKeySpec spec=new RSAPrivateKeySpec(new BigInteger(bigIntegerM),new BigInteger(bigIntegerE));
		try{
			KeyFactory kf=KeyFactory.getInstance(algorithm);
			RSAPrivateKey pub=(RSAPrivateKey)kf.generatePrivate(spec);
			setPrivateKey(pub);
		}catch(NoSuchAlgorithmException e){
			e.printStackTrace();
		}
	}
	public void setPublicKey(byte[]key,int offset,int len)throws InvalidKeySpecException{
		byte[] kk = Arrays.copyOfRange(key,offset,offset+len);
		setPublicKey(kk);
	}
	public void setPublicKey(byte[]key)throws InvalidKeySpecException{
		X509EncodedKeySpec kps=new X509EncodedKeySpec(key);
		try{
			KeyFactory kf=KeyFactory.getInstance(algorithm);
			RSAPublicKey pub=(RSAPublicKey)kf.generatePublic(kps);
			setPublicKey(pub);
		}catch(NoSuchAlgorithmException e){
			e.printStackTrace();
		}
	}
	public void setPublicKey(RSAPublicKey k){
		//int l=k.getModulus().bitLength();
		//encDataMaxLen=l/8-11;
		pubKey=k;
		try{
			encrypt.init(Cipher.ENCRYPT_MODE,k);
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	public void setPublicKey(byte[]bigIntegerE,byte[]bigIntegerM) throws InvalidKeySpecException{
		RSAPublicKeySpec spec=new RSAPublicKeySpec(new BigInteger(bigIntegerM),new BigInteger(bigIntegerE));
		try{
			KeyFactory kf=KeyFactory.getInstance(algorithm);
			RSAPublicKey pub=(RSAPublicKey)kf.generatePublic(spec);
			setPublicKey(pub);
		}catch(NoSuchAlgorithmException e){
			e.printStackTrace();
		}
	}
	public byte[] encryptByPublicKey(byte[]data){
		return encryptByPublicKey(data,0,data.length);
	}
	public byte[] encryptByPublicKey(byte[]data,int offset,int length){
		try{
			int resultUnitLen=pubKey.getModulus().bitLength()/8;
			//大于此长度有时也可以加密但为确保成功使用最小值
			int dataUnitLen=resultUnitLen-11;
			int dataBlockNum=length/dataUnitLen;
			if(length%dataUnitLen!=0){
				dataBlockNum+=1;
			}
			byte[]result=new byte[dataBlockNum*resultUnitLen];
			for(int i=0;i<dataBlockNum;i++){
				int dataLen=(offset+dataUnitLen>data.length)?data.length-offset:dataUnitLen;
				try{
					//Counter.update();
					encrypt.doFinal(data,offset,dataLen,result,resultUnitLen*i);
					//Counter.info();
				}catch(ShortBufferException e){
					e.printStackTrace();
				}
				//System.arraycopy(encrypted,0,result,resultUnitLen*i,resultUnitLen);
				offset+=dataUnitLen;
			}
			return result;
		}catch(IllegalBlockSizeException e){
			e.printStackTrace();
		}catch(BadPaddingException e){
			e.printStackTrace();
		}
		return null;
	}
	public byte[]decryptByPrivateKey(byte[]data){
		return decryptByPrivateKey(data,0,data.length);
	}
	public byte[]decryptByPrivateKey(byte[]data,int offset,int length){
		int resultUnitLen=priKey.getModulus().bitLength()/8;
		//大于此长度有时也可以加密但为确保成功使用最小值
		int dataBlockNum=length/resultUnitLen;
		int dataUnitLen=resultUnitLen-11;
		try{
			byte[]b=new byte[resultUnitLen*dataBlockNum];
			int l=0;
			for(int i=0;i<dataBlockNum;i++){
				try{
					l+=decrypt.doFinal(data,offset,resultUnitLen,b,dataUnitLen*i);
				}catch(ShortBufferException e){
					e.printStackTrace();
				}
				offset+=resultUnitLen;
			}
			return Arrays.copyOf(b,l);
		}catch(IllegalBlockSizeException e){
			e.printStackTrace();
		}catch(BadPaddingException e){
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 使用私钥进行签名
	 * 签名结果长度与密钥长度相等,密钥长度默认为64字节
	 */
	public void signature(byte[]data,int offset,int len,byte[]buf,int bufOffset) {
		try {
			Signature signature=Signature.getInstance(signature_alghorithm);
			signature.initSign(priKey);
			signature.update(data, offset,len);
			signature.sign(buf, bufOffset, buf.length-bufOffset);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (SignatureException e) {
			e.printStackTrace();
		}
	}
	/**
	 * 使用公钥进行数据校验,需要保证sign从offset位置开始有至少公钥长度的数据
	 */
	public boolean verify(byte[]data,int offset,int len,byte[]sign,int signOffset){
		Signature s;
		try {
			s = Signature.getInstance(signature_alghorithm);
			s.initVerify(pubKey);
			s.update(data, offset, len);
			return s.verify(sign, signOffset,getPublicKeyModulusLength());
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (SignatureException e) {
			e.printStackTrace();
		}
		return false;
	}
}

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值