none
RSACryptoServiceProvider.ImportCspBlob

    Question

  • Hi,

    I'm trying to use the public key of my assembly to encrypt a text.
    I'm having this error when I try to encrypt: Key not valid for use in specified state.
    This peace of code explain it self...
    What I want to know is why I have execute the line 17 or 18 to not occour the error. Is this a BUG?


    1    public static void Teste_RSAFromAssembly() 
    2    { 
    3      string clearText = "Teste de criptografia"
    4      byte[] byteText = System.Text.Encoding.Unicode.GetBytes(clearText); 
    5 
    6      //Get assembly publickey --> 160 - 12(header) = 148 bytes 
    7      byte[] cspBlobAssembly = GetCspBlobAssembly(Assembly.GetExecutingAssembly()); 
    8 
    9      //Create the RSA based on the assembly publickey 
    10      RSACryptoServiceProvider rsaAssembly = new RSACryptoServiceProvider(); 
    11      rsaAssembly.ImportCspBlob(cspBlobAssembly); 
    12 
    13      //Try to encrypt - error: "Key not valid for use in specified state." 
    14      byte[] textoCriptografado = rsaAssembly.Encrypt(byteText, false); 
    15 
    16      //Executing one of the two lines below, the encrypt will start to work 
    17      rsaAssembly.ImportParameters(rsaAssembly.ExportParameters(false)); 
    18      rsaAssembly.FromXmlString(rsaAssembly.ToXmlString(false)); 
    19 
    20      //Get CspBlob from RSA 
    21      byte[] cspBlobRSA = rsaAssembly.ExportCspBlob(false); 
    22 
    23      //Compare he sixth byte - only this byte is different 
    24      bool flagEqual = cspBlobRSA[5] == cspBlobAssembly[5]; //flagEqual == false!!! 
    25 
    26      //Try to encrypt - error does not occours any more 
    27      textoCriptografado = rsaAssembly.Encrypt(byteText, false); 
    28    } 
    29 
    30    //Get assembly publickey without first 12 bytes 
    31    private static byte[] GetCspBlobAssembly(Assembly assembly) 
    32    { 
    33      byte[] publicKeyAssembly = assembly.GetName().GetPublicKey(); 
    34      int size = publicKeyAssembly.Length - 12; 
    35      byte[] retorno = new byte[size]; 
    36      Buffer.BlockCopy(publicKeyAssembly, 12, retorno, 0, size); 
    37 
    38      return retorno; 
    39    } 

    Thanks everybody...


    Friday, July 04, 2008 3:39 PM

Answers

  • Hi Marcelo,

    I believe this is the Algorithm ID.  Initially it's set to 00 36 which indicates it's for a signature algorithm, as you might expect on a signed assembly.  After import/export it's 00 164 indicating the key is for key exchange, which I presume is what RSA.Encrtypt requires.
     
    CALG_RSA_KEYX     0x0000a400     RSA public key exchange algorithm. This algorithm is supported by the Microsoft Base Cryptographic Provider.
    CALG_RSA_SIGN     0x00002400     RSA public key signature algorithm. This algorithm is supported by the Microsoft Base Cryptographic Provider.

    from: http://msdn.microsoft.com/en-us/library/aa375549(VS.85).aspx

    The following should allow line 14 to run:

            private static byte[] GetCspBlobAssembly(Assembly assembly)  
            {  
              byte[] publicKeyAssembly = assembly.GetName().GetPublicKey();  
              int size = publicKeyAssembly.Length - 12; 
              byte[] retorno = new byte[size];  
              Buffer.BlockCopy(publicKeyAssembly, 12, retorno, 0, size); 
              retorno[5] = 164; //change algorithm id to key exchange 
              return retorno;  
            } 
     


    Cheers,

    John
    • Proposed as answer by rtizan[0] Sunday, July 06, 2008 6:24 AM
    • Edited by rtizan[0] Sunday, July 06, 2008 6:51 AM code
    • Marked as answer by Zhi-Xin Ye Friday, July 11, 2008 2:44 AM
    Sunday, July 06, 2008 6:24 AM

