how to fix CspParameters not supported by Mono exc

2019-03-02 10:20发布

问题:

I try to port ASP .NET MVC2 password recovery code from Where to find C# sample code to implement password recovery in ASP .NET MVC2 answer to Mono. I it contains procedure below. CryptDeriveKey call causes exception in Mono

CspParameters not supported by Mono

How to implement password recovery in Mono in ASP .NET MVC2 application ?

    /// <summary> 
    /// Takes a text message and encrypts it using a password as a key. 
    /// </summary> 
    /// <param name="plainMessage">A text to encrypt.</param> 
    /// <param name="password">The password to encrypt the message with.</param> 
    /// <returns>Encrypted string.</returns> 
    /// <remarks>This method uses TripleDES symmmectric encryption.</remarks> 
    public static string EncodeMessageWithPassword(string plainMessage, string password)
    {
        if (plainMessage == null)
            throw new ArgumentNullException("encryptedMessage", "The message cannot be null");

        TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
        des.IV = new byte[8];

        //Creates the key based on the password and stores it in a byte array. 
        PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, new byte[0]);
        // in mono CryptDeriveKey causes exception:
        // CspParameters not supported by Mono
        des.Key = pdb.CryptDeriveKey("RC2", "MD5", 128, new byte[8]);

        MemoryStream ms = new MemoryStream(plainMessage.Length * 2);
        CryptoStream encStream = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
        byte[] plainBytes = Encoding.UTF8.GetBytes(plainMessage);
        encStream.Write(plainBytes, 0, plainBytes.Length);
        encStream.FlushFinalBlock();
        byte[] encryptedBytes = new byte[ms.Length];
        ms.Position = 0;
        ms.Read(encryptedBytes, 0, (int)ms.Length);
        encStream.Close();

        return Convert.ToBase64String(encryptedBytes);
    }

回答1:

You cannot fix this as the type CspParameters is not really supported by Mono.

The reason is that this type is used to communicate extra information between the (managed) BCL code and the CryptoAPI's CSP (crypto service provider). Since Mono use only managed code and that CryptoAPI is not available outside Windows then the CspParameters class is mostly a stub, a definition without code.

In particular the type PasswordDeriveBytes is a bit special. It implements the standard PKCS#5 v1.5, which Mono supports, but it also adds a few Microsoft extensions (breaking the specification) including one one them being totally broken (security wise). You should take great care when using PasswordDeriveBytes.

The case for CryptDeriveKey is even more badly designed. It has nothing to do with (any version of) PKCS#5, like the rest of PasswordDeriveBytes (i.e. it's not standard-based). What it does is simply redirect your parameters to CryptoAPI using the default CSP. Some majors issues arise form this:

  1. AFAIK Microsoft never published the algorithm(s) they are using to derive keys in their CSP. I can't say if it's even secure, their PKCS#5 extensions were not;

  2. The "default" CSP can be changed (e.g. by applications) to default to a non-Microsoft CSP (e.g. hardward/smartcard CSP). The key derivation algorithm provided by those CSP is unknown (hopefully they call back into MS CSP);

  3. CryptoAPI and the CSP are only available on Windows and differs by the version of Windows/exportability, not the version of the .NET framework.

You should avoid using PasswordDeriveBytes.CryptDeriveKey, even on Windows, unless you can assure the default CSP is (and will remain) identical across all computers running your application.

In conclusion, to avoid interoperability / security issues, I strongly suggest your to use the newer PKCS#5 v2, which Mono/MonoTouch/Microsoft implements as System.Security.Cryptography.Rfc2898DeriveBytes.