r and s can be determined most easily with an ASN.1 parser, e.g. with BouncyCastle:
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities.Encoders;
...
byte[] der = Hex.Decode("3044021f4043e112eebe8c92c6aee9132f24af6f73c61353dbd1b1cdde2a5429ba4e290221009caf197c3cf9838e72068097409ee23b6648b1b14d44a38a5825495893dcd629");// 3043021f1acda88726e28dbdfcea74e403e7424ba9cba588777e810e42caa5d274135502207283b90135ef09e5847516a9163f4f6a4d403abb9d7632c77397e37165632619");// 3045022100bb3435689a49cb00a04b64fbd7c886d5c67eb406781b43b4ca18bb778df6d35e02201e54c928da6c4867e2b8aafb7703b00784971ef47837433bdf8a6ecb4cdbebcd");
Asn1Sequence seq = Asn1Sequence.GetInstance(der);
BigInteger[] rsBI = new BigInteger[] {
DerInteger.GetInstance(seq[0]).PositiveValue,
DerInteger.GetInstance(seq[1]).PositiveValue
};
Console.WriteLine("r: " + rsBI[0]); // r: 113546697132886645577275908676387668608557658993333016730332161870441106985
Console.WriteLine("s: " + rsBI[1]); // s: 70870178508439211896524481754448149175401343396061029910903220215172053128745
Instead of BigInteger both parts can also be represented as unsigned byte arrays or hex encoded:
byte[] rBytes = rsBI[0].ToByteArrayUnsigned();
byte[] sBytes = rsBI[1].ToByteArrayUnsigned();
Console.WriteLine("r (hex): " + Hex.ToHexString(rBytes)); // r (hex): 4043e112eebe8c92c6aee9132f24af6f73c61353dbd1b1cdde2a5429ba4e29
Console.WriteLine("s (hex): " + Hex.ToHexString(sBytes)); // s (hex): 9caf197c3cf9838e72068097409ee23b6648b1b14d44a38a5825495893dcd629
For the notation as r|s, r and s must be padded to the private key size (32 bytes in the example) since in general both parts can also be smaller than the key size:
using System.Linq;
...
byte[] rBytes32 = new byte[32 - rBytes.Length].Concat(rBytes).ToArray();
byte[] sBytes32 = new byte[32 - sBytes.Length].Concat(sBytes).ToArray();
byte[] rsBytes64 = rBytes32.Concat(sBytes32).ToArray();
Console.WriteLine("r|s: " + Hex.ToHexString(rsBytes64)); // r|s: 004043e112eebe8c92c6aee9132f24af6f73c61353dbd1b1cdde2a5429ba4e299caf197c3cf9838e72068097409ee23b6648b1b14d44a38a5825495893dcd629
For simplicity, in the last code snippet Linq was applied. But the same can be achieved by copying, e.g. with BlockCopy().
As of .NET5 there is the AsnReader class (and hex encoding is supported natively), so BC is not required.
Edit:
The signature can be loaded as follows:
var signedDataDer = Convert.FromBase64String("MIAGCSqG...");
var signers = new CmsSignedData(signedDataDer).GetSignerInfos().GetSigners();
IEnumerator signersEnumerator = signers.GetEnumerator();
signersEnumerator.MoveNext();
SignerInformation signerInfo = (SignerInformation)signersEnumerator.Current;
byte[] signatureDer = signerInfo.GetSignature();
Now r and s can be determined as described above.