C#与Java的RSA(1)

简介:

原创文章,转载请注明出处 http://boytnt.blog.51cto.com/966121/1350441


前段时间的一个项目,涉及到在C#的项目中调用外部Java系统的接口,其中的登录功能要求C#端先与Java端协商RSA公钥,然后用公钥加密密码提交给Java端进行验证。Java端使用的是2048位的标准RSA加密,给出的公钥是一个HEX字符串,如:

30820122300D06092A864886F70D01010105000382010F003082010A02820101008C214751E6EA33378080F64BF55C0888D3EFA4DF08794318069DDFD14A3AB6468B20CD134819100FA20539785AECF595CF2333F7ADC48366F4ACBC41B1CED728B57417CF3B6CA4E7DDB9DA348F7D38158DD6F2FF3934AEB0A70732E2949505EF893A940404B1B5F4B69243E2877BBA90E5994EBFD61986F412DE4AD3E8331CE1D3D41ADAEF5C79D5B22E05C7F76FC748BC5FA42345D70EC3D1DE3DBD338C300C3750841E2E16E7B907E536FCA1A40D05DC9DFCDE4EB2E8575228309AD146486E6F21C386E90C36DEECB57F955CE68609204AFBD434F8A1BFB5D921C470EED82CCA8BFDA92A8EC668E9E9EB6F959CD535C8BCCFCB08A671983A27E8B03F5BF90D0203010001


RSA本身的算法不复杂,相信不少人都在项目中做过加解密,不同语言都封装得挺好,可是一但跨语言,问题就来了。正好借着这个项目,深入研究了一下。


首先看看这个公钥是怎么来的。在Java中,一般这样产生密钥对并且编码输出公钥:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*********** Java代码 ***********/
         
//初始化2048位的RSA密钥生成器
KeyPairGenerator generator = KeyPairGenerator.getInstance( "RSA" );
generator.initialize( 2048 );
         
//生成密钥对,并得到公钥与私钥
KeyPair keys = generator.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey)keys.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey)keys.getPrivate();
         
//编码,并转化成HEX形式的字符串
String publicKeyHex = bytes2hex(publicKey.getEncoded());
                                                                                                                    
System.out.println(publicKeyHex);


本文一开头那个3082开头的大字符串就是这么产生的。需要利用公钥加密时,在Java里一般这么做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*********** Java代码 ***********/
         
//还原公钥
KeyFactory factory = KeyFactory.getInstance( "RSA" );
X509EncodedKeySpec spec =  new  X509EncodedKeySpec(hex2byte(publicKeyHex));
PublicKey publicKey = factory.generatePublic(spec);
         
//RSA公钥加密(无填充模式)
Cipher cipher = Cipher.getInstance( "RSA/ECB/NoPadding" );
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte [] result = cipher.doFinal( "PASSWORD" .getBytes( "UTF-8" ));
             
//密文转化成HEX字符串
String resultHex = byte2hex(result);


上面2段代码涉及到byte[]和HEX字符串的相互转化,顺便也放一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*********** Java代码 ***********/
     
private  byte [] hex2bytes(String hex) {
     String digital =  "0123456789ABCDEF" ;
     char [] hex2char = hex.toCharArray();
     byte [] bytes =  new  byte [hex.length() /  2 ];
     int  temp;
     for  ( int  i =  0 ; i < bytes.length; i++) {
         temp = digital.indexOf(hex2char[ 2  * i]) *  16 ;
         temp += digital.indexOf(hex2char[ 2  * i +  1 ]);
         bytes[i] = ( byte ) (temp &  0xff );
     }
               
     return  bytes;
}
         
private  String bytes2hex( byte [] b) {
     String hs =  "" ;
     String stmp =  "" ;
     for  ( int  n =  0 ; n < b.length; n++) {
         stmp = (java.lang.Integer.toHexString(b[n] &  0xFF ));
         if  (stmp.length() ==  1 )
             hs = hs +  "0"  + stmp;
         else
             hs = hs + stmp;
     }
         
     return  hs.toUpperCase();
}


我相信绝大多数Java程序员从来没想过getEncoded()是什么算法,它是如何把modulus与publicExponent封装到一个byte[]里去的。而对于C#程序员来讲,RSA公钥加密必须要用到modulus与publicExponent,无论它们是XML形式还是byte[]形式,因此如何从publicKeyHex中解析这两个参数就成了第一个关键点。


我们看看JavaDoc上是怎么说的。

An Encoded Form

This is an external encoded form for the key used when a standard representation of the key is needed outside

the Java Virtual Machine, as when transmitting the key to some other party. The key is encoded according to

