.NET导入openssl生成的公钥之BEGIN RSA PUBLIC KEY

我得到了一个公钥,形式如下

-----BEGIN RSA PUBLIC KEY-----

MIGJAoGBAMroxz3qtok…….

……

-----END RSA PUBLIC KEY-----

相要用C#程序,将它导入并加密数据传给opensll应用程序解密。在网上找到很多方法,其中opensslkey.cs文件的实现最完善,但它只能解析-----BEGIN PUBLIC KEY-----打头的公钥。而且内容的长度也不同,看来它是解不开了。在搜索的过程中,发现Jeffrey Walton有文章Cryptographic Interoperability: Keys,讲的是密钥的编解码。从以下地址可以获得

http://www.codeproject.com/KB/security/CryptoInteropKeys.aspx

多从他附带的代码中,搞清楚了密钥的格式。分析了一下我的钥格式,发现其实公钥格式很简单。如下:

//* +- SEQUENCE        // RSAPrivateKey 
//*   +- INTEGER(N)   // N 
//*   +- INTEGER(E)   // E

上面我们看到的就是这个公钥中的全部内容了,比-----BEGIN PUBLIC KEY-----打头的要少很多内容。就是因为太简单了,才给我造成了很大的麻烦!上面的格式其实是一个ASN标准编码,Jeffrey Walton提供的源码中有一个类AsnParser很容易就能解析出来。于是,我在Jeffrey Walton写的类AsnKeyParser中加了一个函数ParsePkcsRSAPublicKey。

internal RSAParameters ParsePkcsRSAPublicKey() 

    //* +- SEQUENCE        // RSAPrivateKey 
    //*   +- INTEGER(N)   // N 
    //*   +- INTEGER(E)   // E

RSAParameters parameters = new RSAParameters();

// Sanity Check 
    int length = 0;

// Checkpoint 
    int position = parser.CurrentPosition();

// Ignore Sequence - PublicKeyInfo 
    length = parser.NextSequence(); 
    if (length != parser.RemainingBytes()) 
    { 
        StringBuilder sb = new StringBuilder("Incorrect Sequence Size. "); 
        sb.AppendFormat("Specified: {0}, Remaining: {1}", 
          length.ToString(CultureInfo.InvariantCulture), 
          parser.RemainingBytes().ToString(CultureInfo.InvariantCulture)); 
        throw new BerDecodeException(sb.ToString(), position); 
    }

// Checkpoint 
    position = parser.CurrentPosition(); 
    int remaining = parser.RemainingBytes();

parameters.Modulus = TrimLeadingZero(parser.NextInteger());

parameters.Exponent = TrimLeadingZero(parser.NextInteger());

Debug.Assert(0 == parser.RemainingBytes());

return parameters; 
}

注意加红的两行就是公钥的全部。

由于AsnKeyParser只有一个构造函数,并且只接受文件名,并从文件中读取数据。但它并不认识PEM格式,所以PEM的解析还是放在外面。看一下原来的构造函数:

internal AsnKeyParser(String pathname) 

    using (BinaryReader reader = new BinaryReader( 
        new FileStream(pathname, FileMode.Open, FileAccess.Read))) 
    { 
        FileInfo info = new FileInfo(pathname);

parser = new AsnParser(reader.ReadBytes((int)info.Length)); 
    } 
}

可以看出来,从文件读取数据之后,直接把二进制数据传给了AsnParser。而AsnParser当然不能解析Base64解码后的数据。我又给它加了一个构造函数

internal AsnKeyParser(AsnParser parser) 

    this.parser = parser; 
}

看出来了吧,就是要在外面做完Base64解码之后再传给AsnKeyParser。

最后再写了一个从文件加载,及调用AsnKeyParser的函数,就大功告成了!

private static RSAParameters LoadRsaPublicKey() 

    string s = File.ReadAllText("pub.pem"); // 从文件读取 
    StringBuilder build = new StringBuilder(s);

// 去掉头尾

build.Replace("-----BEGIN RSA PUBLIC KEY-----", ""); 
    build.Replace("-----END RSA PUBLIC KEY-----", "");

s = build.ToString().Trim(); 
    byte[] binKey = System.Convert.FromBase64String(s);  // Base64解码

AsnParser parser = new AsnParser(binKey); // 现在已经是AsnParser能认识的数据了 
    AsnKeyParser keyParser = new AsnKeyParser(parser);  // 就刚加的构造孙数

