通过java上的公钥验证在c#上创建的签名

时间:2017-10-12 10:31:30

标签: java c# x509certificate verify

我是新来的验证和证书等。 我面临一个问题,我需要在c#上签一条消息然后验证java上的签名,我面临的问题是我无法使用生成的Base64字符串在(PublicKey)对象上加载Java上的公钥c#,我使用以下代码在c#side

上生成私钥和公钥
 CspParameters cspParams = new CspParameters { ProviderType = 1 };
            cspParams.KeyContainerName = "MyKeyContainer";
            RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(1024);

            string publicKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
            string privateKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(true));
            System.Diagnostics.Debug.WriteLine("pub:" + publicKey);
            System.Diagnostics.Debug.WriteLine("pri:" + privateKey);

            Console.WriteLine("Key added to container: \n  {0}", rsaProvider.ToXmlString(true));

然后我使用以下代码在Java端创建公钥:

    X509EncodedKeySpec specc = new X509EncodedKeySpec(org.apache.commons.codec.binary.Base64.decodeBase64("BgIAAACkAABSU0ExAAQAAA......"));
            KeyFactory xx = KeyFactory .getInstance("RSA");
            PublicKey ssx=  xx.generatePublic(specc);

请注意,我从c#控制台复制了base64公钥字符串。 当我尝试在java端运行代码时,我得到以下异常:

java.security.spec.InvalidKeySpecException: Inappropriate key specification: invalid key format
at sun.security.provider.DSAKeyFactory.engineGeneratePublic(Unknown Source)
at java.security.KeyFactory.generatePublic(Unknown Source)

我需要找到一种方法来生成c#上的私钥和公钥(并生成公钥的.cer文件)以在java端加载它,或者找到一种方法将base64公钥字符串加载到( java方面的Publickey)对象。请帮忙!

1 个答案:

答案 0 :(得分:0)

选项1:相同的数据,不同的格式。

从.NET传输公共RSA密钥的最简单方法是检查公共指数值是否为{ 01 00 01 },然后发送模数值。在接收方,你接受模数并断言公共指数。

RSAParameters keyParams = rsa.ExportParameters(false);

if (!keyParams.Exponent.SequenceEqual(new byte[] { 0x01, 0x00, 0x01 }))
    throw new InvalidOperationException();

Send(keyParams.Modulus);

然后Creating RSA keys from known parameters in Java表示你可以直接在Java端恢复它。

选项2:格式相同,解析器不同。

您的下一个选择是继续使用CSP blob,但是用Java编写解析器。数据是使用PUBLICKEYBLOB调用CryptExportKey的结果,按https://msdn.microsoft.com/en-us/library/ee442238.aspxhttps://msdn.microsoft.com/en-us/library/windows/desktop/aa375601(v=vs.85).aspx#pub_BLOB所述进行数据布局。

总结:

标题(您可以决定跳过,或者仅测试它等于您期望的固定值):

  • 字节,值0x06PUBLICKEYBLOB
  • 一个字节,值0x02(blob v2)
  • 一个简短的值0x0000(保留)
  • 将密钥标识为RSA(0x0000A4000x00002400
  • 的整数(存储为little-endian)
  • 一个整数(存储为little-endian),将下一个段标识为RSA公钥(有点多余,但技术上现在是一个不同的结构):0x31415352

毕竟有相关数据:

  • 模数的位长存储为little-endian无符号整数。对于您的1024位示例,这将是1024,即0x00000400,又名{ 00 04 00 00 }(LE)。
  • 公共指数,存储为little-endian无符号整数。这几乎总是0x00010001(又名{ 01 00 01 00 }),但是因为它在那里你应该尊重它。
  • 下一个bitLen/8个字节表示模数值。因为这是公钥,应该是#34;这个数组中的其余字节"。

选项3:构建证书

.NET Framework没有内置此功能(截至当前版本,4.7)。你可以P / Invoke到CertCreateSelfSignCertificate,但这会涉及很多变化(因为RSACryptoServiceProvider不会让你获得关键句柄,所以你必须P / Invoke全部那也是。)

你可以" bit bang"自己出DER编码证书。虽然很有趣,但这很难做到,而且可能不是一条可行的道路。

如果您可以迁移到.NET Core,则可以通过CertificateRequest类将创建证书的功能添加到.NET Core 2.0中。对于来自密钥的非常简单的证书:

var certReq = new CertificateRequest(
    "CN=SubjectCN",
    rsaProvider,
    HashAlgorithmName.SHA256,
    RSASignaturePadding.Pkcs1);

// add any extensions you want. I'm not adding any because I said "simple".

DateTimeOffset now = DateTimeOffset.UtcNow;
X509Certificate2 cert = certReq.CreateSelfSigned(now, now.AddMinutes(90));
byte[] xfer = cert.RawData;