体验.NET Core使用IKVM对接Java
前言
与第三方对接最麻烦的是语言不同,因语言不同内置实现相关标准加密算法还是略微有所差异,对接单点登录场景再寻常不过,由于时间紧迫且对接方使用Java,所以留给我对接开发和联调的时间本就不多,于是乎,在熬夜发版后,继而开始提前研究对接方所提供的加密方式大致处理
方案一(C#实现)
数据对接加密算法采用RSA SHA1 1024位、同时呢,在Java中对于1024或其他位数,对密文有长度限制,所以利用了分段加密,密文长度为117,解密长度为128,如此通用处理方式,网上肯定是可以搜索到的,截取加密部分片段,如下:
public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicK = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicK);
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
while (inputLen - offSet > 0) {
if (inputLen - offSet > 117) {
cache = cipher.doFinal(data, offSet, 117);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * 117;
}
byte[] encryptedData = out.toByteArray();
out.close();
return encryptedData;
}
当然对于密钥在从证书中导出来肯定是二进制的,那么各个对接方最后转换为字符串方式有多种,比如base64、再比如转换为16进制等等,大同小异,这里就不展开了
了解完Java完整代码实现,我们需要对相关业务参数利用对接方所提供的公钥进行加密从而形成签名,以此请求时,将我们本地生成的公钥传递过去,在响应后进行验签,然后通过私钥解密
好了,讲到这里,我们假设已经本地生成证书,然后我们导出RSA公钥和私钥,伪代码如下:
var certificate = new X509Certificate2("pfx_path", "password", X509KeyStorageFlags.Exportable);
var rsa = certificate.GetRSAPrivateKey();
var privateKey = rsa.ExportRSAPrivateKey();
var publicKey = rsa.ExportRSAPublicKey();
那么我们如何利用对接方公钥进行加密呢?接下来在.NET Core中实现就需要解决上述加密算法出现的两个问题
1. Java中可以通过RSA密钥(公钥或私钥)字符串转换为RSA密钥类,.NET Core提供了?
2. 获取到RSA密钥类,调用对应实例doFinal方法进行分段加密,那么是否可以通过.NET Core中的RSA加密方法,也分段加密,结果是否一致?对接方通过公钥字符串加载RSA 公钥,代码如下:
public static RSAPublicKey loadPublicKeyByStr(String publicKeyStr) {
byte[] buffer = Util.hexToByte(publicKeyStr);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
}
然后稍微查看了下加载类的解释

所以我依图索骥,于是乎,大致有了如下模样
var servicePublicKey = "123456"; var rsa = RSA.Create(); rsa.ImportSubjectPublicKeyInfo(Encoding.UTF8.GetBytes(servicePublicKey), out _);
好了,目前通过导入公钥主题信息貌似拿到了RSA,接下来则是分段加密,上述方法中cipher.doFinal,应该是指定算法实例的加密处理,那我们首先截取对应数据的分段,然后调用rsa的Encrypt方法,那加密填充模式是否一致,不得而知,默认如下提供为PKCS1?
var servicePublicKey = "123456";
var plainTextData = Encoding.UTF8.GetBytes("jeffcky");
var rsa = RSA.Create();
rsa.ImportSubjectPublicKeyInfo(Encoding.UTF8.GetBytes(servicePublicKey), out _);
var outputStream = new MemoryStream();
var inputLen = plainTextData.Length;
int offSet = 0;
int i = 0;
while (inputLen - offSet > 0)
{
Span<byte> bytes = plainTextData;
byte[] encryptData;
if (inputLen - offSet > 117)
{
Span<byte> slicedBytes = bytes.Slice(start: offSet, length: 117);
encryptData = rsa.Encrypt(slicedBytes.ToArray(), RSAEncryptionPadding.Pkcs1);
}
else
{
Span<byte> slicedBytes = bytes.Slice(start: offSet, length: inputLen - offSet);
encryptData = rsa.Encrypt(slicedBytes.ToArray(), RSAEncryptionPadding.Pkcs1);
}
outputStream.Write(encryptData, 0, encryptData.Length);
i++;
offSet = i * 117;
}
一顿不知其结果的操作后,经调用对接方接口,响应验签失败,反复改了几版后,依然失败
方案二(IKVM)
不再过多深究其细节实现差异,采用稳妥方式借用IKVM直接在C#中复制对接方Java代码应该可以搞定(https://github.com/ikvm-revived/ikvm)
根据经验来看,我只用到加密,应该只需要用到IKVM.OpenJDK.Core和IKVM.OpenJDK.Security两个库,于是我们在nuget上下载.NET Framework实现版本,我使用的是.NET Framework 4.7.2,想想.NET Framework 4.6+可以适配.NET Standard,那是否也可以经过编译后,通过.NET Core添加程序集引用呢?
在.NET Framework 4.7.2引入上述核心库后,在控制台测试验证加密、验签一点问题都么有,好了,接下来则是将其编译,在.NET Core 3.1中添加程序集进行调用,加载程序集方法时直接抛出大致如下异常
FileNotFoundException: Could not load file or assembly 'System.Configuration.ConfigurationManager, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.
继续添加对应版本程序集,版本过低,在nuget上根本找不到,涉及到.NET Core基础设施,看是否在github runtimes仓库能否找到对应issue

