当前位置: 首页 > news >正文

Java手动实现完整的加密通信流程

默认的像哥斯拉这种通信中会把AES密钥明文发送到流量里,甚至有些硬编码到木马上,我脑海中一直有一种利用RAS实现对称加密密钥的安全分发,从而实现后门与客户端的完整加密通信的过程,来一起探讨下。

RAS加密

首先温故一下RSA相关的知识

RAS算法中

公钥(e,n)

e是公钥指数,n作为模数

公钥用来加密明文

私钥(d,n)

d是私钥指数

pq为两个互质的质数

欧拉函数

ϕ(n)=(p-1)*(q-1)

如果n是质数,则 φ(n)=n-1 。因为质数与小于它的每一个数,都构成互质关系。比如 5 与 1、2、3、4 都构成互质关系。

欧拉定理

x的φ(n)次方的值,模n的余数一定为1

扩展欧几里得算法

用于计算两个整数的最大公约数(GCD)的同时,还能找到一组整数解 x 和 y,使得:

ax+by=gcd(a,b)

在 RSA 密钥生成中,扩展欧几里得算法用于求解私钥 d,其中 d 满足以下条件:

**d×e≡1 (mod ϕ(n))**

这表示de相乘mod**ϕ(n)**的值为1

欧几里得扩展算法求d逆元

假设c是a,b的一个公约数,则有:

a mod c=0, b mod c=0

⇒(a−b) mod c=0

⇒(a−kb) mod c=0

⇒(a mod b) mod c=0

这里的e和n是互质的,那么可以得到gcd(a,b)=gcd(b,a mod b)

static Integer[] exgcd(int a, int b)
{if (b == 0){return new Integer[]{ 1, 1 };}Integer[] ret = exgcd(b, a % b);int t = ret[0];ret[0] = ret[1];ret[1] = t - (a / b * ret[1]);return ret;
}

假设我们有:

ax1+by1=gcd(a,b)

通过递归的方式,我们得到了:

bx2+(a mod b)y2=gcd(b,a mod b)

