Application invokes openssl for signing using
openssl rsautl -sign -in rasi.bin -inkey riktest.key -out allkiri.bin
How to convert this to .NET 6 so that invoking openssl is not required?
riktest.key is text file containing
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAfddAQEArYjDH7msMFifeYc1AG/TkKpcz2LITI73sC0eqnlgmWi3F7PD
Bo8lWrCw32h3v/FFMrK8KuktlnBtsSLaCCz1DWuXORzHaW7EqG8O8QNzFSmhIoqp
...
This is ASP.NET 6 MVC application. Does .NET 6 System.Security.Cryptography namespace contain this OpenSsl functionality ?
Why generally the native .NET methods cannot be used
For
RSASignaturePadding.Pkcs1
, the native .NET implementationsSignData()
andSignHash()
follow the RSASSA-PKCS1-v1_5 signature scheme described in RFC8017, which applies EMSA-PKCS1-v1_5 as encoding operation: The message is hashed and the following value is signed (i.e. encrypted with the private key):Here PS consists of so many 0xff values that the size of EM is equal to the size of the modulus of the key. T is the DER encoding of the DigestInfo value, which contains the digest OID and the hash, e.g. for SHA256:
where H is the 32 bytes SHA56 hash of the message M to be signed.
In contrast, openssl rsautl uses the RSA algorithm directly, as mentioned in the NOTES section, i.e. the following data is signed:
This cannot be achieved with the native .NET methods in general (except for a special use case, see below):
SignData()
hashes and therefore fails,SignHash()
does not hash but internally (likeSignData()
) generates the DER encoding of the DigestInfo value.An alternative is BouncyCastle, which signs with the algorithm
NoneWithRSA
just like openssl rsautl.One disadvantage of this algorithm is that only short messages can be signed due to the missing hashing, since the length criterion cannot be fulfilled for longer messages (according to which the size of EM' must correspond to the size of the modulus of the key).
Key Import
The posted key is a PEM encoded private key in PKCS#1 format.
.NET supports the import of PEM encoded keys (private/public, PKCS#8/PKCS#1 format) with
ImportFromPem()
since .NET 5, but the import of DER encoded keys has been supported since .NET Core 3.0. A private DER encoded key in PKCS#1 format can be imported withImportRSAPrivateKey()
(the conversion between PEM and DER encoding is trivial and consists of removing header, footer and line breaks and Base64 decoding of the remaining body).BouncyCastle supports the import of a PEM encoded key with the
PemReader
class.A possible implementation of the posted OpenSSL functionality with BouncyCastle
The following code generates the same signature as the OpenSSL statement when rasi.bin holds the data from
dataToSign
and riktest.key holds the key fromprivatePkcs1Pem
:Special use case - when the native C# methods can be used
If rasi.bin contains the DER encoding of the DigestInfo value, BouncyCastle is not needed.
The following example assumes that rasi.bin contains the DER encoding of the DigestInfo value for the message The quick brown fox jumps over the lazy dog with SHA256 as digest. I.e. the last 32 bytes correspond to the SHA256 hash.
A possible implementation with native .NET methods is then:
which gives the same signature, since rasi.bin is identical in both cases.
However, keep in mind that the last approach only works if rasi.bin contains the DER encoding of the DigestInfo value, while the first solution works for arbitrary data in rasi.bin (as long as the length criterion is met).