`
crazier9527
  • 浏览: 1024516 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

RSA算法介绍及JAVA实现

    博客分类:
  • Java
阅读更多
最近处理RSA算法,找了一些相关的资料和代码,整理了一下,汇总成这篇文章。

<一>基础

RSA算法非常简单,概述如下:
找两素数p和q
取n=p*q
取t=(p-1)*(q-1)
取任何一个数e,要求满足e<t并且e与t互素(就是最大公因数为1)
取d*e%t==1

这样最终得到三个数: n  d  e

设消息为数M (M <n)
设c=(M**d)%n就得到了加密后的消息c
设m=(c**e)%n则 m == M,从而完成对c的解密。
注:**表示次方,上面两式中的d和e可以互换。

在对称加密中:
n d两个数构成公钥,可以告诉别人;
n e两个数构成私钥,e自己保留,不让任何人知道。
给别人发送的信息使用e加密,只要别人能用d解开就证明信息是由你发送的,构成了签名机制。
别人给你发送信息时使用d加密,这样只有拥有e的你能够对其解密。

rsa的安全性在于对于一个大数n,没有有效的方法能够将其分解
从而在已知n d的情况下无法获得e;同样在已知n e的情况下无法
求得d。



<二>实践

接下来我们来一个实践,看看实际的操作:
找两个素数:
p=47
q=59
这样
n=p*q=2773
t=(p-1)*(q-1)=2668
取e=63,满足e<t并且e和t互素
用perl简单穷举可以获得满主 e*d%t ==1的数d:
C:\Temp>perl -e "foreach $i (1..9999){ print($i),last if $i*63%2668==1 }"
847
即d=847

最终我们获得关键的
n=2773
d=847
e=63

取消息M=244我们看看

加密:

c=M**d%n = 244**847%2773
用perl的大数计算来算一下:
C:\Temp>perl -Mbigint -e "print 244**847%2773"
465
即用d对M加密后获得加密信息c=465

解密:

我们可以用e来对加密后的c进行解密,还原M:
m=c**e%n=465**63%2773 :
C:\Temp>perl -Mbigint -e "print 465**63%2773"
244
即用e对c解密后获得m=244 , 该值和原始信息M相等。


<三>字符串加密

把上面的过程集成一下我们就能实现一个对字符串加密解密的示例了。
每次取字符串中的一个字符的ascii值作为M进行计算,其输出为加密后16进制
的数的字符串形式,按3字节表示,如01F

代码如下:

#!/usr/bin/perl -w
#RSA 计算过程学习程序编写的测试程序
#watercloud 2003-8-12
#

use strict;
use Math::BigInt;

my %RSA_CORE = (n=>2773,e=>63,d=>847); #p=47,q=59

my $N=new Math::BigInt($RSA_CORE{n});
my $E=new Math::BigInt($RSA_CORE{e});
my $D=new Math::BigInt($RSA_CORE{d});

print "N=$N D=$D E=$E\n";

sub RSA_ENCRYPT
{

    my $r_mess = shift @_;
    my ($c,$i,$M,$C,$cmess);

    for($i=0;$i < length($$r_mess);$i++)
    {

        $c=ord(substr($$r_mess,$i,1));
        $M=Math::BigInt->new($c);
        $C=$M->copy(); $C->bmodpow($D,$N);
        $c=sprintf "%03X",$C;
        $cmess.=$c;
    }

    return \$cmess;
}


sub RSA_DECRYPT
{

    my $r_mess = shift @_;
    my ($c,$i,$M,$C,$dmess);

    for($i=0;$i < length($$r_mess);$i+=3)
    {

        $c=substr($$r_mess,$i,3);
        $c=hex($c);
        $M=Math::BigInt->new($c);
        $C=$M->copy(); $C->bmodpow($E,$N);
        $c=chr($C);
        $dmess.=$c;
    }

    return \$dmess;
}


my $mess="RSA 娃哈哈哈~~~";
$mess=$ARGV[0] if @ARGV >= 1;
print "原始串:",$mess,"\n";

my $r_cmess = RSA_ENCRYPT(\$mess);
print "加密串:",$$r_cmess,"\n";

my $r_dmess = RSA_DECRYPT($r_cmess);
print "解密串:",$$r_dmess,"\n";

#EOF


测试一下:
C:\Temp>perl rsa-test.pl
N=2773  D=847  E=63
原始串:RSA 娃哈哈哈~~~
加密串:5CB6CD6BC58A7709470AA74A0AA74A0AA74A6C70A46C70A46C70A4
解密串:RSA 娃哈哈哈~~~


C:\Temp>perl rsa-test.pl 安全焦点(xfocus)
N=2773  D=847  E=63
原始串:安全焦点(xfocus)
加密串:3393EC12F0A466E0AA9510D025D7BA0712DC3379F47D51C325D67B
解密串:安全焦点(xfocus)



<四>提高

前面已经提到,rsa的安全来源于n足够大,我们测试中使用的n是非常小的,根本不能保障安全性,
我们可以通过RSAKit、RSATool之类的工具获得足够大的N 及D E。
通过工具,我们获得1024位的N及D E来测试一下:

n=0x328C74784DF31119C526D18098EBEBB943B0032B599CEE13CC2BCE7B5FCD15F90B66EC3A85F5005D
BDCDED9BDFCB3C4C265AF164AD55884D8278F791C7A6BFDAD55EDBC4F017F9CCF1538D4C2013433B383B
47D80EC74B51276CA05B5D6346B9EE5AD2D7BE7ABFB36E37108DD60438941D2ED173CCA50E114705D7E2
BC511951

d=0x10001

e=0xE760A3804ACDE1E8E3D7DC0197F9CEF6282EF552E8CEBBB7434B01CB19A9D87A3106DD28C523C2995
4C5D86B36E943080E4919CA8CE08718C3B0930867A98F635EB9EA9200B25906D91B80A47B77324E66AFF2
C4D70D8B1C69C50A9D8B4B7A3C9EE05FFF3A16AFC023731D80634763DA1DCABE9861A4789BD782A592D2B
1965


设原始信息
M=0x11111111111122222222222233333333333

完成这么大数字的计算依赖于大数运算库,用perl来运算非常简单:

A) 用d对M进行加密如下:
c=M**d%n :
C:\Temp>perl -Mbigint -e " $x=Math::BigInt->bmodpow(0x11111111111122222222222233
333333333, 0x10001, 0x328C74784DF31119C526D18098EBEBB943B0032B599CEE13CC2BCE7B5F
CD15F90B66EC3A85F5005DBDCDED9BDFCB3C4C265AF164AD55884D8278F791C7A6BFDAD55EDBC4F0
17F9CCF1538D4C2013433B383B47D80EC74B51276CA05B5D6346B9EE5AD2D7BE7ABFB36E37108DD6
0438941D2ED173CCA50E114705D7E2BC511951);print $x->as_hex"
0x17b287be418c69ecd7c39227ab681ac422fcc84bb35d8a632543b304de288a8d4434b73d2576bd
45692b007f3a2f7c5f5aa1d99ef3866af26a8e876712ed1d4cc4b293e26bc0a1dc67e247715caa6b
3028f9461a3b1533ec0cb476441465f10d8ad47452a12db0601c5e8beda686dd96d2acd59ea89b91
f1834580c3f6d90898

即用d对M加密后信息为:
c=0x17b287be418c69ecd7c39227ab681ac422fcc84bb35d8a632543b304de288a8d4434b73d2576bd
45692b007f3a2f7c5f5aa1d99ef3866af26a8e876712ed1d4cc4b293e26bc0a1dc67e247715caa6b
3028f9461a3b1533ec0cb476441465f10d8ad47452a12db0601c5e8beda686dd96d2acd59ea89b91
f1834580c3f6d90898



B) 用e对c进行解密如下:

m=c**e%n :
C:\Temp>perl -Mbigint -e " $x=Math::BigInt->bmodpow(0x17b287be418c69ecd7c39227ab
681ac422fcc84bb35d8a632543b304de288a8d4434b73d2576bd45692b007f3a2f7c5f5aa1d99ef3
866af26a8e876712ed1d4cc4b293e26bc0a1dc67e247715caa6b3028f9461a3b1533ec0cb4764414
65f10d8ad47452a12db0601c5e8beda686dd96d2acd59ea89b91f1834580c3f6d90898,  0xE760A
3804ACDE1E8E3D7DC0197F9CEF6282EF552E8CEBBB7434B01CB19A9D87A3106DD28C523C29954C5D
86B36E943080E4919CA8CE08718C3B0930867A98F635EB9EA9200B25906D91B80A47B77324E66AFF
2C4D70D8B1C69C50A9D8B4B7A3C9EE05FFF3A16AFC023731D80634763DA1DCABE9861A4789BD782A
592D2B1965,  0x328C74784DF31119C526D18098EBEBB943B0032B599CEE13CC2BCE7B5FCD15F90
B66EC3A85F5005DBDCDED9BDFCB3C4C265AF164AD55884D8278F791C7A6BFDAD55EDBC4F017F9CCF
1538D4C2013433B383B47D80EC74B51276CA05B5D6346B9EE5AD2D7BE7ABFB36E37108DD60438941
D2ED173CCA50E114705D7E2BC511951);print $x->as_hex"
0x11111111111122222222222233333333333
(我的P4 1.6G的机器上计算了约5秒钟)

得到用e解密后的m=0x11111111111122222222222233333333333  == M


C) RSA通常的实现
RSA简洁幽雅,但计算速度比较慢,通常加密中并不是直接使用RSA 来对所有的信息进行加密,
最常见的情况是随机产生一个对称加密的密钥,然后使用对称加密算法对信息加密,之后用
RSA对刚才的加密密钥进行加密。


最后需要说明的是,当前小于1024位的N已经被证明是不安全的
自己使用中不要使用小于1024位的RSA,最好使用2048位的。

----------------------------------------------------------

一个简单的RSA算法实现JAVA源代码:

filename:RSA.java

<script src="/plug-ins/SyntaxHighlighter/shCore.js"></script>

  1. /*  
  2.  
  3. * Created on Mar 3, 2005  
  4.  
  5. *  
  6.  
  7. * TODO To change the template for this generated file go to  
  8.  
  9. * Window - Preferences - Java - Code Style - Code Templates  
  10.  
  11. */  
  12.   
  13.   
  14. import java.math.BigInteger;   
  15.   
  16. import java.io.InputStream;   
  17.   
  18. import java.io.OutputStream;   
  19.   
  20. import java.io.FileInputStream;   
  21.   
  22. import java.io.FileOutputStream;   
  23.   
  24. import java.io.FileNotFoundException;   
  25.   
  26. import java.io.IOException;   
  27.   
  28. import java.io.FileWriter;   
  29.   
  30. import java.io.FileReader;   
  31.   
  32. import java.io.BufferedReader;   
  33.   
  34. import java.util.StringTokenizer;   
  35.   
  36.   
  37. /**  
  38.  
  39. * @author Steve  
  40.  
  41. *  
  42.  
  43. * TODO To change the template for this generated type comment go to  
  44.  
  45. * Window - Preferences - Java - Code Style - Code Templates  
  46.  
  47. */  
  48.   
  49. public class RSA {   
  50.   
  51.       
  52.   
  53.     /**  
  54.  
  55.      * BigInteger.ZERO  
  56.  
  57.      */  
  58.   
  59.     private static final BigInteger ZERO = BigInteger.ZERO;   
  60.   
  61.       
  62.   
  63.     /**  
  64.  
  65.      * BigInteger.ONE  
  66.  
  67.      */  
  68.   
  69.     private static final BigInteger ONE = BigInteger.ONE;   
  70.   
  71.       
  72.   
  73.     /**  
  74.  
  75.      * Pseudo BigInteger.TWO  
  76.  
  77.      */  
  78.   
  79.     private static final BigInteger TWO = new BigInteger("2");   
  80.   
  81.       
  82.   
  83.     private BigInteger myKey;   
  84.   
  85.       
  86.   
  87.     private BigInteger myMod;   
  88.   
  89.       
  90.   
  91.     private int blockSize;   
  92.   
  93.       
  94.   
  95.     public RSA (BigInteger key, BigInteger n, int b) {   
  96.   
  97.         myKey = key;   
  98.   
  99.         myMod = n;   
  100.   
  101.         blockSize = b;   
  102.   
  103.     }   
  104.   
  105.       
  106.   
  107.     public void encodeFile (String filename) {   
  108.   
  109.         byte[] bytes = new byte[blockSize / 8 + 1];   
  110.   
  111.         byte[] temp;   
  112.   
  113.         int tempLen;   
  114.   
  115.         InputStream is = null;   
  116.   
  117.         FileWriter writer = null;   
  118.   
  119.         try {   
  120.   
  121.              is = new FileInputStream(filename);   
  122.   
  123.              writer = new FileWriter(filename + ".enc");   
  124.   
  125.         }   
  126.   
  127.         catch (FileNotFoundException e1){   
  128.   
  129.             System.out.println("File not found: " + filename);   
  130.   
  131.         }   
  132.   
  133.         catch (IOException e1){   
  134.   
  135.             System.out.println("File not found: " + filename + ".enc");   
  136.   
  137.         }   
  138.   
  139.           
  140.   
  141.         /**  
  142.  
  143.          * Write encoded message to 'filename'.enc  
  144.  
  145.          */  
  146.   
  147.         try {   
  148.   
  149.             while ((tempLen = is.read(bytes, 1, blockSize / 8)) > 0) {   
  150.   
  151.                 for (int i = tempLen + 1; i < bytes.length; ++i) {   
  152.   
  153.                     bytes[i] = 0;   
  154.   
  155.                 }    
  156.   
  157.                 writer.write(encodeDecode(new BigInteger(bytes)) + " ");   
  158.   
  159.             }   
  160.   
  161.         }   
  162.   
  163.         catch (IOException e1) {   
  164.   
  165.             System.out.println("error writing to file");   
  166.   
  167.         }   
  168.   
  169.           
  170.   
  171.         /**  
  172.  
  173.          * Close input stream and file writer  
  174.  
  175.          */  
  176.   
  177.         try {   
  178.   
  179.             is.close();   
  180.   
  181.             writer.close();   
  182.   
  183.         }   
  184.   
  185.         catch (IOException e1) {   
  186.   
  187.             System.out.println("Error closing file.");   
  188.   
  189.         }   
  190.   
  191.     }   
  192.   
  193.       
  194.   
  195.     public void decodeFile (String filename) {   
  196.   
  197.           
  198.   
  199.         FileReader reader = null;   
  200.   
  201.         OutputStream os = null;   
  202.   
  203.         try {   
  204.   
  205.             reader = new FileReader(filename);   
  206.   
  207.             os = new FileOutputStream(filename.replaceAll(".enc"".dec"));   
  208.   
  209.         }   
  210.   
  211.         catch (FileNotFoundException e1) {   
  212.   
  213.             if (reader == null)   
  214.   
  215.                 System.out.println("File not found: " + filename);   
  216.   
  217.             else  
  218.   
  219.                 System.out.println("File not found: " + filename.replaceAll(".enc""dec"));   
  220.   
  221.         }   
  222.   
  223.           
  224.   
  225.         BufferedReader br = new BufferedReader(reader);   
  226.   
  227.         int offset;   
  228.   
  229.         byte[] temp, toFile;   
  230.   
  231.         StringTokenizer st = null;   
  232.   
  233.         try {   
  234.   
  235.             while (br.ready()) {   
  236.   
  237.                 st = new StringTokenizer(br.readLine());   
  238.   
  239.                 while (st.hasMoreTokens()){   
  240.   
  241.                     toFile = encodeDecode(new BigInteger(st.nextToken())).toByteArray();   
  242.   
  243.                     System.out.println(toFile.length + " x " + (blockSize / 8));   
  244.   
  245.                       
  246.   
  247.                     if (toFile[0] == 0 && toFile.length != (blockSize / 8)) {   
  248.   
  249.                         temp = new byte[blockSize / 8];   
  250.   
  251.                         offset = temp.length - toFile.length;    
  252.   
  253.                         for (int i = toFile.length - 1; (i <= 0) && ((i + offset) <= 0); --i) {   
  254.   
  255.                             temp[i + offset] = toFile[i];   
  256.   
  257.                         }   
  258.   
  259.                         toFile = temp;   
  260.   
  261.                     }   
  262.   
  263.                       
  264.   
  265.                     /*if (toFile.length != ((blockSize / 8) + 1)){  
  266.  
  267.                         temp = new byte[(blockSize / 8) + 1];  
  268.  
  269.                         System.out.println(toFile.length + " x " + temp.length);  
  270.  
  271.                         for (int i = 1; i < temp.length; i++) {  
  272.  
  273.                             temp[i] = toFile[i - 1];  
  274.  
  275.                         }  
  276.  
  277.                         toFile = temp;  
  278.  
  279.                     }  
  280.  
  281.                     else  
  282.  
  283.                         System.out.println(toFile.length + " " + ((blockSize / 8) + 1));*/  
  284.   
  285.                     os.write(toFile);   
  286.   
  287.                 }   
  288.   
  289.             }   
  290.   
  291.         }   
  292.   
  293.         catch (IOException e1) {   
  294.   
  295.             System.out.println("Something went wrong");   
  296.   
  297.         }   
  298.   
  299.           
  300.   
  301.         /**  
  302.  
  303.          * close data streams  
  304.  
  305.          */  
  306.   
  307.         try {   
  308.   
  309.             os.close();   
  310.   
  311.             reader.close();   
  312.   
  313.         }   
  314.   
  315.         catch (IOException e1) {   
  316.   
  317.             System.out.println("Error closing file.");   
  318.   
  319.         }   
  320.   
  321.     }   
  322.   
  323.       
  324.   
  325.     /**  
  326.  
  327.      * Performs <tt>base</tt>^<sup><tt>pow</tt></sup> within the modular  
  328.  
  329.      * domain of <tt>mod</tt>.  
  330.  
  331.      *  
  332.  
  333.      * @param base the base to be raised  
  334.  
  335.      * @param pow the power to which the base will be raisded  
  336.  
  337.      * @param mod the modular domain over which to perform this operation  
  338.  
  339.      * @return <tt>base</tt>^<sup><tt>pow</tt></sup> within the modular  
  340.  
  341.      * domain of <tt>mod</tt>.  
  342.  
  343.      */  
  344.   
  345.     public BigInteger encodeDecode(BigInteger base) {   
  346.   
  347.         BigInteger a = ONE;   
  348.   
  349.         BigInteger s = base;   
  350.   
  351.         BigInteger n = myKey;   
  352.   
  353.           
  354.   
  355.         while (!n.equals(ZERO)) {   
  356.   
  357.             if(!n.mod(TWO).equals(ZERO))   
  358.   
  359.                 a = a.multiply(s).mod(myMod);   
  360.   
  361.               
  362.   
  363.             s = s.pow(2).mod(myMod);   
  364.   
  365.             n = n.divide(TWO);   
  366.   
  367.         }   
  368.   
  369.           
  370.   
  371.         return a;   
  372.   
  373.     }      
  374.   
  375. }   
  376.   

 

在这里提供两个版本的RSA算法JAVA实现的代码下载:

1. 来自于 http://www.javafr.com/code.aspx?ID=27020 的RSA算法实现源代码包:
        JavaFR_RSA_Source.rar 

2. 来自于 http://www.ferrara.linux.it/Members/lucabariani/RSA/implementazioneRsa/ 的实现:
        sorgentiJava.tar.gz  - 源代码包
        algoritmoRSA.jar - 编译好的jar包

另外关于RSA算法的php实现请参见文章: php下的RSA算法实现

关于使用VB实现RSA算法的源代码下载(此程序采用了psc1算法来实现快速的RSA加密): 
        vb_PSC1_RSA.rar

RSA加密的JavaScript实现: http://www.ohdave.com/rsa/

分享到:
评论

相关推荐

    1.金融风控知识与架构介绍.ipynb

    1.金融风控知识与架构介绍.ipynb

    基于Plecs仿真的全桥PSFB+ZVS技术参数研究与应用案例 电力电子

    内容概要:本文详细探讨了基于Plecs仿真的全桥移相全桥变换器(PSFB)结合副边同步整流(ZVS)的技术参数研究及其应用案例。文章首先介绍了系统的配置,重点在于确保驱动时序对齐以及优化移相角来实现ZVS。接着讨论了同步整流的关键点,即精确控制副边开关管的驱动时序以避免反向导通的问题。随后提供了参数调整的具体方法,如改变变压器匝比、计算合适的电感值以及选择适当的散热材料。最后分享了一些常见的仿真问题解决方案,比如处理震荡尖峰和轻载情况下的ZVS失效。同时推荐了几款实用的仿真工具和相关文献供进一步学习。 适合人群:从事电力电子设计的专业人士,特别是那些希望深入了解PSFB和ZVS技术的研究人员和技术工程师。 使用场景及目标:适用于需要进行高效电源转换设计的场合,旨在帮助读者掌握PSFB和ZVS的工作原理,提高电路性能,降低损耗并优化系统参数。 其他说明:文中提供的代码片段和参数设定均为实际仿真过程中积累的经验,对于初学者来说是非常宝贵的参考资料。此外,还提到了一些常见错误及其解决办法,有助于减少开发过程中的障碍。

    基于指标预测与对比的我国“双一流”高校专利转化潜力研究.zip

    基于指标预测与对比的我国“双一流”高校专利转化潜力研究.zip

    插秧机系统设计.rar

    插秧机系统设计.rar

    二轴五档变速器设计.rar

    二轴五档变速器设计.rar

    Lq2.shx

    拷贝到Auto CAD的Fonts下

    风扇叶片注射模具设计.rar

    风扇叶片注射模具设计.rar

    CQRN-RIS.pdf

    CQRN-RIS

    使用Java自带的界面开发包完成一个简易的登陆注册界面

    使用Java自带的界面开发包完成一个简易的登陆注册界面

    锤式破碎机.rar

    锤式破碎机.rar

    2024工业互联网技术产业创新报告.pdf

    2024工业互联网技术产业创新报告.pdf

    易优我的足迹插件.zip

    易优我的足迹插件.zip

    四旋翼无人机UAV轨迹跟踪PID控制仿真:位置与姿态控制的对比分析及应用 Matlab

    内容概要:本文详细介绍了四旋翼无人机(UAV)轨迹跟踪的PID控制仿真方法及其应用。首先解释了PID控制的基本原理,即通过比例、积分和微分三项参数调整控制量,确保无人机能够精确沿预定路径飞行。接着讨论了无人机的动力学模型,涵盖位置和姿态控制的具体公式。随后,文中展示了如何利用MATLAB进行仿真,包括PID参数的设置与调整,以及具体的代码实现。最后,通过位置三维图像、姿态角度图像和位置误差分析图,直观展现了PID控制的效果。 适合人群:对无人机控制技术和PID控制感兴趣的科研人员、工程师和技术爱好者。 使用场景及目标:适用于希望深入了解无人机飞行控制机制的研究者,特别是那些想通过仿真手段测试不同控制算法效果的人群。目标是提高无人机的飞行稳定性和准确性。 其他说明:文章不仅提供了理论知识,还附有详细的代码示例和仿真结果,便于读者理解和实践。此外,文章还展望了未来可能采用的改进型控制算法,如模糊PID、滑模控制和基于模型预测控制(MPC),为后续研究指明方向。

    X62W万能铣床PLC电器改造设计.rar

    X62W万能铣床PLC电器改造设计.rar

    基于多智能体系统一致性算法的电力系统分布式经济调度策略复现及其实验分析 一致性算法

    内容概要:本文详细介绍了基于多智能体系统一致性算法的电力系统分布式经济调度策略的复现过程。文中首先介绍了电力系统经济调度的重要性和挑战,然后构建了一个由10个发电单元和19个柔性负荷单元组成的多智能体系统模型。接着,通过初始化智能体状态、定义信息交换协议以及应用一致性算法,逐步实现了分布式经济调度。最后,通过Python代码展示了算法的具体实现,并对实验结果进行了详细的分析,验证了该策略的有效性。 适合人群:从事电力系统研究、分布式控制系统开发的研究人员和技术人员,尤其是对多智能体系统和一致性算法感兴趣的学者。 使用场景及目标:适用于电力系统分布式经济调度的研究和开发项目,旨在提高电力系统的效率和稳定性,确保总功率输出能满足总负荷需求并实现经济调度。 其他说明:本文不仅提供了理论分析,还包括了具体的代码实现和实验数据,有助于读者深入理解和应用该策略。同时,也为未来的研究提供了宝贵的参考和改进方向。

    T68镗床的控制系统的改造设计 电气控制系统PLC改造设计.rar

    T68镗床的控制系统的改造设计 电气控制系统PLC改造设计.rar

    毕业设计-万能表单weui 8.1.16-整站商业源码.zip

    毕业设计-万能表单weui 8.1.16-整站商业源码.zip

    2024年Google数据和AI趋势报告.pdf

    2024年Google数据和AI趋势报告.pdf

    mysql安装配置教程.docx

    mysql安装配置教程

    干粉压片机设计.rar

    干粉压片机设计.rar

Global site tag (gtag.js) - Google Analytics