RSAParameters publicKey = keyParser.ParsePkcsRSAPublicKey();  // 还是用刚加的解析函数

return publicKey; // 公钥已经得到,可以尽情的用RSACryptoServiceProvider了。 
}

为了测试得到的密钥正不正常,我用openssl生成了一对密钥。再用我的程序来加载密钥,并测试加密与解密,来最后解密出来的结果,是否与加密前一至。

生成的密钥如下你也可以自己生成

pub.pem

-----BEGIN RSA PUBLIC KEY----- 
MIGJAoGBAMroxz3qtok9aa777ssNfVKHgGI8BPrGexhS2PE+2xZGffakR2QbS5vw 
CidhVzrpzRJJuaZqktBrcVC7as1TsP2mY8RgWPNOvHisDDZp+H5c2+UwVQ6bV1tk 
MXx1RSDryOO4mmeONJE8aJcGG+9KWkoZEQL5XIzrzy3NeYNYu5J1AgMBAAE= 
-----END RSA PUBLIC KEY-----

pri.pem

-----BEGIN RSA PRIVATE KEY----- 
MIICXgIBAAKBgQDK6Mc96raJPWmu++7LDX1Sh4BiPAT6xnsYUtjxPtsWRn32pEdk 
G0ub8AonYVc66c0SSbmmapLQa3FQu2rNU7D9pmPEYFjzTrx4rAw2afh+XNvlMFUO 
m1dbZDF8dUUg68jjuJpnjjSRPGiXBhvvSlpKGREC+VyM688tzXmDWLuSdQIDAQAB 
AoGBAMfr6sO6yvcVp1ddqr4uIFh8YaZodI+RmB8zIcUwpTShZ+Lnod+kdS7Dp319 
jzDgw8lNErpBLz5jXlapEmYUG8FNOLK/z45oVVSlLZquuQowcR3JoDtb/yKvOPdQ 
EavCsvoQT7lIn4oCUAWZP/yyQQA2TDjyVmUF9gQctbbuwPkBAkEA9L0FC91Pa3dd 
Ry1sD0rhcrLAsFZX0gzd3ozgAQGM/p2dY1AN0pOF15mJgaHRP2UImqb0qtmsroSd 
BwEsZulwcQJBANQ/BMFnfcvxh7IvrxvA8Mh/Edb8RJcKxuutLjABj4Ah8nIdGb5S 
XHhCQ3JIA2x6ydygY6ldqLvsYAQiuOY2hEUCQQDlV3QxKBTSmiq5FqGauwsFlujm 
1iK53gDUGqOXjcJ4n27rsAsj98aGwYSQC/mwNJeZhTbmG9GsQO19sOXREpShAkEA 
sKx4b+mO3GoEE33/3DFh/PNRTUyWZ8hPxzRUIx/ZbMZVQ0oX+MYkNPKrpABv4Sfg 
ymc0LnJJF4zua+LfWLp+pQJAXFa8xvDdLcQ4PhG4pDqUuvklbUkyl36GrfU9CkIK 
GbnoDXw7W5SJ0qb258JxIx4cNsDIC+CU0r7Ejmo5g3RMew== 
-----END RSA PRIVATE KEY-----

把它们放在bin\Debug目录下

又实现了一个解析密钥的函数

internal RSAParameters ParsePkcsRSAPrivateKey() 

    //*+- SEQUENCE            // RSAPrivateKey 
    //*     +- INTEGER(0)       // Version - 0 (v1998) 
    //*     +- INTEGER(N) 
    //*     +- INTEGER(E) 
    //*     +- INTEGER(D) 
    //*     +- INTEGER(P) 
    //*     +- INTEGER(Q) 
    //*     +- INTEGER(DP) 
    //*     +- INTEGER(DQ) 
    //*     +- INTEGER(Inv Q)

RSAParameters parameters = new RSAParameters();

// Current value 
    byte[] value = null;

// Checkpoint 
    int position = parser.CurrentPosition();

// Sanity Check 
    int length = 0;

// Ignore Sequence - PrivateKeyInfo 
    length = parser.NextSequence(); 
    if (length != parser.RemainingBytes()) 
    { 
        StringBuilder sb = new StringBuilder("Incorrect Sequence Size. "); 
        sb.AppendFormat("Specified: {0}, Remaining: {1}", 
          length.ToString(CultureInfo.InvariantCulture), parser.RemainingBytes().ToString(CultureInfo.InvariantCulture)); 
        throw new BerDecodeException(sb.ToString(), position); 
    }

