背景介绍:
服务器在传输数据前先进行RSA加密,然后在手机端进行RSA解密。
开发任务:
IOS客户端进行RSA解密
已知数据:
.net仅仅提供了公钥和私钥,但是格式为XML格式。格式如下:
公钥!!加密
private static string publickey = @"<RSAKeyValue><Modulus>loChDDauMkcVdcentrn123EttwW66zx8hcPQmSu3yaWKq3q7kCFhnbQv4+MfylbzekpweH8896WqDPNtTl+gIE6UCJckHXmGspJI9OXZ7zAtLVMB9nDoLhBeEqpNBC93ACkNyI29VN6YvqnVuIrxsputcb5MBcb4slmVeus6668=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
私钥!!解密
private static string privatekey = @"<RSAKeyValue><Modulus>loChDDauMkcVdcentrn0SvEttwW66zx8hcPQmSu3yaWKq3q7kCFhnbQv4+MfylbzekpweHcziwWqDPNtTl+gIE6UCJckHXmGspJI9OXZ7zAtLVMB9nDoLhBeEqpNBC93ACkNyI29VN6YtyuVuIrxsputcb5MBcb4slmVeus2q5M=</Modulus><Exponent>AQAB</Exponent><P>0UENxry9MzrOo/tZboOiOLh8Z+7dUUgQqhAeBuR31Or4S/88b2A+HCCjwjpSqlV6eX13kJOoD+XNCBnZdr3+yw==</P><Q>uB+mtbKnAf6JcoRyCwIMs2S/KGU0IB53boj4KKSwsb/ffhg12346saekETWuRN8Rse5q8ExcFUmxMO7ZmYllWQ==</Q><DP>AjGLYmzyEspoBiIJYvYwSiXs7+36FfMKca3vkqTseKfp6GoJiSVaIVNaRBv2Z+CzgviEu1hA58HB73bN58fAFw==</DP><DQ>ont7+JghfUcMMDzlDwXOA3mCn7k/mKCKMMc6h1Negl2acoIXtusicihIRs+ibDCWQvjs8gXQtRCFB3OgHzf6cQ==</DQ><InverseQ>PFnyOHfoeraI+n6325ddKgIS6kY5ZWuguqmI+66XFZn+8Pq4gTIpXFhXZFBH/SYyNhWB2sOi94lhfdWcOwPX4A==</InverseQ><D>JUbx3M0UY2ag5Z3QqB0x6XgIOytjq009iIJCQhYGfR8QFD4fg+kIvR96tyaz4pGb19/lMKlRrm72OiP3IHDl/4kXmA7PUY4d3LoJTsXCrnJNQrPWD4DLs3iFpYWgymDqRFuVBv06OWjewXiEFDCybnlie6CahmL0q4EebYgIOTk=</D></RSAKeyValue>";
解决方案:
步骤一:XML-->PEM(参考http://bbs.csdn.net/topics/390913872),可以找.net帮忙转换
私钥:
var rsa = new RSACryptoServiceProvider();
using(var sr = new StreamReader("e:\\key.xml"))
{
rsa.FromXmlString(sr.ReadToEnd());
}
var p = rsa.ExportParameters(true);
var key = new RsaPrivateCrtKeyParameters(
new BigInteger(1, p.Modulus), new BigInteger(1, p.Exponent), new BigInteger(1, p.D),
new BigInteger(1, p.P), new BigInteger(1, p.Q), new BigInteger(1, p.DP), new BigInteger(1, p.DQ),
new BigInteger(1, p.InverseQ));
using (var sw = new StreamWriter("e:\\key.pem"))
{
var pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(sw);
pemWriter.WriteObject(key);
}
公钥:
public static String ExportPublicKeyToPEMFormat(RSACryptoServiceProvider csp)
{
TextWriter outputStream = new StringWriter();
var parameters = csp.ExportParameters(false);
using (var stream = new MemoryStream())
{
var writer = new BinaryWriter(stream);
writer.Write((byte)0x30); // SEQUENCE
using (var innerStream = new MemoryStream())
{
var innerWriter = new BinaryWriter(innerStream);
EncodeIntegerBigEndian(innerWriter, new byte[] { 0x00 }); // Version
EncodeIntegerBigEndian(innerWriter, parameters.Modulus);
EncodeIntegerBigEndian(innerWriter, parameters.Exponent);
//All Parameter Must Have Value so Set Other Parameter Value Whit Invalid Data (for keeping Key Structure use "parameters.Exponent" value for invalid data)
EncodeIntegerBigEndian(innerWriter, parameters.Exponent); // instead of parameters.D
EncodeIntegerBigEndian(innerWriter, parameters.Exponent); // instead of parameters.P
EncodeIntegerBigEndian(innerWriter, parameters.Exponent); // instead of parameters.Q
EncodeIntegerBigEndian(innerWriter, parameters.Exponent); // instead of parameters.DP
EncodeIntegerBigEndian(innerWriter, parameters.Exponent); // instead of parameters.DQ
EncodeIntegerBigEndian(innerWriter, parameters.Exponent); // instead of parameters.InverseQ
var length = (int)innerStream.Length;
EncodeLength(writer, length);
writer.Write(innerStream.GetBuffer(), 0, length);
}
var base64 = Convert.ToBase64String(stream.GetBuffer(), 0, (int)stream.Length).ToCharArray();
outputStream.WriteLine("-----BEGIN PUBLIC KEY-----");
// Output as Base64 with lines chopped at 64 characters
for (var i = 0; i < base64.Length; i += 64)
{
outputStream.WriteLine(base64, i, Math.Min(64, base64.Length - i));
}
outputStream.WriteLine("-----END PUBLIC KEY-----");
return outputStream.ToString();
}
}
private static void EncodeIntegerBigEndian(BinaryWriter stream, byte[] value, bool forceUnsigned = true)
{
stream.Write((byte)0x02); // INTEGER
var prefixZeros = 0;
for (var i = 0; i < value.Length; i++)
{
if (value[i] != 0) break;
prefixZeros++;
}
if (value.Length - prefixZeros == 0)
{
EncodeLength(stream, 1);
stream.Write((byte)0);
}
else
{
if (forceUnsigned && value[prefixZeros] > 0x7f)
{
// Add a prefix zero to force unsigned if the MSB is 1
EncodeLength(stream, value.Length - prefixZeros + 1);
stream.Write((byte)0);
}
else
{
EncodeLength(stream, value.Length - prefixZeros);
}
for (var i = prefixZeros; i < value.Length; i++)
{
stream.Write(value[i]);
}
}
}
private static void EncodeLength(BinaryWriter stream, int length)
{
if (length < 0) throw new ArgumentOutOfRangeException("length", "Length must be non-negative");
if (length < 0x80)
{
// Short form
stream.Write((byte)length);
}
else
{
// Long form
var temp = length;
var bytesRequired = 0;
while (temp > 0)
{
temp >>= 8;
bytesRequired++;
}
stream.Write((byte)(bytesRequired | 0x80));
for (var i = bytesRequired - 1; i >= 0; i--)
{
stream.Write((byte)(length >> (8 * i) & 0xff));
}
}
}
经过转换,我们可以得到PEM格式的公钥和私钥,格式如下:
公钥:
-----BEGIN PUBLIC KEY——
MIGqAgEAAoGBAJaAoQw2rjJHFXXHp7a59ErxLbcFuus8fIXD0Jkrt8mliqt6u5Ah YZ20L+PjH8pW83pKcHh3M4sFqgzzbU5foCBOlAiXJB15hrKSSPTl2e8wLS1TAfZw 6C4QXhKqTQQvdwApDciNvVTemL6p1biK8bKbrXG+TAXG+LJZlXrrNquTAgMBAAEC
AwEAAQIDAQABAgMBAAECAwEAAQIDAQABAgMBAAE=
-----END PUBLIC KEY——
私钥:
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCWgKEMNq4yRxV1x6e2ufRK8S23BbrrPHyFw9CZK7fJpYqreruQ
IWGdtC/j4x/KVvN6SnB4dzOLBaoM821OX6AgTpQIlyQdeYaykkj05dnvMC0tUwH2
cOguEF4Sqk0EL3cAKQ3Ijb1U3pi+qdW4ivGym61xvkwFxviyWZV66zarkwIDAQAB
AoGAJUbx3M0UY2ag5Z3QqB0x6XgIOytjq009iIJCQhYGfR8QFD4fg+kIvR96tyaz
4pGb19/lMKlRrm72OiP3IHDl/4kXmA7PUY4d3LoJTsXCrnJNQrPWD4DLs3iFpYWg
ymDqRFuVBv06OWjewXiEFDCybnlie6CahmL0q4EebYgIOTkCQQDRQQ3GvL0zOs6j
+1lug6I4uHxn7t1RSBCqEB4G5HfU6vhL/zxvYD4cIKPCOlKqVXp5fXeQk6gP5c0I
Gdl2vf7LAkEAuB+mtbKnAf6JcoRyCwIMs2S/KGU0IB53boj4KKSwsb/ffhg2ty36
saekETWuRN8Rse5q8ExcFUmxMO7ZmYllWQJAAjGLYmzyEspoBiIJYvYwSiXs7+36
FfMKca3vkqTseKfp6GoJiSVaIVNaRBv2Z+CzgviEu1hA58HB73bN58fAFwJBAKJ7
e/iYIX1HDDA85Q8FzgN5gp+5P5igijDHOodTXoJdmnKCF7brInIoSEbPomwwlkL4
7PIF0LUQhQdzoB83+nECQDxZ8jh36Hq2iPp37yInXSoCEupGOWVroLqpiPuulxWZ
/vD6uIEyKVxYV2RQR/0mMjYVgdrDoveJBkx1nDsD1+A=
-----END RSA PRIVATE KEY——
步骤二:生成私钥pem文件
1.打开终端,输入 “openssl genrsa -out private_key.pem 1024"
2.打开Finder,在【这台Mac】查找".pem",应该可以看到 private_key.pem。
3.选中private_key.pem , 选择用文本编辑的方式打开
4.将里面的内容替换成你的私钥
步骤三:生成公钥pem文件
1.在终端输入
openssl rsa -in private_key.pem -pubout -out public_key.pem
2.打开Finder,在【这台Mac】查找".pem",应该可以看到 public_key.pem.
3.选中public_key.pem , 选择用文本编辑的方式打开
4.将里面的内容替换成你的公钥
步骤四:demo测试(参考:https://github.com/jslim89/RSA-objc)
打开demo,会有报错,
step01:添加必要的文件(把工程里红色的文件加进来)
step02:全局替换:把”#include<openssl/“ 替换成 ”#include <“
step03:全局替换:把”# include <openssl/“ 替换成 ”#include <“
步骤五:代码接入到自己的项目