博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#与Java的RSA(1)
阅读量:7223 次
发布时间:2019-06-29

本文共 4061 字,大约阅读时间需要 13 分钟。

原创文章,转载请注明出处 。


前段时间的一个项目,涉及到在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#的实现,

3、自己分析

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

未完待续……

     本文转自 BoyTNT 51CTO博客,原文链接:
http://blog.51cto.com/boytnt/1350441,如需转载请自行联系原作者
你可能感兴趣的文章
C语言之流程控制
查看>>
ElasticSearch ik,elasticsearch-jdbc 使用 和 yii2 实例
查看>>
RPA视频教程丨UiBot—Function函数详解
查看>>
Getinstall如何解决下载一app 点击浏览器就变成一二微码
查看>>
我的友情链接
查看>>
Windows Server 2008 RemoteApp(五)---远程桌面Web访问
查看>>
linux C函数之strdup函数分析
查看>>
GHOST系统部分报错解决方法
查看>>
我的友情链接
查看>>
数据库与java程序数据传递过程中日期类型的转换
查看>>
改掉阻碍晋升的7个坏毛病 走通职场晋升路
查看>>
Hyper-V——snapshot虚拟机快照
查看>>
Android实现弹出Toast提示
查看>>
Ubuntu12.04平台安装cacti全过程(二)
查看>>
存储过程的具体操作
查看>>
CentOS7修改网卡名称
查看>>
C++拓展笔记1-3:浅析C++关键字const的几个作用
查看>>
免费的编程中文书籍索引
查看>>
Linux DNS服务实验报告
查看>>
浅析python 中__name__ = '__main__' 的作用
查看>>