在java中创建RSA的签名可以使用
java.security.Signature.getInstance("SHA1WithRSA");
创建的示例。从字面上可以看出这个算法在做RSA签名之前要做SHA1的哈希计算,这是因为对于一个秘钥长度不超过1024位的RSA来说,内容长度不能超过117个字节,对于签名来说密文是不需要被解密成明文的,因此可以是用摘要算法。
在这个问题中,生成的私钥和公钥如下命令来生成的,基于openssl:
生成RSA私钥:openssl>genrsa -out rsa_private_key.pem 1024
生成RSA公钥:openssl>rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
用Java语言开发需要将私钥转换成PKCS8格式:openssl>pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt
最后一步把生成的内容保存到rsa_private_pkcs8_key.pem文件中。
因为.NET中提供的RSA算法无法直接使用openssl生成的秘钥,因此这里使用了BouncyCastle的.NET版本(可在nuget中直接安装)
第一步:读取秘钥
BouncyCastle可以读取pkcs8格式的和openssl直接生成的秘钥文件。
openssl直接生成的秘钥内容中包含了一对秘钥(私钥和公钥),读取的时候我们只提取私钥:
using (var reader = new StreamReader(File.OpenRead("D:\\rsa_private_key.pem"))) { var privateKey = (new PemReader(reader).ReadObject() as AsymmetricCipherKeyPair).Private; }
pkcs8的内容中只包含私钥:
using (var reader = new StreamReader(File.OpenRead("D:\\rsa_private_pkcs8_key.pem"))) { var privateKey = new PemReader(reader).ReadObject() as RsaPrivateCrtKeyParameters; }
第二步:加密内容
内容加密的代码和Java中的类似。
public static byte[] Sign(ICipherParameters privateKey, byte[] data, int offset, int length) { var signer = SignerUtilities.GetSigner("SHA1withRSA"); signer.Init(true, privateKey); signer.BlockUpdate(data, offset, length); return signer.GenerateSignature(); }
第三步:读取公钥
这里要提醒一下RSA的基本原理,加密用私钥,解密用公钥。那么对签名进行验证时也要使用公钥,很显然如果使用私钥验证那么私钥就要暴露给对方,这是不满足签名算法的要求的。
这里读取公钥:
using (var reader = new StreamReader(File.OpenRead("D:\\rsa_public_key.pem"))) { var publicKey = new PemReader(reader).ReadObject() as RsaKeyParameters; }
第四步:签名验证
public static bool Verify(ICipherParameters publicKey, byte[] signature, byte[] data, int offset, int length) { var signer = SignerUtilities.GetSigner("SHA1withRSA"); signer.Init(false, publicKey); signer.BlockUpdate(data, offset, length); return signer.VerifySignature(signature); }
多亏了BouncyCastle,让RSA签名在使用的时候变的这么简单。
上面就是对签名验证的步骤,即使使用Java语言生成的签名,使用上面的代码也可以正确验证。