user1646933
user1646933

Reputation: 97

RSAProvider.ImportParameters() => Bad Data for some special rsa private keys

We have an C#/.Net 4.0 application which imports RSA Private Keys from a PEM file to RSACryptoServiceProvider.

This application works perfectly for RSA-Keys from 512 to 4096 bit, but doesn't with a special kind of rsa private keys.

Here you can find two example keys: test keys

OpenSSL-Command: openssl genrsa -out PRIVATE.KEY 2048

The main difference is the selection of the two primes (p and q). Instead of both are generated with 1024 bits, they are 1023 and 1025 bit in the not-working key. According to the RSA specs this should be fine.

But on ImportParameters() we always get an Cryptographic-Exception with "Bad Data". Any Ideas how to get more Informations, why the private key data is rejected or a way how to get more informations? We searched for informations if there are requirements by microsoft for rsa keys, but couldn't find any useful specs.

Both Keys can be used by OpenSSL and also various smartcards where tested with these private keys.

Here are the byte lengths:

Working-Key:

Not-Working-Key:

Also tried to parse the PEM file with BouncyCastle and export the RsaParameters from it, but they get also rejected with "Bad Data". (Using BouncyCastle instead is not an Option btw. :()

Thanks in Advance!

Upvotes: 2

Views: 4868

Answers (3)

Rodrigo Pereira
Rodrigo Pereira

Reputation: 1

Is better if you use BouncyCastle to do this job. Below,some code to do what using Rsa passing the publicKey to encrypt and privateKey to decrypt. Both keys was generated using BouncyCastle too, as you can see:

class Program
{
    private static string _csr;
    private static string _publicKey;
    private static string _privateKey;

    static void Main(string[] args)
    {
        GeneratePkcs10("braspag.com.br", "braspag", "BR", RootLenght.RootLength2048);
        CryptDecryptTest();

        Console.ReadKey();
    }

    private static void GeneratePkcs10
        (string domainName, string companyName, string countryIso2Characters, RootLenght rootLength)
    {
        try
        {
            var rsaKeyPairGenerator = new RsaKeyPairGenerator();
            var secureRandom = new SecureRandom();

            // Note: the numbers {3, 5, 17, 257 or 65537} as Fermat primes.
            // NIST doesn't allow a public exponent smaller than 65537, since smaller exponents are a problem if they aren't properly padded.
            // Note: the default in openssl is '65537', i.e. 0x10001.
            var genParam = new RsaKeyGenerationParameters
                (BigInteger.ValueOf(0x10001), secureRandom, (int)rootLength, 128);

            rsaKeyPairGenerator.Init(genParam);

            AsymmetricCipherKeyPair pair = rsaKeyPairGenerator.GenerateKeyPair();

            IDictionary attrs = new Hashtable();

            attrs.Add(X509Name.CN, domainName);
            attrs.Add(X509Name.O, companyName);
            attrs.Add(X509Name.C, countryIso2Characters);

            var subject = new X509Name(new ArrayList(attrs.Keys), attrs);

            var textWriter = new StringWriter(CultureInfo.InvariantCulture);
            var pemWriter = new PemWriter(textWriter);
            pemWriter.WriteObject(pair.Private);
            pemWriter.Writer.Flush();
            _privateKey = textWriter.ToString();
            textWriter.Close();

            textWriter = new StringWriter(CultureInfo.InvariantCulture);
            pemWriter = new PemWriter(textWriter);
            pemWriter.WriteObject(pair.Public);
            pemWriter.Writer.Flush();

            _publicKey = textWriter.ToString();
            textWriter.Close();

            ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA256WITHRSA", pair.Private, secureRandom);

            var pkcs10CertificationRequest = new Pkcs10CertificationRequest
                (signatureFactory, subject, pair.Public, null, pair.Private);
            _csr = Convert.ToBase64String(pkcs10CertificationRequest.GetEncoded());
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    private static void CryptDecryptTest()
    {
        const string originalText = "test";

        var encryptedText = RsaEncryptWithPublic(originalText, _publicKey);

        var decryptedText = RsaDecryptWithPrivate(encryptedText, _privateKey);

        Console.WriteLine(string.Format("Original Text: {0}", originalText));
        Console.WriteLine(string.Format("Encrypted Text: {0}", encryptedText));
        Console.WriteLine(string.Format("Decrypted Text: {0}", decryptedText));
    }

    public static string RsaEncryptWithPublic(string clearText, string publicKey)
    {
        var bytesToEncrypt = Encoding.UTF8.GetBytes(clearText);

        var encryptEngine = new Pkcs1Encoding(new RsaEngine());

        using (var txtreader = new StringReader(publicKey))
        {
            var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();

            encryptEngine.Init(true, keyParameter);
        }

        var encrypted = Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length));
        return encrypted;

    }

    public static string RsaDecryptWithPrivate(string base64Input, string privateKey)
    {
        var bytesToDecrypt = Convert.FromBase64String(base64Input);

        AsymmetricCipherKeyPair keyPair;
        var decryptEngine = new Pkcs1Encoding(new RsaEngine());

        using (var txtreader = new StringReader(privateKey))
        {
            keyPair = (AsymmetricCipherKeyPair)new PemReader(txtreader).ReadObject();

            decryptEngine.Init(false, keyPair.Private);
        }

        var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
         return decrypted;
}


    private enum RootLenght
    {

        RootLength2048 = 2048,

        RootLength3072 = 3072,

        RootLength4096 = 4096,

    }
}

Upvotes: 0

mbargiel
mbargiel

Reputation: 305

I had a similar problem. Thanks to you, I noticed some of the buffers had one extra byte than expected. All of those had 0 as the first byte, and a value >= 128 as the second byte. Stripping the 0-prefixes while parsing the keys solved my problem.

Upvotes: 1

user1646933
user1646933

Reputation: 97

Just in case somebody else runs in the same problem. A MSDN-Mod gave me a link with the infos what Microsoft thinks a RSA-Keys should look like.

2.2.2.9.1 RSA Private Key BLOB

P and Q are expected each to be 128 Byte for 2048 bit RSA Keys...

Upvotes: 1

Related Questions