解决了上述程序集问题,接下来再次运行,又来其他异常,.NET Core版本IO加载文件该方法已被剔除

坑一波接着一波,看来使用.NET Core引用.NET Framework程序集此路行不通,而且这仅仅只是在windows下测试,发布到linux上出现的问题无法预知,我再次翻了下,居然发现了.NET Core分支,惊喜不惊喜,查看分支就可以知道已有老外在处理迁移到.NET Core版本,只是还处于未发布状态

fork原.NET Framework版本迁移至适配.NET Core链接(https://github.com/ams-ts-ikvm/ikvm-bin/tree/net_core_compat/bin),如果只是用到相关加密,只需引用上述链接bin目录下,如下库即可

最后,还需要添加上述在.NET Framework中测试中出现异常的包以及版本(System.Configuration.ConfigurationManager),如下:

经过上述一番折腾过程,终于对接上,耗时一天多,真尼玛是不容易,搞完一个字,累的一p
总结
使用还未发布适配.NET Core的IKVM,在仅有时间内,快速对接上了Java加密,如若后续再遇到类似比较耗时耗力的加密方式,还不如使用IKVM,将更多时间花在处理业务逻辑上才是正道
体验.NET Core使用IKVM对接Java的更多相关文章
- IKVM:java代码c#调用
在工作中遇到对接java接口,涉及到java加密或签名问题,.net无法实.就将java代码编辑为dll给.net调用 注:这里只做简单java代码处理,不涉及到复杂的java包 java文件处理: ...
- 在docker中初次体验.net core 2.0
.net core的跨平台有了Linux,不能没有docker……网上的系列文章一大推,特别是docker还有了中文官网:https://www.docker-cn.com/ .上面说的很清楚了,这里 ...
- 在ubuntu16.04中再次体验.net core 2.0
在上一篇文章中在ubuntu16.04中初次体验.net core 2.0 简单介绍了一下ubuntu中运行.net core 2.0.配置nginx反向代理以及安装supervisor守护进程……本 ...
- mybatis的判定时间字段问题 java.lang.IllegalArgumentException: invalid comparison: cn.hutool.core.date.DateTime and java.lang.String
今天听组员说: mybatis在3.30版本及以上判定时间时 <if test="date_time != null and date_time != '' "> da ...
- .NET 对接JAVA 使用Modulus,Exponent RSA 加密
最近有一个工作是需要把数据用RSA发送给Java 虽然一开始标准公钥 net和Java RSA填充的一些算法不一样 但是后来这个坑也补的差不多了 具体可以参考 http://www.cnblogs. ...
- 在ubuntu16.04中初次体验.net core 2.0
.net core运行在Linux中的例子.文章已经很多了,看了一些之后也想体验一下,顺便记录一下…… 环境:win10 1709.它内置的Linux子系统(这里安装的是Ubuntu 16.04) 一 ...
- 不简单的工厂:实际体验 .NET Core 2.1 新生物 HttpClientFactory
在 HttpClientFactory 出生之前,由于 HttpClient 臭名昭著的“dispose之后4分钟TCP连接才会被关闭”问题(详情),只能使用单例或静态的 HttpClient ,比如 ...
- C#对接JAVA系统遇到的AES加密坑
起因对接合作伙伴的系统,需要对数据进行AES加密 默认的使用了已经写好的帮助类中加密算法,发现结果不对,各种尝试改变加密模式改变向量等等折腾快一下午.最后网上查了下AES在JAVA里面的实现完整代码如 ...
- PHP DES-ECB加密对接Java解密
最近公司有个业务,需要对接第三方接口,但是参数是需要加密的,对方也只提供了一个java的demo,在网上到处搜索,没有找到直接就能用的方法,后来还是跟公司的Android工程师对接出来的,在这里记录一 ...
随机推荐
- Mysql集群搭建(多实例、主从)
1 MySQL多实例 一 .MySQL多实例介绍 1.什么是MySQL多实例 MySQL多实例就是在一台机器上开启多个不同的服务端口(如:3306,3307,3308),运行多个MySQL服务进程,通 ...
- 在CentOS 6中安装和配置OrientDB社区版
OrientDB概述: OrientDB是一个开源NoSQL非关系型数据库管理系统. NoSQL数据库提供了一种用于存储和检索引用除表式数据之外的数据(例如文档数据或图形数据)的NO关系或非关系数据的 ...
- github注册教程最新版(十年程序员保姆级教程)
您可以在墨抒颖的网站体验本文章的纯净版 准备 拥有一个可以接受信息的邮箱即可 开始 点击github官网github step1.进入注册页面 点击Sign Up进入注册流程 step2.输入邮箱 这 ...
- html行内元素
定义 行内元素只占据它对应标签的边框所包含的空间,没有换行效果 div{ /* 定义行内元素*/ display:inline } 特点 多个元素可以横排显示 不支持宽高和上下margin 支持pad ...
- MacOS下Java与JDK关系与相关路径
MacOS下Java与JDK关系与相关路径 macOS下的Java与JDK的路径曾经困扰过我一段时间,今天稍有些忘记,故记下笔记,整理一下.Java与JDK的关系不在本文笔记之内,Javaer常识. ...
- res目录下的结构
目录 res目录下的结构 drawable开头的文件夹 mipmap开头的文件夹 values开头的文件夹 layout文件夹 使用res目录下的资源 res目录下的结构 如果你展开res目录看一下, ...
- 搭载Dubbo+Zookeeper踩了这么多坑,我终于决定写下这篇!
大家好,我是melo,一名大二上软件工程在读生,经历了一年的摸滚,现在已经在工作室里边准备开发后台项目啦. 这篇文章我们不谈数据结构了,来谈谈入门分布式踩过的坑.感觉到了分布式这一层,由于技术更新迭代 ...
- 分布式全局ID与分布式事务
1. 概述 老话说的好:人不可貌相,海水不可斗量.以貌取人是非常不好的,我们要平等的对待每一个人. 言归正传,今天我们来聊一下分布式全局 ID 与分布式事务. 2. 分布式全局ID 2.1 分布式数据 ...
- STM32采集AD的输入阻抗问题
在做一款消费电子产品时,需要采集电池电压(3.3V-4.2V),同时在休眠的时候希望尽量减小待机电流.电池电压采集电路采用两个1%的300K电阻进行分压,由该电路引起的待机电路为4.2/(300+30 ...
- DDD领域驱动设计-项目包结构说明-Ⅳ
基于DDD领域驱动设计的思想,在开发具体系统时,需要先建立不同的层级包.主要是梳理不同层面(应用层,领域层,基础设施层,展示层)包括的功能目录,每一个层面应该包括哪些模块.本例所讲述的分层是DDD落 ...