// Checkpoint 
    position = parser.CurrentPosition(); 
    // Version 
    value = parser.NextInteger(); 
    if (0x00 != value[0]) 
    { 
        StringBuilder sb = new StringBuilder("Incorrect RSAPrivateKey Version. "); 
        BigInteger v = new BigInteger(value); 
        sb.AppendFormat("Expected: 0, Specified: {0}", v.ToString(10)); 
        throw new BerDecodeException(sb.ToString(), position); 
    }

parameters.Modulus = TrimLeadingZero(parser.NextInteger()); 
    parameters.Exponent = TrimLeadingZero(parser.NextInteger()); 
    parameters.D = TrimLeadingZero(parser.NextInteger()); 
    parameters.P = TrimLeadingZero(parser.NextInteger()); 
    parameters.Q = TrimLeadingZero(parser.NextInteger()); 
    parameters.DP = TrimLeadingZero(parser.NextInteger()); 
    parameters.DQ = TrimLeadingZero(parser.NextInteger()); 
    parameters.InverseQ = TrimLeadingZero(parser.NextInteger());

Debug.Assert(0 == parser.RemainingBytes());

return parameters; 
}

密钥比公钥复杂的多,但也是通常所见到的格式的一小部份。

加载密钥的函数

private static RSAParameters LoadRsaPrivateKey() 

    string s = File.ReadAllText("pri.pem"); 
    StringBuilder build = new StringBuilder(s);

build.Replace("-----BEGIN RSA PRIVATE KEY-----", ""); 
    build.Replace("-----END RSA PRIVATE KEY-----", "");

s = build.ToString().Trim(); 
    byte[] binKey = Convert.FromBase64String(s);

AsnParser parser = new AsnParser(binKey); 
    AsnKeyParser keyParser = new AsnKeyParser(parser);

RSAParameters privateKey = keyParser.ParsePkcsRSAPrivateKey();

return privateKey; 
}

与加载公钥差不多

最后还有一点测试代码

private static void TestRsaKeys() // 测试的入口 

    RSAParameters publicKey = LoadRsaPublicKey(); 
    RSAParameters privateKey = LoadRsaPrivateKey();

string s = TryEncrypt(publicKey); // 测试加密 
    System.Console.Out.WriteLine(s);

s = TryDecrypt(privateKey, s); // 测试解密 
    System.Console.Out.WriteLine(s); 
}

private static string TryEncrypt(RSAParameters publicKey) 

    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); 
    rsa.ImportParameters(publicKey);

string test = "用来测试加密的串"; 
    byte[] bytes = Encoding.Unicode.GetBytes(test); 
    byte[] encryptedBytes = rsa.Encrypt(bytes, false);

string outString = Convert.ToBase64String(encryptedBytes);

return outString; 
}

private static string TryDecrypt(RSAParameters privateKey, string src) 

    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); 
    rsa.ImportParameters(privateKey);

byte[] bytes = Convert.FromBase64String(src); 
    byte[] decryptBytes = rsa.Decrypt(bytes, false);

string outString = Encoding.Unicode.GetString(decryptBytes);

return outString; 
}

经过测试已经证明我的解码是成功的。感谢Jeffrey Walton!

值得一提的是AsnKeyParser中的ParseRSAPublicKey是可以直接解析-----BEGIN PUBLIC KEY-----打头的公钥。通常openssl生成的私钥都是-----BEGIN RSA PRIVATE KEY----- 打头的,所以还是要用我写的ParsePkcsRSAPrivateKey函数。