All replies

  • Hi Marcelo,

    I believe this is the Algorithm ID.  Initially it's set to 00 36 which indicates it's for a signature algorithm, as you might expect on a signed assembly.  After import/export it's 00 164 indicating the key is for key exchange, which I presume is what RSA.Encrtypt requires.
     
    CALG_RSA_KEYX     0x0000a400     RSA public key exchange algorithm. This algorithm is supported by the Microsoft Base Cryptographic Provider.
    CALG_RSA_SIGN     0x00002400     RSA public key signature algorithm. This algorithm is supported by the Microsoft Base Cryptographic Provider.

    from: http://msdn.microsoft.com/en-us/library/aa375549(VS.85).aspx

    The following should allow line 14 to run:

            private static byte[] GetCspBlobAssembly(Assembly assembly)  
            {  
              byte[] publicKeyAssembly = assembly.GetName().GetPublicKey();  
              int size = publicKeyAssembly.Length - 12; 
              byte[] retorno = new byte[size];  
              Buffer.BlockCopy(publicKeyAssembly, 12, retorno, 0, size); 
              retorno[5] = 164; //change algorithm id to key exchange 
              return retorno;  
            } 
     


    Cheers,

    John
    • Proposed as answer by rtizan[0] Sunday, July 06, 2008 6:24 AM
    • Edited by rtizan[0] Sunday, July 06, 2008 6:51 AM code
    • Marked as answer by Zhi-Xin Ye Friday, July 11, 2008 2:44 AM
    Sunday, July 06, 2008 6:24 AM
  • Hi John,

    Thanks for the answer.. but changing the sixth byte, as shown in your code, does not works.
    I think that this error is a bug. I don't beleave it's an algorithm id error (exchange/signature). If you see some properties on line 14 and compare after executing line 17 or 18, they are the same. Some of this properties are:

    rsaAssembly.SignatureAlgorithm
    rsaAssembly.KeyExchangeAlgorithm
    rsaAssembly.CspKeyContainerInfo.RandomlyGenerated
    rsaAssembly.CspKeyContainerInfo.ProviderType
    rsaAssembly.CspKeyContainerInfo.RandomlyGenerated

    If is an algorithm id error, when I export and import the parameters values it should keep in original state.
    It seems to be a BUG. Anyone can help me?

    --
    Marcelo Diniz
    Monday, July 07, 2008 1:29 PM
  • Hi Marcelo,

    In what way does my code not work?  When I ran you code I got exactly the same exception you describe.  When I flipped the bit it runs without an exception.  (See code/output below).

    Here's my theory:

    The primary function of the assembly public key is to sign/verify the assembly and so that's the only 'capability' that's indicated in the rsaParameters.

    As you can see from my code below the unmodified rsaParameters work ok with the rsa.VerifyHash() method.

    If you flip byte5,bit8 then the stated algorithm changes from 'signature' to 'key exchange' and you can now use them for encryption and signing.

    But I agree, these classes are very opaque.  I haven't found any way you look at an rsaCrytptoServiceProvider and tell if will 'allow' encryption.  And as you demonstrated all the export methods provide a value of 0xA4 (key exchange).

    I guess these are limitations of the underlying COM, WinAPI or whatever unmanaged code it's wrapping.

    My explanation could be completely wrong but in either case you could:
    1. Add community content to the online documention explaining/warning about this issue.
    2. Go to connect.microsoft.com/VisualStudio/Feedback if you'd like to report it as a bug.
    Good luck,

    John

    using System;  
    using System.IO;  
    using System.Reflection; 
    using System.Security.Cryptography; 
     
    namespace Green 
        class Grass 
        {         
            static byte[] signature; 
            static byte[] hashOfExe; 
     
            public static void Main() 
            { 
                byte[] exe; 
                using (FileStream fs = new FileStream("Green.exe", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
                { 
                    int len = (int)fs.Length; 
                    exe = new byte[len]; 
                    fs.Read(exe, 0, len); 
                } 
     
                signature = Assembly.GetEntryAssembly().GetName().GetPublicKeyToken(); 
                hashOfExe = SHA1.Create().ComputeHash(exe); 
     
                Console.WriteLine("About to VERIFY SIGNATURE with doChangeAlgId = FALSE"); 
                Teste_RSAFromAssembly_Verify(false); 
                Console.WriteLine("Done"); 
                Console.WriteLine(); 
     
                try 
                { 
                    Console.WriteLine("About to ENCRYPT with doChangeAlgId = FALSE"); 
                    Teste_RSAFromAssembly(false); 
                    Console.Write("Done"); 
                    Console.WriteLine(); 
                } 
                catch 
                { 
                    Console.WriteLine("Oops! - EXCEPTION"); 
                    Console.WriteLine(); 
                } 
     
                Console.WriteLine("About to VERIFY SIGNATURE with doChangeAlgId = TRUE"); 
                Teste_RSAFromAssembly_Verify(true); 
                Console.WriteLine("Done"); 
                Console.WriteLine(); 
     
                Console.WriteLine("About to ENCRYPT with doChangeAlgId = TRUE"); 
                Teste_RSAFromAssembly(true); 
                Console.WriteLine("Done"); 
                Console.WriteLine(); 
     
                Console.ReadKey(); 
            } 
     
            public static void Teste_RSAFromAssembly(bool doChangeAlgId) 
            { 
                string clearText = "Teste de criptografia"
                byte[] byteText = System.Text.Encoding.Unicode.GetBytes(clearText); 
     
                //Get assembly publickey --> 160 - 12(header) = 148 bytes  
                byte[] cspBlobAssembly = GetCspBlobAssembly(Assembly.GetExecutingAssembly(), doChangeAlgId); 
     
                //Create the RSA based on the assembly publickey  
                RSACryptoServiceProvider rsaAssembly = new RSACryptoServiceProvider(); 
                rsaAssembly.ImportCspBlob(cspBlobAssembly); 
     
                //Try to encrypt - error: "Key not valid for use in specified state."  
                byte[] textoCriptografado = rsaAssembly.Encrypt(byteText, false); 
     
                //Executing one of the two lines below, the encrypt will start to work  
                rsaAssembly.ImportParameters(rsaAssembly.ExportParameters(false)); 
                rsaAssembly.FromXmlString(rsaAssembly.ToXmlString(false)); 
     
                //Get CspBlob from RSA  
                byte[] cspBlobRSA = rsaAssembly.ExportCspBlob(false); 
     
                //Compare he sixth byte - only this byte is different  
                bool flagEqual = cspBlobRSA[5] == cspBlobAssembly[5]; //flagEqual == false!!!  
     
                //Try to encrypt - error does not occours any more  
                textoCriptografado = rsaAssembly.Encrypt(byteText, false); 
            } 
     
            public static bool Teste_RSAFromAssembly_Verify(bool doChangeAlgId) 
            { 
                string clearText = "Teste de criptografia"
                byte[] byteText = System.Text.Encoding.Unicode.GetBytes(clearText); 
     
                //Get assembly publickey --> 160 - 12(header) = 148 bytes  
                byte[] cspBlobAssembly = GetCspBlobAssembly(Assembly.GetExecutingAssembly(), doChangeAlgId); 
     
                //Create the RSA based on the assembly publickey  
                RSACryptoServiceProvider rsaAssembly = new RSACryptoServiceProvider(); 
                rsaAssembly.ImportCspBlob(cspBlobAssembly); 
                 
                return rsaAssembly.VerifyHash(hashOfExe, CryptoConfig.MapNameToOID("SHA1"), signature ); 
            } 
     
            private static byte[] GetCspBlobAssembly(Assembly assembly, bool doChangeAlgId) 
            { 
                byte[] publicKeyAssembly = assembly.GetName().GetPublicKey(); 
                int size = publicKeyAssembly.Length - 12; 
                byte[] retorno = new byte[size]; 
                Buffer.BlockCopy(publicKeyAssembly, 12, retorno, 0, size); 
                if (doChangeAlgId) 
                    retorno[5] = (byte)(0x80 | retorno[5]); 
                return retorno; 
            } 
        } 
    // Output: 
    // About to VERIFY SIGNATURE with doChangeAlgId = FALSE 
    // Done 
     
    // About to ENCRYPT with doChangeAlgId = FALSE 
    // Oops! - EXCEPTION 
     
    // About to VERIFY SIGNATURE with doChangeAlgId = TRUE 
    // Done 
     
    // About to ENCRYPT with doChangeAlgId = TRUE 
    // Done 

    • Marked as answer by Marcelo Diniz Wednesday, July 09, 2008 3:50 PM
    • Unmarked as answer by Marcelo Diniz Wednesday, July 09, 2008 3:50 PM
    • Unmarked as answer by Marcelo Diniz Wednesday, July 09, 2008 3:51 PM
    • Unmarked as answer by Marcelo Diniz Wednesday, July 09, 2008 3:51 PM
    Monday, July 07, 2008 5:05 PM
  • John,

    Thanks for your time and explain..
    I submit this item as a bug (as we don't know why exactly this error occours)

    Link: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=355537


    Wednesday, July 09, 2008 3:49 PM