验证通过 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(
0x0000A400
或 0x00002400
)
- 一个整数(存储为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;
我不熟悉验证和证书等.. 我面临一个问题,我需要在 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(
0x0000A400
或0x00002400
) - 一个整数(存储为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;