关于java RSA密钥的长度问题

2023-10-27

最近在搞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;
	}
}

 

 

 

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

关于java RSA密钥的长度问题 的相关文章

随机推荐

  • 【python数据挖掘课程】十.Pandas、Matplotlib、PCA绘图实用代码补充

    这篇文章主要是最近整理 数据挖掘与分析 课程中的作品及课件过程中 收集了几段比较好的代码供大家学习 同时 做数据分析到后面 除非是研究算法创新的 否则越来越觉得数据非常重要 才是有价值的东西 后面的课程会慢慢讲解Python应用在Hadoo
  • 【Shell牛客刷题系列】SHELL23 nginx日志分析1-IP统计

    该系列是基于牛客Shell题库 针对具体题目进行查漏补缺 学习相应的命令 刷题链接 牛客题霸 Shell篇 该系列文章都放到专栏下 专栏链接为 专栏 Shell 欢迎关注专栏 本文知识预告 本文首先复习了substr 截取字符串函数和sor
  • 【H.264/AVC视频编解码技术详解】十三、熵编码算法(3):CAVLC原理

    H 264 AVC视频编解码技术详解 视频教程已经在 CSDN学院 上线 视频中详述了H 264的背景 标准协议和实现 并通过一个实战工程的形式对H 264的标准进行解析和实现 欢迎观看 纸上得来终觉浅 绝知此事要躬行 只有自己按照标准文档
  • 高德vue-amap使用(一)标记点位获取地址及经纬度

    vue高德地图 图片示例 准备工作 安装与配置 npm安装 main js配置 使用 父组件 子组件 图片示例 准备工作 高德开放平台 https lbs amap com 注册登录后进入控制台 在应用管理下我的应用里创建应用添加key 就
  • PAT乙级1039 到底买不买 (20 分)

    1039 到底买不买 20 分 一 问题描述 小红想买些珠子做一串自己喜欢的珠串 卖珠子的摊主有很多串五颜六色的珠串 但是不肯把任何一串拆散了卖 于是小红要你帮忙判断一下 某串珠子里是否包含了全部自己想要的珠子 如果是 那么告诉她有多少多余
  • Android音频系统之AudioFlinger(二)

    1 1 1 音频设备的管理 虽然AudioFlinger实体已经成功创建并初始化 但到目前为止它还是一块静态的内存空间 没有涉及到具体的工作 从职能分布上来讲 AudioPolicyService是策略的制定者 比如什么时候打开音频接口设备
  • LeetCode-1344. Jump Game V

    Given an array of integers arr and an integer d In one step you can jump from index i to index i x where i x lt arr leng
  • 计算机网络笔记:动态主机配置协议(DHCP)

    协议配置 为了把协议软件做成通用和便于移植的 协议软件的编写者不会把所有的细节都固定在源代码中 相反 他们把协议软件参数化 这就使得在很多台计算机上有可能使用同一个经过编译的二进制代码 一台计算机和另一台计算机的很多区别 都可以通过一些不同
  • delphi 自定义popupmenu_delphi PopupMenu动态创建菜单

    动态地创建菜单 通常我们使用以下的语句PopupMenu1 TPopupMenu Create Self Item TMenuItem Create PopupMenu1 Item Caption First Menu Item OnCli
  • 赛元SC92F7250单片机开发

    文章目录 芯片简介 开发环境 安装Keil C51 安装SOC Keil插件 配置Keil环境 基本功能开发 gpio操作 外部中断 定时器 看门狗 EEPROM 注意事项 芯片简介 赛元SC92F7250单片机 是国产的超低成本选择 对于
  • QColorDialog中文翻译完整版

    QColorDialog 界面默认显示英文文本 想要翻译成中文 则进行如下步骤 1 搜索qt安装目录下qt zh CN qm文件 拷贝到你的项目Res文件夹 不要问我为什么在这个文件夹 看到最后的 注意 你就知道了 有很多路径下都有 比如
  • jsonp

    一 cookie封装 function getCookie key 获取所有的cookie document cookie let cookie document cookie username zhangsan password 1234
  • 华为服务器pxe装系统,pxe启动服务器

    pxe启动服务器 内容精选 换一换 根据给定的云服务器ID列表 批量启动云服务器 一次最多可以启动1000台 POST v1 project id cloudservers action参数说明请参见表1 参数说明参数是否必选描述proje
  • C语言:选择+编程(每日一练Day9)

    目录 选择题 题一 题二 题三 题四 题五 编程题 题一 自除数 思路一 题二 除自身以外数组的乘积 思路二 本人实力有限可能对一些地方解释和理解的不够清晰 可以自己尝试读代码 或者评论区指出错误 望海涵 感谢大佬们的一键三连 感谢大佬们的
  • 解决IDEA使用Spring Initializr创建项目时无法连接到https://start.spring.io的问题

    IDEA使用Spring Initializr创建项目时报错 但在浏览器中输入https start spring io能正常访问 点击 Check connection 测试一下配置 输入 https start spring io 提示
  • vscode--vue注释快捷方式

    目录 vscode配置 配置文件 vue javascript vscode配置 1 F1 gt 输入 首选项 配置用户代码片段 2 选择下面这个 3 输入或选择需要的配置 配置文件 vue vue json Print to vueTit
  • 谈谈从小公司进入大厂,我都做对了哪些事?

    故事得从19年的那个秋天说起 同事小丽给俺发了条消息 俺对象进城了 纳尼 你对象从乡下来了 不是 俺对象去大公司了 哦哦 这么个意思啊 你对象真棒 考官都问什么了 我看看 于是俺也蠢蠢欲动 开启了大厂面试的征程 下面是俺的经验 可以给诸位借
  • 从入门到精通真不难:Python最佳学习路线(学习教程)分享

    随着人工智能时代的来临 Python开始崭露头角并迅速吸引了人们的广泛关注 很多人想要从事Python开发 但需要学什么内容 怎么快速学习呢 接下来就给大家分享Python最佳学习路线 帮你快速找准自己定位 第一阶段Python基础与Lin
  • 【Spring Cloud Alibaba】链路监控 Sleuth+Zipkin

    Spring Cloud Alibaba 链路监控 Sleuth Zipkin 1 Zipkin 2 项目集成 3 持久化 4 Kafka数据传输 4 1 环境搭建 4 2 Zipkin 配置 5 采样策略 5 1 采样率策略 5 2 抽样
  • 关于java RSA密钥的长度问题

    最近在搞udp可靠通信 不单单是丢失重传 为了进行密钥传输学习一下密钥长度的一些知识 mark一下 java默认的rsa填充方案为RSA ECB PKCS1Padding 一般说的rsa密钥长度单位是bit 本文所有长度单位均为byte 除