因为 a mod b = a - (a // b) * b//为整除,我们可以将其代入上式:

bx2+(a−(a//b)∗b)y2=gcd(a,b)

展开并整理得到:

a**y2**+b**(x2−(a//b)∗y2)**=gcd(a,b)

因此,我们可以得到新的 x 和 y 值:

  • 新的 x 是原来的 y2
  • 新的 y 是 x2 - (a // b) * y2

故得到公式代码

RAS加密中明文长度

RAS加密过程涉及对大整数进行模幂运算,所以明文的长度不能超过模数n的byte长度。

RAS密钥长度和padding方式决定了明文的最大长度。

假设使用的 RSA 密钥长度为 2048 位(256 字节),我们可以计算明文的最大长度:

  • 使用 PKCS#1 填充
    • 最大明文长度 = 密钥长度(字节数) - 填充字节数
    • 对于 2048 位密钥,最大明文长度 = 256 - 11 = 245 字节

  • 使用 OAEP 填充
    • 最大明文长度 = 密钥长度(字节数) - 填充字节数
    • 对于 2048 位密钥,最大明文长度 = 256 - 42 = 214 字节

RAS加密的主要作用就是用来分发一个加密后的对称加密密钥,而不是加密整个消息。

字符串格式,一位就是一个字节,如果加密明文为int型,那么一个字节

就是8位 2^8=1024

最小值 [-2^8,2^8-1]也就是[-1024,1023],所以一个字节差不多就是4位十进制数字大小范围,不同字节计算出来int可占位也是不同的

4个字节差不多就是最大10位的十进制数

而对于字符串来说 一个字符也就是一个byte

package RAS;import java.io.UnsupportedEncodingException;
import java.math.BigInteger;class RsaKeySize {public static void main(String[] args) {// 给定的 n、e 和 d 值BigInteger n = new BigInteger("123456789");BigInteger e = new BigInteger("65537");BigInteger d = new BigInteger("1959087718915866815402369");// 计算 n 的位数int bitLength = n.bitLength();// 计算字节数int byteLength = (bitLength + 7) / 8; // 进位计算 保证字节位数是大于等于被8整除位的System.out.println("Bit length of n: " + bitLength);System.out.println("Byte length of n: " + byteLength);String asciiString = "Hello123!";String utf8String = "你好!";try {byte[] asciiBytes = asciiString.getBytes("UTF-8");byte[] utf8Bytes = utf8String.getBytes("UTF-8");System.out.println("ASCII string bytes: " + asciiBytes.length); // 输出 9System.out.println("UTF-8 string bytes: " + utf8Bytes.length);  // 输出 15} catch (UnsupportedEncodingException ex) {ex.printStackTrace();}}
}

实现RAS加密

public class RasCrypt {private static final SecureRandom random = new SecureRandom();private static final BigInteger e = BigInteger.valueOf(65537);static {random.setSeed(new Date().getTime());}// 获取一个质数public static BigInteger getPrime(int bitLength) {BigInteger p;while (!(p = BigInteger.probablePrime(bitLength, random)).isProbablePrime(100)) {continue;}return p;}// 生成rsa三个参数public static BigInteger[] genRsaKey() {BigInteger p, q, n, φ, d, e = BigInteger.valueOf(65537);p = getPrime(200);q = getPrime(200);n = p.multiply(q);//φ=(p-1)*(q-1)φ = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));d = extGcd(e, φ)[0];BigInteger[] result = new BigInteger[]{n, e, d};if (d.compareTo(BigInteger.ONE) < 0 || !p.gcd(q).equals(BigInteger.ONE)) {return genRsaKey();}/*        System.out.println(p);System.out.println(q);*/return result;}// rsa加密public static byte[] rsaEncrype(byte[] m, BigInteger n, BigInteger e) {if (e == null) {e = BigInteger.valueOf(65537);}return new BigInteger(m).modPow(e, n).toByteArray();}// rsa解密public static byte[] rsaDecrype(byte[] c, BigInteger n, BigInteger d) {return new BigInteger(c).modPow(d, n).toByteArray();}// 扩展欧几里得算法,求私钥dpublic static BigInteger[] extGcd(BigInteger a, BigInteger b) {BigInteger[] result = null;if (b.equals(BigInteger.ZERO)) {result = new BigInteger[]{BigInteger.ONE, BigInteger.ZERO};return result;}result = extGcd(b, a.mod(b));BigInteger x = result[1];BigInteger y = result[0].subtract(a.divide(b).multiply(x));result = new BigInteger[]{x, y};return result;}public static void main(String[] args) {BigInteger p  = getPrime(5);BigInteger[] result =genRsaKey();BigInteger[] keys = genRsaKey();BigInteger n =  keys[0];BigInteger privateKey =  keys[2];System.out.println("n,e,d:"+ Arrays.toString(keys));String key ="";key = padTo16Bytes(key);String originalMessage = key;byte[] encryptedMessage = rsaEncrype(originalMessage.getBytes(), n, e);byte[] decryptedMessage = rsaDecrype(encryptedMessage, n, privateKey);String base64Encoded = Base64.getEncoder().encodeToString(encryptedMessage);// 输出Base64编码的字符串System.out.println("Base64 Encoding Ciphertext: " + base64Encoded);System.out.println("Private Key is :"+Base64.getEncoder().encodeToString(originalMessage.getBytes(StandardCharsets.UTF_8)));System.out.println("Ciphertext Byte Array:"+new String(encryptedMessage));if (new String(decryptedMessage).equals(originalMessage)) {System.out.println("AES key is:"+new String(decryptedMessage));System.out.println("Public and private keys match!");} else {System.out.println("Public and private keys do not match.");}}
}

AES加密

利用RAS来分发对称密钥,不过密钥长度有要求,需要满足标准的16、24、32字节,也就是128位、192位和256位

对于加密的明文有区块填充要求,可以采用多种填充方式

Cipher c = Cipher.getInstance(aesMode);

aesMode="AES/OFB/ISO10126Padding";表示AES加密采用OFB加密模式,采用随机填充

常见的加密模式:

  • ECB(Electronic Codebook):最简单的模式,每个块独立加密。
  • CBC(Cipher Block Chaining):将前一个密文块与当前块异或后再加密。
  • CFB(Cipher Feedback):类似于 CBC,但更适合流加密。
  • OFB(Output Feedback):将加密器的输出作为下一个块的输入。
  • GCM(Galois/Counter Mode):一种支持认证的加密模式,确保数据完整性。

填充方式:

  • PKCS5Padding:将缺少的字节填充为与块大小相同的数值。
  • ISO10126Padding:随机填充,并用最后一个字节指明填充的字节数。
  • NoPadding:表示不使用填充。通常,分组加密算法要求输入的数据长度是加密块大小的整数倍(AES 的块大小是 16 字节)。如果输入的数据不足以填满最后一个块,通常会使用填充(比如 PKCS5Padding)。NoPadding 表示你必须自行确保数据是正确大小,否则会导致异常。

这里随机生成密钥key16位或者32位,iv为随机生成

public class aesEncrypt {private static final String aesMode = "AES/OFB/ISO10126Padding";private static final SecureRandom random = new SecureRandom();static {random.setSeed(new Date().getTime());}// 随机生成16字节的IVprivate static byte[] generateIV() {byte[] iv = new byte[16]; // AES的IV是16字节random.nextBytes(iv);return iv;}/*** hex解码为byte** @param data* @return*/public static byte[] hex2b(String data) {byte[] byteArray = new BigInteger(data, 36).toByteArray();if (byteArray[0] == 0) {byte[] output = new byte[byteArray.length - 1];System.arraycopy(byteArray, 1, output,0, output.length);return output;}return byteArray;}/*** byte编码为hex** @param data* @return*/public static String b2hex(byte[] data){return new BigInteger(1, data).toString(36).toLowerCase();}/*** 字节转为字符串** @param data* @return*/public static String b2s(byte[] data) {try {return new String(data, "utf-8");} catch (Exception e) {return "";}}/*** 字符串转为字节** @param data* @return*/public static byte[] s2b(String data) {try {return data.getBytes("utf-8");} catch (Exception e) {return new byte[]{};}}/*** aes加密** @param s* @param k* @return*/public static byte[] aesEncrypt(byte[] s, String k, byte[] iv) {try {Cipher c = Cipher.getInstance(aesMode);c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k.getBytes(), "AES"), new IvParameterSpec(iv));return c.doFinal(s);} catch (Exception e) {e.printStackTrace();return null;}}/*** aes解密** @param s* @param k* @return*/public static byte[] aesDecrypt(byte[] s, String k, byte[] iv) {try {Cipher c = Cipher.getInstance(aesMode);c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(k.getBytes(), "AES"), new IvParameterSpec(iv));return c.doFinal(s);} catch (Exception e) {e.printStackTrace();return null;}}// 填充或截断字符串以使其为16字节private static String padTo16Bytes(String input) {StringBuilder paddedInput = new StringBuilder(input);int padding_le = 16;if (input.length()>=16 && input.length()<=32 ){padding_le =32;}else if(input.length()>32)return input.substring(0,32);while (paddedInput.length() < padding_le) {paddedInput.append((char)(random.nextInt(94)+32)); // 补全为16、32字节}return paddedInput.toString();}public static void main(String[] args) {String key ="";key = padTo16Bytes(key);System.out.println("padding Key:"+key);String se = "test shellcode \\xfc\\xe8\\x89\\x00..........";byte[] test =s2b(se);byte[] shellcode={0x68,0x65,0x6c,0x6c,0x6f,0x77,0x6f,0x72,0x64};byte[]  iv = generateIV();byte[] mi = aesEncrypt(test,key,iv);byte[] test_1= aesDecrypt(mi,key,iv);System.out.println(new String(test_1));}
}

加密通信思维图

这种通信流程可以用于冰蝎、哥斯拉等实现加密的通信流程,在流量端也无法直接获取到私钥,没法解密通信内容。

这里主要是分享实现流程和思路,可以微调操作的地方还是很多,大家可以发挥想象。


http://www.mrgr.cn/news/56895.html

相关文章:

  • 掌握ElasticSearch(三):探索核心概念——文档、索引、分片、倒排索引
  • 【AUTOSAR标准文档】AotuSar结构横向分层详解(RTE、BSW)
  • 6.mysql安装【Docker】
  • 使用Java和SNMP4J实现SNMP操作
  • 一次使用LD_DEBUG定位问题的经历
  • 五、Shell 脚本编程:从基础至实用实例
  • Go中的指针指向指针(双指针)
  • h5和app区分
  • 什么是销售与销售管理?
  • avue-crud组件,输入框回车搜索问题
  • VScode超简单豆包MarsCode部署+初体验
  • 同事竟然用了这个注解@Deprecated
  • 2024 JavaScript 前端开发:技术融合、优势与常用库
  • ssm智慧游客服务系统-计算机毕业设计源码82047
  • Python教程:制作贪吃蛇游戏存以exe文件运行
  • Xinference 注册本地模型
  • 【MySQL 保姆级教学】表的约束--详细(6)
  • 谷歌仓库管理工具repo
  • Midjourney最新版本爆火全网!网友:和摄影几乎没区别!!!
  • leetcode:输入m,n(1 < m < n < 1000000),返回区间[m,n]内的所有素数的个数
  • 在Maple中创建交互式应用程序
  • Maven 不同环境灵活构建
  • Kotlin学习第一课
  • 通过滑动控制 图片3d(多张视频序列帧图片) 展示
  • linux调用exit函数退出进程,变跟的文件内容会立即同步到磁盘吗
  • 人大金仓下载,有人知道怎么解决吗