如何在C#中使用PFX证书对请求进行签名?

huangapple go评论89阅读模式
英文:

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

问题

这是您提供的Java代码的翻译版本:

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;
    
    // 对数据进行哈希
    var hash = HashText(text);
    
    // 对哈希值进行签名
    var signature = provider.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
    return Convert.ToBase64String(signature);
}

public static byte[] HashText(string text) 
{
    using (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);
}

请注意,这段C#代码是根据您提供的Java代码进行翻译的,但由于两种语言的编码和库的不同,可能会导致一些微小的差异。如果输出与Java版本不同,您可能需要进一步检查参数、编码和库的使用,以确保在C#中正确地实现了相同的功能。

英文:

I'm trying to make use of unofficial API documentation. I need to sign all of the requests with my own certificate, but the docs came up only with Java code to use, here is it:

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());
}}

I came up with this code

    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);
    }

but the output differs from the Java's version.
I've tried to temporarily run java task from c# so I would know if it even works, and it is.
Is there any way to write this in c#?

答案1

得分: 0

两个代码都使用了RSA签名,使用了Pkcs#1 v1.5填充和SHA1哈希,在Java代码中通过SHA1withRSA指定,在C#代码中则通过RSA#SignHash方法的第二和第三个参数来明确指定。由于用于签名的Pkcs#1 v1.5变体(RSASSA-PKCS1-v1_5)是确定性的,假设要签名的数据和私钥相同,两个代码必须提供相同的签名。

如果不是这种情况,实际上只有两种合理的解释:

  • 首先,是使用的编码:在Java代码中,数据以byte[]形式传输,所以根据发布的代码无法确定使用了哪种编码。相比之下,在C#代码中,数据以字符串形式传递,并在HashText中转换为byte[]。为此,使用了UnicodeEncoding的标准构造函数,该构造函数应用了UTF16LE和字节顺序标记(BOM)。对于UTF8(无BOM),应使用UTF8Encoding。在C#代码中应用与Java代码相同的编码是至关重要的,否则通常会生成不同的签名。
  • 其次,是pfx/p12文件:pfx/p12文件可以包含多个证书。在Java代码中,KeyStore#getKey使用其别名引用私钥(证书也是如此),而在C#代码中,X509Certificate2(Byte[], String, X509KeyStorageFlags)引用容器中的第一个证书,也请参阅这里。为了确保两个代码中引用了相同的证书/密钥,pfx/p12文件可能只包含完全相同的一个证书,以及相应的私钥。

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

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

英文:

Both codes use for the signature RSA with Pkcs#1 v1.5 padding and SHA1, which is specified in the Java code via SHA1withRSA 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) is deterministic, both codes must provide the same signature (assuming the same data to be signed and the same private key).

If this isn't the case, there are actually only two reasonable explanations:

  • First, the encoding used: In the Java code, the data are transferred as byte[], so that it's not possible to determine which encoding is used based on the posted code. In the C# code, in contrast, the data are passed as a string and converted into a byte[] in HashText. For this the standard-ctor of UnicodeEncoding is used, which applies UTF16LE and a byte order mark (BOM). For UTF8 (without BOM) UTF8Encoding would have to be used. It's crucial that the same encoding is applied in the C# code as in the Java code, otherwise different signatures are usually generated.
  • Second, the pfx/p12 file: pfx/p12 files can contain 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. To ensure that the same certificate/key is referenced in both codes, the pfx/p12 file may therefore contain only exactly one certificate including the corresponding private key.

If this is taken into account, both codes generate the same signature on my machine (assuming the same data to be signed and the same pfx/p12 file).

Finally, it should be noted that in both codes cert is used for different objects. In the Java code, it denotes the InputStream, in the C# code the Base64 encoded binary data of the pfx/pf12 file.

huangapple
  • 本文由 发表于 2020年3月15日 13:59:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/60690158.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定