验证通过 java 上的 public 键在 c# 上创建的签名

Authenticating signature created on c# by public key on java

我不熟悉验证和证书等.. 我面临一个问题,我需要在 c# 上签署一条消息,然后在 java 上验证签名,我面临的问题是我无法在 java 上加载 public 密钥在使用 c# 上生成的 Base64 字符串的 (PublicKey) 对象上,我使用以下代码在 c# 端

上生成私有密钥和 public 密钥
 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 侧创建了一个 public 键:

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

请注意,我从 c# 控制台复制了 base64 public 密钥字符串。 当我尝试 运行 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# 上生成私有和 public 密钥的方法(并为 public 密钥生成一个 .cer 文件)以将其加载到 java 端,或者找到一种方法将 base64 public 密钥字符串加载到 java 侧的 (Publickey) 对象中。请帮忙!

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

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

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 中编写解析器。数据是调用CryptExportKey with PUBLICKEYBLOB, making your data layout as described at https://msdn.microsoft.com/en-us/library/ee442238.aspx and https://msdn.microsoft.com/en-us/library/windows/desktop/aa375601(v=vs.85).aspx#pub_BLOB.

的结果

总结:

A header(您可以决定跳过它,或者只是测试它是否等于您期望的固定值):

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

毕竟是相关数据:

  • 模数的bit-length存储为little-endian无符号整数。对于您的 1024 位示例,这将是 1024,又名 0x00000400,又名 { 00 04 00 00 }(LE).
  • public 指数,存储为 little-endian 无符号整数。这几乎总是 0x00010001(又名 { 01 00 01 00 }),但既然它在那里,你应该尊重它。
  • 接下来的 bitLen/8 个字节表示模值。因为这是 public 键,所以应该是 "the rest of the bytes in this array".

选项 3:构建证书

.NET Framework 没有此功能 built-in(从当前版本 4.7 开始)。您可以 P/Invoke 到 CertCreateSelfSignCertificate,但这会涉及很多更改(因为 RSACryptoServiceProvider 不会让您获得密钥句柄,所以您必须 P/Invoke 所有那也是)。

您可以 "bit bang" 自己颁发 DER-encoded 证书。虽然这很有趣,但很难做到正确,而且可能不是一条可行的道路。

如果您可以迁移到 .NET Core,创建证书的功能已通过 CertificateRequest class 添加到 .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;