如何在 C# 中使用 PFX 证书签署请求?

How to sign an request using PFX certificate in c#?

我正在尝试使用非官方 API 文档。我需要用我自己的证书签署所有请求,但文档只提供了 Java 代码供使用,这里是:

public class EncryptionUtils {
private static final String ALGORITHM_NAME = "SHA1withRSA";
private static final String CERT_TYPE = "pkcs12";
private static final String CONTAINER_NAME = "LoginCert";
private static final String PASSWORD = "CE75EA598C7743AD9B0B7328DED85B06";

public static String signContent(byte[] contents, final InputStream cert) throws IOException, GeneralSecurityException, NullPointerException {
    final KeyStore instance = KeyStore.getInstance(CERT_TYPE);
    instance.load(cert, PASSWORD.toCharArray());
    final PrivateKey privateKey = (PrivateKey) instance.getKey(CONTAINER_NAME, PASSWORD.toCharArray());
    final Signature instance2 = Signature.getInstance(ALGORITHM_NAME);
    instance2.initSign(privateKey);
    instance2.update(contents);
    return Base64.getEncoder().encodeToString(instance2.sign());
}}

我想出了这个代码

    private static string password = "CE75EA598C7743AD9B0B7328DED85B06";
    public static string Sign(string text, string cert)
    {
        X509Certificate2 certificate = new X509Certificate2(DecodeCrt(cert), password, X509KeyStorageFlags.Exportable);
        RSA provider = (RSA)certificate.PrivateKey;
        // Hash the data
        var hash = HashText(text);
        // Sign the hash

        var signature = provider.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
        return Convert.ToBase64String(signature);

    }
    public static byte[] HashText(string text) 
    {
        SHA1Managed sha1Hasher = new SHA1Managed();
        UnicodeEncoding encoding = new UnicodeEncoding();
        byte[] data = encoding.GetBytes(text);
        byte[] hash = sha1Hasher.ComputeHash(data);
        return hash;
    }
    public static byte[] DecodeCrt(string crt)
    {
        return Convert.FromBase64String(crt);
    }

但输出与 Java 的版本不同。 我试过从 c# 临时 运行 java 任务,所以我知道它是否有效,而且确实如此。 有没有办法用c#写这个?

两种代码都用于带有 Pkcs#1 v1.5 填充和 SHA1 的签名 RSA,它在 Java 代码中通过 and in the C# code explicitly in the RSA#SignHash method via the 2nd and 3rd parameter. Since the Pkcs#1 v1.5 variant for signatures (RSASSA-PKCS1-v1_5 指定)是确定性的,两种代码必须提供相同的签名(假设要签名的数据和私钥相同)。

如果不是这样,其实只有两个合理的解释:

  • 首先,使用的编码:在Java代码中,数据传输为byte[],因此无法根据发布的代码确定使用哪种编码。相反,在 C# 代码中,数据作为字符串传递并在 HashText 中转换为 byte[]。为此,必须使用 UnicodeEncoding is used, which applies UTF16LE and a byte order mark (BOM). For UTF8 (without BOM) UTF8Encoding 的标准构造函数。 关键 在 C# 代码中应用与 Java 代码相同的编码,否则通常会生成不同的签名。
  • 其次,pfx/p12文件:pfx/p12个文件可以包含several certificates. In the Java code, KeyStore#getKey references the private key with its alias (the same applies to the certificate), in the C# code, X509Certificate2(Byte[], String, X509KeyStorageFlags) references the first certificate in the container, see also here个。为确保 same certificate/key 在两个代码中都被引用,因此 pfx/p12 文件可能仅包含 exactly one 证书包括对应的私钥。

如果考虑到这一点,两个代码在我的机器上生成相同的签名(假设要签名的数据相同,pfx/p12文件相同)。

最后,需要注意的是,在两个代码中,cert 用于不同的对象。在Java代码中表示InputStream,在C#代码中表示pfx/pf12文件的Base64编码二进制数据。