.NET导入openssl生成的公钥之BEGIN RSA PUBLIC KEY的更多相关文章

  1. yum 安装提示公钥安装失败,Public key for .x86_64.rpm is not instal 手动导入公钥方案

    Linux 中yum 安装google-chrome-stable时,报错如下,提示公钥安装失败,原因是 GPG公钥获取失败,无法连接获取到 https://dl-ssl.google.com/lin ...

  2. Java中使用OpenSSL生成的RSA公私钥

    RSA是什么:RSA公钥加密算法是1977年由Ron Rivest.Adi Shamirh和LenAdleman在(美国麻省理工学院)开发的.RSA取名来自开发他们三者的名字.RSA是目前最有影响力的 ...

  3. 关于ssh-keygen 生成的key以“BEGIN OPENSSH PRIVATE KEY”开头

    现在使用命令 ssh-keygen -t rsa  生成ssh,默认是以新的格式生成,id_rsa的第一行变成了"BEGIN OPENSSH PRIVATE KEY" 而不在是&q ...

  4. 使用OpenSSL生成证书

    使用OpenSSL生成证书 下载安装openssl,进入/bin/下面,执行命令(把ssl目录下的openssl.cnf 拷贝到bin目录下)1.首先要生成服务器端的私钥(key文件):openssl ...

  5. 生成ssh公钥

    部分内容参考:http://git.mydoc.io/?t=154712 1.在电脑桌面上右键,选择git Base here 2.生成ssh公钥 ssh-keygen -t rsa -C" ...

  6. Putty使用公钥认证时,报错:Disconnected: No supported authentication methods available(server sent:public key) 问题的解决

    Putty使用公钥认证时,按照常规方法设置,一直报错:Disconnected: No supported authentication methods available (server sent: ...

  7. rsa字符串格式公钥转换python rsa库可识别的公钥形式

    在爬虫分析的时候,经常在网页上看到如下格式的rsa公钥: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDC7kw8r6tq43pwApYvkJ5laljaN9BZb21 ...

  8. 在iOS中使用OpenSSL的Public Key 进行加密

    这几天一直潜心于iOS开发,刚好把遇到的问题都记录一下.这次遇到的问题就是如果根据得到的Public Key在iOS 客户端对用户名和密码进行加密. Public Key如下: -----BEGIN ...

  9. 如何使用openssl生成RSA公钥和私钥对

      在ubuntu上要使用openssl的话需要先进行安装,命令如下: sudo apt-get install openssl 安装完成就可以使用openssl了. 首先需要进入openssl的交互 ...

随机推荐

  1. SSIS使用OleDB和Ado.Net两种方式调用 存储过程

    在使用”执行 SQL 任务“组件调用存储过程时,连接方式使用OleDB和Ado.Net稍有不同,结合图例说明一下 当我们使用OleDB时,设置的截图如下: 参数使用?来代替,Parameter Nam ...

  2. php 函数ignore_user_abort()

    ignore_user_abort()  设置与客户机断开是否会终止脚本的执行. 工作中看到这样一个类似的方法,查资料理解了一下: 一个的ignore_user_abort()的例子,配合set_ti ...

  3. dennis gabor 从傅里叶(Fourier)变换到伽柏(Gabor)变换再到小波(Wavelet)变换(转载)

    dennis gabor 题目:从傅里叶(Fourier)变换到伽柏(Gabor)变换再到小波(Wavelet)变换 本文是边学习边总结和摘抄各参考文献内容而成的,是一篇综述性入门文档,重点在于梳理傅 ...

  4. WebRTC录音(1)-实现通话双向录音

    最近公司的iPad项目中一个功能点涉及到了VOIP通讯中的录音,需要在已有的WebRTC引擎中增加录音功能,录制通话双方的声音参考了往上一位兄弟的博文(链接在此 http://blog.csdn.ne ...

  5. live555源代码编译

    参考http://www.cnblogs.com/MikeZhang/archive/2013/04/24/live555Windows_20130424.html 环境:windowsxp + VS ...

  6. HDU-5806 NanoApe Loves Sequence Ⅱ(two-pointer或二分)

    题目大意:给一个整数序列,统计<k,m>子序列的数目.<k,m>序列是满足第k大的数字不比m小的连续子序列. 题目分析:维护一个不小于m的数的个数的后缀和数组,可以枚举序列起点 ...

  7. HZAU 17:LCS

    17: LCS Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 184  Solved: 43[Submit][Status][Web Board] De ...

  8. windows 10安装framework 3.5失败的解决方案

    装了两次win 10,全都因为没法安装framework 3.5,用不了老版本的开发环境,又换回了win7. 网上有两种解决方案: a,通过iso安装. 可是拜托,我的系统都是用ghost版本安装的, ...

  9. 【转】BLE_CC2540_初学者入门指导

    原文网址:http://www.deyisupport.com/question_answer/wireless_connectivity/bluetooth/f/103/t/61462.aspx 看 ...

  10. ubuntu下如何安装wxpython

    1.运行时缺失wx库,如何安装 Error:ImportError: No module named wx 解决方法:sudo apt-get install python-wxgtk2.8 pyth ...