英文:
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 abyte[]
inHashText
. For this the standard-ctor ofUnicodeEncoding
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论