a standard format (such as X.509 SubjectPublicKeyInfo or PKCS#8), and is returned using the getEncoded method.

Note: The syntax of the ASN.1 type SubjectPublicKeyInfo is defined as follows:


SubjectPublicKeyInfo ::= SEQUENCE {

  algorithm AlgorithmIdentifier,

  subjectPublicKey BIT STRING }


AlgorithmIdentifier ::= SEQUENCE {

  algorithm OBJECT IDENTIFIER,

  parameters ANY DEFINED BY algorithm OPTIONAL }


For more information, see RFC 3280: Internet X.509 Public Key Infrastructure Certificate and CRL Profile.


根据ASN.1标准进行保存的,涉及到了2种格式:X.509 SubjectPublicKeyInfo和PKCS#8, 具体是哪种可以从getFormat()的方法说明里可以找到答案:公钥使用的是X.509 SubjectPublicKeyInfo,私钥使用的是PKCS#8。


我们只关心公钥,OK,下一步来看看如何从中解出我们需要的modulus和publicExponent。很遗憾的说一句,.NetFramework中没有现成的类来处理ASN.1和SubjectPublicKeyInfo,想获取数据有这么几个方法:

1、利用Win32API,引入非托管代码crypt32.dll,调用CryptDecodeObject()方法来解码

2、利用第三方的类库,比如BouncyCastle,它有C#的实现,http://www.bouncycastle.org/csharp

3、自己分析


个人比较推荐BouncyCastle,不过本文打算“深入”研究一下,所以准备走第3条路。


未完待续……






     本文转自 BoyTNT 51CTO博客,原文链接: http://blog.51cto.com/boytnt/1350441,如需转载请自行联系原作者
相关文章
|
3月前
|
算法 安全 Java
Java 实现 RSA 非对称加密算法-加解密和签名验签
Java 实现 RSA 非对称加密算法-加解密和签名验签
|
6月前
|
开发框架 Oracle Java
2023年10月TIOBE排行榜发布:C#逼近Java
2023年10月TIOBE排行榜发布:C#逼近Java
138 0
|
6月前
|
存储 Java C#
c#快速入门~在java基础上,知道C#和JAVA 的不同即可
c#快速入门~在java基础上,知道C#和JAVA 的不同即可
87 0
|
5月前
|
人工智能 搜索推荐 Java
C# 最有可能摘得“年度编程语言”,Python、Java 正在让位,TIOBE 12 月编程语言榜单最新预测来了!
C# 最有可能摘得“年度编程语言”,Python、Java 正在让位,TIOBE 12 月编程语言榜单最新预测来了!
32 1
|
10月前
|
算法 安全 Java
【算法】Java实现RSA算法
RSA(Rivest-Shamir-Adleman)是一种非对称加密算法,它是目前最广泛使用的公钥加密算法之一。RSA算法是由三位密码学家(Ron Rivest、Adi Shamir、Leonard Adleman)在1977年提出的。
301 0
|
5月前
|
XML 算法 安全
C# | 上位机开发新手指南(九)加密算法——RSA
RSA的特性 非对称性 RSA算法使用公钥和私钥两个不同的密钥,公钥用于加密数据,私钥用于解密数据。公钥可以公开,任何人都可以使用,而私钥只有密钥持有人可以访问。 安全性 RSA算法基于大数分解难题,即将一个大的合数分解成其质数因子的乘积。由于目前没有有效的算法可以在合理的时间内对大质数进行分解,因此RSA算法被认为是一种安全的加密算法。 可逆性 RSA算法既可以用于加密,也可以用于解密。加密和解密都是可逆的过程,只要使用正确的密钥,就可以还原原始数据。 签名 RSA算法可以用于数字签名,用于验证数据的完整性和真实性。签名过程是将数据使用私钥进行加密,验证过程是将签名使用公钥进行解密。
109 0
C# | 上位机开发新手指南(九)加密算法——RSA
|
5月前
|
算法 Java 数据安全/隐私保护
RSA - 非对称加密算法简要介绍与JAVA实现
RSA - 非对称加密算法简要介绍与JAVA实现
26 0
|
5月前
|
算法 安全 Java
详解RSA加密算法 | Java模拟实现RSA算法
详解RSA加密算法 | Java模拟实现RSA算法
112 1
|
6月前
|
开发框架 JavaScript Java
推荐一个日历转换开源工具库,支持C#、Java、PHP等主流的语言
推荐一个日历转换开源工具库,支持C#、Java、PHP等主流的语言
47 0
|
7月前
|
SQL Rust Java
C# 逼近 Java
C# 是一个严重被低估的全能型编程语言,C# 和 Java 之间的差距越来越小,达到了历史新低,目前两者的差距仅为 1.2%。TIOBE CEO Paul Jansen 认为,如果这一趋势继续保持,那么 C# 将在两个月后超过 Java。
234 2