.NET Core加解密实战系列之——RSA非对称加密算法
简介
加解密现状,编写此项目的背景:
需要考虑系统环境兼容性问题(Linux、Windows)
语言互通问题(如C#、Java)
网上资料版本不一、不全面
.NET官方库密码算法提供不全面,很难针对其他语言(Java)进行适配
本系列文章主要介绍如何结合BouncyCastle在 .NET Core 中使用非对称加密算法、编码算法、哈希算法、对称加密算法、国密算法等一系列算法,内容篇幅代码居多(加解密算法相关的原理知识网上有很多,因此不过多介绍)。如有错误之处,还请大家批评指正。
本系列代码项目地址:https://github.com/fuluteam/ICH.BouncyCastle.git
功能依赖
BouncyCastle(https://www.bouncycastle.org/csharp) 是一个开放源码的轻量级密码术包;它支持大量的密码术算法,它提供了很多.NET Core标准库没有的算法。
支持.NET 4,.NET Standard 1.0-2.0,WP,Silverlight,MonoAndroid,Xamarin.iOS,.NET Core
功能 | 依赖 |
Portable.BouncyCastle | Portable.BouncyCastle • 1.8.5 |
生成RSA秘钥
PKCS1格式
/// <summary>/// PKCS1(非Java适用)/// </summary>/// <param name="keySize">密钥长度”一般只是指模值的位长度。目前主流可选值:1024、2048、3072、4096...</param>/// <param name="format">PEM格式</param>/// <returns></returns>public RSAKeyParameter Pkcs1(int keySize, bool format=false){var keyGenerator = GeneratorUtilities.GetKeyPairGenerator("RSA");keyGenerator.Init(new KeyGenerationParameters(new SecureRandom(), keySize));var keyPair = keyGenerator.GenerateKeyPair();var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);if (!format){return new RSAKeyParameter{PrivateKey = Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded()),PublicKey = Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded())};}var rsaKey = new RSAKeyParameter();using (var sw = new StringWriter()){var pWrt = new PemWriter(sw);pWrt.WriteObject(keyPair.Private);pWrt.Writer.Close();rsaKey.PrivateKey = sw.ToString();}using (var sw = new StringWriter()){var pWrt = new PemWriter(sw);pWrt.WriteObject(keyPair.Public);pWrt.Writer.Close();rsaKey.PublicKey = sw.ToString();}return rsaKey;}
PKCS8格式
/// <summary>/// PKCS8(JAVA适用)/// </summary>/// <param name="keySize">密钥长度”一般只是指模值的位长度。目前主流可选值:1024、2048、3072、4096...</param>/// <param name="format">PEM格式</param>/// <returns></returns>public RSAKeyParameter Pkcs8(int keySize, bool format=false){var keyGenerator = GeneratorUtilities.GetKeyPairGenerator("RSA");keyGenerator.Init(new KeyGenerationParameters(new SecureRandom(), keySize));var keyPair = keyGenerator.GenerateKeyPair();var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);if (!format){return new RSAKeyParameter{PrivateKey = Base64.ToBase64String(privateKeyInfo.GetEncoded()),PublicKey = Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded())};}var rsaKey = new RSAKeyParameter();using (var sw = new StringWriter()){var pWrt = new PemWriter(sw);var pkcs8 = new Pkcs8Generator(keyPair.Private);pWrt.WriteObject(pkcs8);pWrt.Writer.Close();rsaKey.PrivateKey = sw.ToString();}using (var sw = new StringWriter()){var pWrt = new PemWriter(sw);pWrt.WriteObject(keyPair.Public);pWrt.Writer.Close();rsaKey.PublicKey = sw.ToString();}return rsaKey;}
私钥操作
PKCS1与PKCS8格式互转
仅私钥有PKCS1和PKCS8之分,公钥无格式区别。
/// <summary>/// Pkcs1>>Pkcs8/// </summary>/// <param name="privateKey">Pkcs1私钥</param>/// <param name="format">是否转PEM格式</param>/// <returns></returns>public static string PrivateKeyPkcs1ToPkcs8(string privateKey, bool format = false){var akp = RSAUtilities.GetAsymmetricKeyParameterFormPrivateKey(privateKey);if (format){var sw = new StringWriter();var pWrt = new PemWriter(sw);var pkcs8 = new Pkcs8Generator(akp);pWrt.WriteObject(pkcs8);pWrt.Writer.Close();return sw.ToString();}else{var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp);return Base64.ToBase64String(privateKeyInfo.GetEncoded());}}
/// <summary>/// Pkcs8>>Pkcs1/// </summary>/// <param name="privateKey">Pkcs8私钥</param>/// <param name="format">是否转PEM格式</param>/// <returns></returns>public static string PrivateKeyPkcs8ToPkcs1(string privateKey, bool format = false){var akp = RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(privateKey);if (format){var sw = new StringWriter();var pWrt = new PemWriter(sw);pWrt.WriteObject(akp);pWrt.Writer.Close();return sw.ToString();}else{var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp);return Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded());}}
PKCS1与PKCS8私钥中提取公钥
/// <summary>/// 从Pkcs1私钥中提取公钥/// </summary>/// <param name="privateKey">Pkcs1私钥</param>/// <returns></returns>public static string GetPublicKeyFromPrivateKeyPkcs1(string privateKey){var instance = RsaPrivateKeyStructure.GetInstance(Base64.Decode(privateKey));var publicParameter = (AsymmetricKeyParameter)new RsaKeyParameters(false, instance.Modulus,instance.PublicExponent);var privateParameter = (AsymmetricKeyParameter)new RsaPrivateCrtKeyParameters(instance.Modulus,instance.PublicExponent, instance.PrivateExponent, instance.Prime1, instance.Prime2, instance.Exponent1,instance.Exponent2, instance.Coefficient);var keyPair = new AsymmetricCipherKeyPair(publicParameter, privateParameter);var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);return Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded());}
/// <summary>/// 从Pkcs8私钥中提取公钥/// </summary>/// <param name="privateKey">Pkcs8私钥</param>/// <returns></returns>public static string GetPublicKeyFromPrivateKeyPkcs8(string privateKey){var privateKeyInfo = PrivateKeyInfo.GetInstance(Asn1Object.FromByteArray(Base64.Decode(privateKey)));privateKey = Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded());var instance = RsaPrivateKeyStructure.GetInstance(Base64.Decode(privateKey));var publicParameter = (AsymmetricKeyParameter)new RsaKeyParameters(false, instance.Modulus,instance.PublicExponent);var privateParameter = (AsymmetricKeyParameter)new RsaPrivateCrtKeyParameters(instance.Modulus,instance.PublicExponent, instance.PrivateExponent, instance.Prime1, instance.Prime2, instance.Exponent1,instance.Exponent2, instance.Coefficient);var keyPair = new AsymmetricCipherKeyPair(publicParameter, privateParameter);var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);return Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded());}
PEM操作
PEM格式密钥读取
public static string ReadPkcs1PrivateKey(string text){if (!text.StartsWith("-----BEGIN RSA PRIVATE KEY-----")){return text;}using (var reader = new StringReader(text)){var pr = new PemReader(reader);var keyPair = pr.ReadObject() as AsymmetricCipherKeyPair;pr.Reader.Close();var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair?.Private);return Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded());}}public static string ReadPkcs8PrivateKey(string text){if (!text.StartsWith("-----BEGIN PRIVATE KEY-----")){return text;}using (var reader = new StringReader(text)){var pr = new PemReader(reader);var akp = pr.ReadObject() as AsymmetricKeyParameter; ;pr.Reader.Close();return Base64.ToBase64String(PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp).GetEncoded());}}public static string ReadPublicKey(string text){if (!text.StartsWith("-----BEGIN PUBLIC KEY-----")){return text;}using (var reader = new StringReader(text)){var pr = new PemReader(reader);var keyPair = pr.ReadObject() as AsymmetricCipherKeyPair;pr.Reader.Close();var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair?.Public);returnBase64.ToBase64String(subjectPublicKeyIno.GetEncoded());}}
PEM格式密钥写入
public static string WritePkcs1PrivateKey(string privateKey){if (privateKey.StartsWith("-----BEGIN RSA PRIVATE KEY-----")){return privateKey;}var akp = RSAUtilities.GetAsymmetricKeyParameterFormPrivateKey(privateKey);using (var sw = new StringWriter()){var pWrt = new PemWriter(sw);pWrt.WriteObject(akp);pWrt.Writer.Close();return sw.ToString();}}public static string WritePkcs8PrivateKey(string privateKey){if (privateKey.StartsWith("-----BEGIN PRIVATE KEY-----")){return privateKey;}var akp = RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(privateKey);using (var sw = new StringWriter()){var pWrt = new PemWriter(sw);var pkcs8 = new Pkcs8Generator(akp);pWrt.WriteObject(pkcs8);pWrt.Writer.Close();return sw.ToString();}}public static string WritePublicKey(string publicKey){if (publicKey.StartsWith("-----BEGIN PUBLIC KEY-----")){return publicKey;}var akp = RSAUtilities.GetAsymmetricKeyParameterFormPublicKey(publicKey);using (var sw = new StringWriter()){var pWrt = new PemWriter(sw);pWrt.WriteObject(akp);pWrt.Writer.Close();return sw.ToString();}}
RSA加解密
获取非对称秘钥参数(AsymmetricKeyParameter)
/// <summary>/// -----BEGIN RSA PRIVATE KEY-----/// .../// -----END RSA PRIVATE KEY-----/// </summary>/// <param name="privateKey">Pkcs1格式私钥</param>/// <returns></returns>public static AsymmetricKeyParameter GetAsymmetricKeyParameterFormPrivateKey(string privateKey){if (string.IsNullOrEmpty(privateKey)){throw new ArgumentNullException(nameof(privateKey));}var instance = RsaPrivateKeyStructure.GetInstance(Base64.Decode(privateKey));return new RsaPrivateCrtKeyParameters(instance.Modulus, instance.PublicExponent, instance.PrivateExponent,instance.Prime1, instance.Prime2, instance.Exponent1, instance.Exponent2, instance.Coefficient);}/// <summary>/// -----BEGIN PRIVATE KEY-----/// .../// -----END PRIVATE KEY-----/// </summary>/// <param name="privateKey">Pkcs8格式私钥</param>/// <returns></returns>public static AsymmetricKeyParameter GetAsymmetricKeyParameterFormAsn1PrivateKey(string privateKey){return PrivateKeyFactory.CreateKey(Base64.Decode(privateKey));}/// <summary>/// -----BEGIN PUBLIC KEY-----/// .../// -----END PUBLIC KEY-----/// </summary>/// <param name="publicKey">公钥</param>/// <returns></returns>public static AsymmetricKeyParameter GetAsymmetricKeyParameterFormPublicKey(string publicKey){if (string.IsNullOrEmpty(publicKey)){throw new ArgumentNullException(nameof(publicKey));}return PublicKeyFactory.CreateKey(Base64.Decode(publicKey));}
RSA加解与解密
/// <summary>/// RSA加密/// </summary>/// <param name="data">未加密数据字节数组</param>/// <param name="parameters">非对称密钥参数</param>/// <param name="algorithm">密文算法</param>/// <returns>已加密数据字节数组</returns>public static byte[] Encrypt(byte[] data, AsymmetricKeyParameter parameters, string algorithm){if (data == null){throw new ArgumentNullException(nameof(data));}if (parameters == null){throw new ArgumentNullException(nameof(parameters));}if (string.IsNullOrEmpty(algorithm)){throw new ArgumentNullException(nameof(algorithm));}var bufferedCipher = CipherUtilities.GetCipher(algorithm);bufferedCipher.Init(true, parameters);return bufferedCipher.DoFinal(data);}/// <summary>/// RSA解密/// </summary>/// <param name="data">已加密数据字节数组</param>/// <param name="parameters">非对称密钥参数</param>/// <param name="algorithm">密文算法</param>/// <returns>未加密数据字节数组</returns>public static byte[] Decrypt(byte[] data, AsymmetricKeyParameter parameters, string algorithm){if (data == null){throw new ArgumentNullException(nameof(data));}if (parameters == null){throw new ArgumentNullException(nameof(parameters));}if (string.IsNullOrEmpty(algorithm)){throw new ArgumentNullException(nameof(algorithm));}var bufferedCipher = CipherUtilities.GetCipher(algorithm);bufferedCipher.Init(false, parameters);return bufferedCipher.DoFinal(data);}/// <summary>/// RSA加密——Base64/// </summary>/// <param name="data">未加密字符串</param>/// <param name="parameters">非对称密钥参数</param>/// <param name="algorithm">密文算法</param>/// <returns>已加密Base64字符串</returns>public static string EncryptToBase64(string data, AsymmetricKeyParameter parameters, string algorithm){return Base64.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(data), parameters, algorithm));}/// <summary>/// RSA解密——Base64/// </summary>/// <param name="data">已加密Base64字符串</param>/// <param name="parameters">非对称密钥参数</param>/// <param name="algorithm">密文算法</param>/// <returns>未加密字符串</returns>public static string DecryptFromBase64(string data, AsymmetricKeyParameter parameters, string algorithm){return Encoding.UTF8.GetString(Decrypt(Base64.Decode(data), parameters, algorithm));}/// <summary>/// RSA加密——十六进制/// </summary>/// <param name="data">未加密字符串</param>/// <param name="parameters">非对称密钥参数</param>/// <param name="algorithm">密文算法</param>/// <returns>已加密十六进制字符串</returns>public static string EncryptToHex(string data, AsymmetricKeyParameter parameters, string algorithm){return Hex.ToHexString(Encrypt(Encoding.UTF8.GetBytes(data), parameters, algorithm));}/// <summary>/// RSA解密——十六进制/// </summary>/// <param name="data">已加密十六进制字符串</param>/// <param name="parameters">非对称密钥参数</param>/// <param name="algorithm">密文算法</param>/// <returns>未加密字符串</returns>public static string DecryptFromHex(string data, AsymmetricKeyParameter parameters, string algorithm){return Encoding.UTF8.GetString(Decrypt(Hex.Decode(data), parameters, algorithm));}
RSA密文算法
public const string RSA_NONE_NoPadding = "RSA/NONE/NoPadding";public const string RSA_NONE_PKCS1Padding = "RSA/NONE/PKCS1Padding";public const string RSA_NONE_OAEPPadding = "RSA/NONE/OAEPPadding";public const string RSA_NONE_OAEPWithSHA1AndMGF1Padding = "RSA/NONE/OAEPWithSHA1AndMGF1Padding";public const string RSA_NONE_OAEPWithSHA224AndMGF1Padding = "RSA/NONE/OAEPWithSHA224AndMGF1Padding";public const string RSA_NONE_OAEPWithSHA256AndMGF1Padding = "RSA/NONE/OAEPWithSHA256AndMGF1Padding";public const string RSA_NONE_OAEPWithSHA384AndMGF1Padding = "RSA/NONE/OAEPWithSHA384AndMGF1Padding";public const string RSA_NONE_OAEPWithMD5AndMGF1Padding = "RSA/NONE/OAEPWithMD5AndMGF1Padding";public const string RSA_ECB_NoPadding = "RSA/ECB/NoPadding";public const string RSA_ECB_PKCS1Padding = "RSA/ECB/PKCS1Padding";public const string RSA_ECB_OAEPPadding = "RSA/ECB/OAEPPadding";public const string RSA_ECB_OAEPWithSHA1AndMGF1Padding = "RSA/ECB/OAEPWithSHA1AndMGF1Padding";public const string RSA_ECB_OAEPWithSHA224AndMGF1Padding = "RSA/ECB/OAEPWithSHA224AndMGF1Padding";public const string RSA_ECB_OAEPWithSHA256AndMGF1Padding = "RSA/ECB/OAEPWithSHA256AndMGF1Padding";public const string RSA_ECB_OAEPWithSHA384AndMGF1Padding = "RSA/ECB/OAEPWithSHA384AndMGF1Padding";public const string RSA_ECB_OAEPWithMD5AndMGF1Padding = "RSA/ECB/OAEPWithMD5AndMGF1Padding";......
编码算法
大家要明白,不管是对称算法还是非对称算法,其输入与输出均是字节数组,通常我们要结合编码算法对加密之后或解密之前的数据,进行编码操作。
BouncyCastle提供的Base64编码算法
namespace Org.BouncyCastle.Utilities.Encoders{public sealed class Base64{//public static byte[] Decode(byte[] data);//public static byte[] Decode(string data);//public static int Decode(string data, Stream outStream);//public static byte[] Encode(byte[] data);//public static byte[] Encode(byte[] data, int off, int length);//public static int Encode(byte[] data, Stream outStream);//public static int Encode(byte[] data, int off, int length, Stream outStream);public static string ToBase64String(byte[] data);public static string ToBase64String(byte[] data, int off, int length);}}
BouncyCastle提供的Hex十六进制编码算法
namespace Org.BouncyCastle.Utilities.Encoders{//// 摘要:// Class to decode and encode Hex.public sealed class Hex{//public static byte[] Decode(byte[] data);//public static byte[] Decode(string data);//public static int Decode(string data, Stream outStream);//public static byte[] Encode(byte[] data);//public static byte[] Encode(byte[] data, int off, int length);//public static int Encode(byte[] data, Stream outStream);//public static int Encode(byte[] data, int off, int length, Stream outStream);public static string ToHexString(byte[] data);public static string ToHexString(byte[] data, int off, int length);}}
RSA加解密示例
private static void RSA_ECB_PKCS1Padding(){var data = "hello rsa";Console.WriteLine($"加密原文:{data}");// rsa pkcs8 private key encrypt//algorithm rsa/ecb/pkcs1paddingvar pkcs8data = RSA.EncryptToBase64(data, RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(pkcs8_1024_private_key),CipherAlgorithms.RSA_ECB_PKCS1Padding);Console.WriteLine("密钥格式:pkcs8,密文算法:rsa/ecb/pkcs1padding,加密结果");Console.WriteLine(pkcs8data);//rsa pkcs1 private key encrypt//algorithm rsa/ecb/pkcs1paddingvar pkcs1data = RSA.EncryptToBase64(data, RSAUtilities.GetAsymmetricKeyParameterFormPrivateKey(pkcs1_1024_private_key),CipherAlgorithms.RSA_ECB_PKCS1Padding);Console.WriteLine($"密钥格式:pkcs1,密文算法:rsa/ecb/pkcs1padding");Console.WriteLine(pkcs1data);Console.WriteLine($"加密结果比对是否一致:{pkcs8data.Equals(pkcs1data)}");var _1024_public_key = RSAKeyConverter.GetPublicKeyFromPrivateKeyPkcs1(pkcs1_1024_private_key);Console.WriteLine($"从pkcs1私钥中提取公钥:");Console.WriteLine(_1024_public_key);Console.WriteLine("使用公钥解密数据:");//rsa public key decrypt//algorithm rsa/ecb/pkcs1paddingConsole.WriteLine(RSA.DecryptFromBase64(pkcs1data, RSAUtilities.GetAsymmetricKeyParameterFormPublicKey(_1024_public_key),CipherAlgorithms.RSA_ECB_PKCS1Padding));Console.WriteLine();}
下期预告
下一篇将介绍哈希算法(HMACSHA1、HMACSHA256、SHA1、SHA1WithRSA、SHA256、SHA256WithRSA),敬请期待…
.NET Core加解密实战系列之——RSA非对称加密算法相关推荐
- .NET Core加解密实战系列之——消息摘要与数字签名算法
简介 加解密现状,编写此系列文章的背景: 需要考虑系统环境兼容性问题(Linux.Windows) 语言互通问题(如C#.Java等)(加解密本质上没有语言之分,所以原则上不存在互通性问题) 网上资料 ...
- .NET Core加解密实战系列之——使用BouncyCastle制作p12(.pfx)数字证书
简介 加解密现状,编写此系列文章的背景: 需要考虑系统环境兼容性问题(Linux.Windows) 语言互通问题(如C#.Java等)(加解密本质上没有语言之分,所以原则上不存在互通性问题) 网上资料 ...
- Java 加解密技术系列之 RSA
序 距离上一次写博客感觉已经很长时间了,先吐槽一下,这个月以来,公司一直在加班,又是发版.上线,又是新项目太紧,具体的就不多说了,想听我吐槽的小伙伴,可以私信给我(*^__^*) .上一篇文章,已经把 ...
- Java 实现 RSA 非对称加密算法-加解密和签名验签
1. 非对称加密算法简介 非对称加密算法又称现代加密算法,是计算机通信安全的基石,保证了加密数据不会被破解.与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密(pr ...
- Java 加解密技术系列之 MD5
序 上一篇文章中,介绍了最基础的编码方式 - - BASE64,也简单的提了一下编码的原理.这篇文章继续加解密的系列,当然也是介绍比较基础的加密方式 - - MD5,MD5 属于单向加密算法,是不可逆 ...
- python能解密java的_实现Java加密,Python解密的RSA非对称加密算法功能
摘要 因为最近业务需要使用到openssl的rsa非对称加密算法,研究了下它的使用方式,但是特殊在于前端分IOS和android两端,所以前端部门要求使用java给他们做一个加密工具包,但是因为服务端 ...
- java python rsa加密_实现Java加密,Python解密的RSA非对称加密算法功能
摘要 因为最近业务需要使用到openssl的rsa非对称加密算法,研究了下它的使用方式,但是特殊在于前端分IOS和android两端,所以前端部门要求使用java给他们做一个加密工具包,但是因为服务端 ...
- Java 加解密技术系列之 总结
序 上一篇文章中简单的介绍了第二种非对称加密算法 - - DH,这种算法也经常被叫做密钥交换协议,它主要是针对密钥的保护.同时,由于水平的限制,打算这个系列就到此为止了,这篇文章就算是一个总结吧,回顾 ...
- java 3des解密过程_6. Java 加解密技术系列之 3DES
importjavax.crypto.Cipher;importjavax.crypto.SecretKey;importjavax.crypto.spec.SecretKeySpec;importj ...
最新文章
- 高压发生器的输出电压波形
- Hadoop、Spark、Hbase、Hive的安装
- 教你移除IE 7.0浏览器的默认搜索框
- jQuery对类的操作.addClass()/.removeClass()/.hasClass()方法
- mysql导出表结构_mysql导入导出表结构及表数据及执行sql文件
- XOS 源码详解3: os_s_xxxx.s 汇编代码的 LDR指令
- 英语面试功略:英语口语突击法
- java子字符串查找位置_初学者求教,如何在字符串中查找多个子字符串的位置...
- 手机语音混响软件_手机混响软件 如何把微信里的语音转发出去 有什么好用的变声软件 安卓微信语音转发插件...
- 图像直方图以及直方图的应用
- 免费的Bootstrap管理后台模板集合
- 软件项目管理期末复习整理
- 英语练习95 Computers
- 一款 Windows 上的万能工具 Wox
- CST学习:圆形贴片天线四元阵设计(一)阵元设计
- 少儿创意学编程(Scratch基础篇):第5课——绘画板
- word文件太大怎么压缩到最小-word压缩教程
- [洛谷 P4084 USACO17DEC] Barn Painting G (树形dp经典)
- iphone开发每日一练1【2011-09-30】
- 2021-11-29 vue移动端卖座电影项目(三) vue项目中使用Swiper插件,Film页面设置轮拨图,nowPlaying页面设置样式
热门文章
- 2017年初随想——几个小目标
- 【转】Asp.Net中Excel操作权限的问题
- kali linux超级用户_如何优雅的在Linux上使用Powershell]
- chromebook刷机_如何在Chromebook上切换(或离开)Canary频道
- monogdb操作system.*权限
- 微信小程序仿微信SlideView组件slide-view
- Android GIS开发系列-- 入门季(13)Gdal简单写个shp文件
- 12c分页查询特性FETCH FIRST ROWS,OFFSET ROWS FETCH NEXT ROW LIMIT Clause子句
- 仿豆丁百度文库网页版阅读器完整解决方案
- LoadPostData 的一些注意事项