前言

与第三方对接最麻烦的是语言不同,因语言不同内置实现相关标准加密算法还是略微有所差异,对接单点登录场景再寻常不过,由于时间紧迫且对接方使用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的更多相关文章

  1. IKVM:java代码c#调用

    在工作中遇到对接java接口,涉及到java加密或签名问题,.net无法实.就将java代码编辑为dll给.net调用 注:这里只做简单java代码处理,不涉及到复杂的java包 java文件处理: ...

  2. 在docker中初次体验.net core 2.0

    .net core的跨平台有了Linux,不能没有docker……网上的系列文章一大推,特别是docker还有了中文官网:https://www.docker-cn.com/ .上面说的很清楚了,这里 ...

  3. 在ubuntu16.04中再次体验.net core 2.0

    在上一篇文章中在ubuntu16.04中初次体验.net core 2.0 简单介绍了一下ubuntu中运行.net core 2.0.配置nginx反向代理以及安装supervisor守护进程……本 ...

  4. 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 ...

  5. .NET 对接JAVA 使用Modulus,Exponent RSA 加密

    最近有一个工作是需要把数据用RSA发送给Java 虽然一开始标准公钥 net和Java  RSA填充的一些算法不一样 但是后来这个坑也补的差不多了 具体可以参考 http://www.cnblogs. ...

  6. 在ubuntu16.04中初次体验.net core 2.0

    .net core运行在Linux中的例子.文章已经很多了,看了一些之后也想体验一下,顺便记录一下…… 环境:win10 1709.它内置的Linux子系统(这里安装的是Ubuntu 16.04) 一 ...

  7. 不简单的工厂:实际体验 .NET Core 2.1 新生物 HttpClientFactory

    在 HttpClientFactory 出生之前,由于 HttpClient 臭名昭著的“dispose之后4分钟TCP连接才会被关闭”问题(详情),只能使用单例或静态的 HttpClient ,比如 ...

  8. C#对接JAVA系统遇到的AES加密坑

    起因对接合作伙伴的系统,需要对数据进行AES加密 默认的使用了已经写好的帮助类中加密算法,发现结果不对,各种尝试改变加密模式改变向量等等折腾快一下午.最后网上查了下AES在JAVA里面的实现完整代码如 ...

  9. PHP DES-ECB加密对接Java解密

    最近公司有个业务,需要对接第三方接口,但是参数是需要加密的,对方也只提供了一个java的demo,在网上到处搜索,没有找到直接就能用的方法,后来还是跟公司的Android工程师对接出来的,在这里记录一 ...

随机推荐

  1. 关于Postman你必须学会的技能

    关于Postman 工欲善其事,必先利其器,在了解了接口测试之后,就要选择一款适用的工具.之所以选择postman是因为它简单.容易上手.能覆盖大多数HTTP接口测试场景,性价比极高. Postman ...

  2. Google Chrome打开权限设置开关(摄像头,录音等)

    在搜索框输入以下字符 chrome://flags/#unsafely-treat-insecure-origin-as-secure

  3. JavaWeb#JSP内置对象

    [1.JSP内置对象简介] 内置对象:不加声明就可以在JSP页面脚本中使用的成员变量.(使用这些对象可以更容易收集客户端发送的请求信息,响应客户端的请求及存储客户信息.) 主要介绍:out,reque ...

  4. 数据库MySQL主从-GTID

    1.第一步在主服务器上/etc/my.cnf/下添加 log-bin=log-bin server-id=1 gtid_mode=ON enforce_gtid_consistency 2.第二步:重 ...

  5. Lamport时间戳论文笔记

    本文主要参考文献[1]完成. 声明:本人仅在博客园发表了本文章,笔名LightningStar,其他网站均为转载. 笔记 私以为,论文中作者的核心工作是为分布式系统建立了一种数学模型,并基于这种数学模 ...

  6. MySQL的详细讲解

    目录 Mysql的架构与历史 MySQL的逻辑架构 更新中---- Mysql的架构与历史 MySQL的逻辑架构 第二层的架构是所有的跨引擎的功能实现的地方,例如:存储,触发器,视图等. 第三层半酣了 ...

  7. 洛谷3163 CQOI2014危桥 (最大流)

    一开始想了一发费用流做法然后直接出负环了 首先,比较显然的思路就是对于原图中没有限制的边,对应的流量就是\(inf\),如果是危桥,那么流量就应该是\(2\). 由于存在两个起始点,我们考虑直接\(s ...

  8. perl打开读取文件(open)

    在Perl中可以用open或者sysopen函数来打开文件进行操作,这两个函数都需要通过一个文件句柄(即文件指针)来对文件进行读写定位等操作.下面以open函数为例:1:读:open(文件句柄,&qu ...

  9. 通过简单例子 | 快速理清 UML 中类与类的六大关系

    关于封面:我想我们都会离开 类与类之间的六大关系 泛化 ( Generalization ) ---> 表继承关系 实现 ( Realization ) 关联 ( Association ) 聚 ...

  10. Linux基础是零基础必须要过的关,你懂了多少

    #LINUX基础学习 ##命令行下的基础知识 Linux区分英文的大小写. date :查看时间 cal:查看日历 [Tab] 热键 :可以自动补全命令名和文件名 [Ctrl]+C 热键 :可以中断正 ...