Search code examples
c#.net-corersabouncycastle

Create RSA public key and private key


Im trying to make a SHA1withRSA sign/verify function on ASP.NET core using BouncyCastle library, 1st of all I need to make a keypair, I am using this page to generate the key: https://travistidwell.com/jsencrypt/demo/

However from BouncyCastle example code I found (C# Sign Data with RSA using BouncyCastle), from the Public and Private key text generated, I couldn't make the key files, which as I understand, would be a .CER file for public key and .PEM for private key.

So could you please suggest me a way to make the .CER and .PEM file? Also, I haven't found a complete example about signing SHA1withRSA using BouncyCastle library - or just core C#, I'd be so grateful if you could suggest me a complete example, thank you so much.


Solution

  • The linked website generates private keys in PKCS#1 format and public keys in X.509/SPKI format, each PEM encoded.

    .NET Core only supports the import of PKCS#1 and X.509 keys as of version 3.0. For .NET Core 2.2, the easiest way is to apply BouncyCastle. For loading the PEM keys the PemReader of BouncyCastle can be used.

    Option 1:

    With BouncyCastle's DotNetUtilities class, RSAParameters instances can be derived and thus RSACryptoServiceProvider instances:

    using Org.BouncyCastle.Crypto;
    using Org.BouncyCastle.Crypto.Parameters;
    using Org.BouncyCastle.OpenSsl;
    using Org.BouncyCastle.Security;
    ...
    public static RSACryptoServiceProvider GetRSACryptoServiceProviderFromPem(string pem, bool isPrivate)
    {
        PemReader pemReader = new PemReader(new StringReader(pem));
        object key = pemReader.ReadObject();
    
        RSAParameters rsaParameters = isPrivate ? 
            DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)(((AsymmetricCipherKeyPair)key).Private)) : 
            DotNetUtilities.ToRSAParameters((RsaKeyParameters)key);
    
        RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider();
        rsaKey.ImportParameters(rsaParameters);
    
        return rsaKey;
    }
    

    RSACryptoServiceProvider in turn has methods for signing/verifying:

    RSACryptoServiceProvider privateCSP = GetRSACryptoServiceProviderFromPem(privateKey, true);
    RSACryptoServiceProvider publicCSP = GetRSACryptoServiceProviderFromPem(publicKey, false);
    
    byte[] dataToSign = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
    byte[] signature = privateCSP.SignData(dataToSign, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
    bool verified = publicCSP.VerifyData(dataToSign, signature, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
    

    Option 2:

    Alternatively, the RsaPrivateCrtKeyParameters and the RsaKeyParameters classes of BouncyCastle can be used directly:

    using Org.BouncyCastle.Crypto;
    using Org.BouncyCastle.Crypto.Parameters;
    using Org.BouncyCastle.OpenSsl;
    using Org.BouncyCastle.Security;
    ...
    public static AsymmetricKeyParameter GetAsymmetricKeyParameterFromPem(string pem, bool isPrivate)
    {
        PemReader pemReader = new PemReader(new StringReader(pem));
        object key = pemReader.ReadObject();
    
        return isPrivate ? ((AsymmetricCipherKeyPair)key).Private : (AsymmetricKeyParameter)key;
    }
    

    as well as classes for signing and verifying provided by BouncyCastle's SignerUtilities:

    RsaPrivateCrtKeyParameters privateKeyParameters = (RsaPrivateCrtKeyParameters)GetAsymmetricKeyParameterFromPem(privateKey, true);
    RsaKeyParameters publicKeyParameters = (RsaKeyParameters)GetAsymmetricKeyParameterFromPem(publicKey, false);
    
    ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");
    signer.Init(true, privateKeyParameters);
    byte[] dataToSign = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
    signer.BlockUpdate(dataToSign, 0, dataToSign.Length);
    byte[] signature = signer.GenerateSignature();
    
    signer.Init(false, publicKeyParameters);
    signer.BlockUpdate(dataToSign, 0, dataToSign.Length);
    bool verified = signer.VerifySignature(signature);
    Console.WriteLine(verified);
    

    Both implementations are executable under .NET Core 2.2.