// // System.Security.Cryptography.DSACryptoServiceProvider.cs // // Authors: // Dan Lewis (dihlewis@yahoo.co.uk) // Sebastien Pouliot // Ben Maurer (bmaurer@users.sf.net) // // (C) 2002 // Portions (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) // Portions (C) 2003 Ben Maurer // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System.IO; using System.Globalization; using System.Runtime.InteropServices; using Mono.Security.Cryptography; namespace System.Security.Cryptography { [ComVisible (true)] public sealed class DSACryptoServiceProvider : DSA, ICspAsymmetricAlgorithm { private const int PROV_DSS_DH = 13; // from WinCrypt.h private KeyPairPersistence store; private bool persistKey; private bool persisted; private bool privateKeyExportable = true; private bool m_disposed; private DSAManaged dsa; // MS implementation generates a keypair everytime a new DSA // object is created (unless an existing key container is // specified in the CspParameters). // However we: // (a) often use DSA to import an existing keypair. // (b) take a LOT of time to generate the DSA group // So we'll generate the keypair only when (and if) it's being // used (or exported). This should save us a lot of time (at // least in the unit tests). public DSACryptoServiceProvider () : this (1024) { } public DSACryptoServiceProvider (CspParameters parameters) : this (1024, parameters) { } public DSACryptoServiceProvider (int dwKeySize) { Common (dwKeySize, false); } public DSACryptoServiceProvider (int dwKeySize, CspParameters parameters) { bool has_parameters = parameters != null; Common (dwKeySize, has_parameters); if (has_parameters) Common (parameters); } void Common (int dwKeySize, bool parameters) { LegalKeySizesValue = new KeySizes [1]; LegalKeySizesValue [0] = new KeySizes (512, 1024, 64); // will throw an exception is key size isn't supported KeySize = dwKeySize; dsa = new DSAManaged (dwKeySize); dsa.KeyGenerated += new DSAManaged.KeyGeneratedEventHandler (OnKeyGenerated); persistKey = parameters; if (parameters) return; var p = new CspParameters (PROV_DSS_DH); if (useMachineKeyStore) p.Flags |= CspProviderFlags.UseMachineKeyStore; store = new KeyPairPersistence (p); // no need to load - it cannot exists } void Common (CspParameters parameters) { store = new KeyPairPersistence (parameters); store.Load (); if (store.KeyValue != null) { persisted = true; this.FromXmlString (store.KeyValue); } privateKeyExportable = (parameters.Flags & CspProviderFlags.UseNonExportableKey) == 0; } ~DSACryptoServiceProvider () { Dispose (false); } // DSA isn't used for key exchange public override string KeyExchangeAlgorithm { get { return null; } } public override int KeySize { get { return dsa.KeySize; } } public bool PersistKeyInCsp { get { return persistKey; } set { persistKey = value; } } [ComVisible (false)] public bool PublicOnly { get { return dsa.PublicOnly; } } public override string SignatureAlgorithm { get { return "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; } } private static bool useMachineKeyStore; public static bool UseMachineKeyStore { get { return useMachineKeyStore; } set { useMachineKeyStore = value; } } public override DSAParameters ExportParameters (bool includePrivateParameters) { if ((includePrivateParameters) && (!privateKeyExportable)) { throw new CryptographicException ( Locale.GetText ("Cannot export private key")); } return dsa.ExportParameters (includePrivateParameters); } public override void ImportParameters (DSAParameters parameters) { dsa.ImportParameters (parameters); } public override byte[] CreateSignature (byte[] rgbHash) { return dsa.CreateSignature (rgbHash); } public byte[] SignData (byte[] buffer) { // right now only SHA1 is supported by FIPS186-2 HashAlgorithm hash = SHA1.Create (); byte[] toBeSigned = hash.ComputeHash (buffer); return dsa.CreateSignature (toBeSigned); } public byte[] SignData (byte[] buffer, int offset, int count) { // right now only SHA1 is supported by FIPS186-2 HashAlgorithm hash = SHA1.Create (); byte[] toBeSigned = hash.ComputeHash (buffer, offset, count); return dsa.CreateSignature (toBeSigned); } public byte[] SignData (Stream inputStream) { // right now only SHA1 is supported by FIPS186-2 HashAlgorithm hash = SHA1.Create (); byte[] toBeSigned = hash.ComputeHash (inputStream); return dsa.CreateSignature (toBeSigned); } public byte[] SignHash (byte[] rgbHash, string str) { // right now only SHA1 is supported by FIPS186-2 if (String.Compare (str, "SHA1", true, CultureInfo.InvariantCulture) != 0) { // not documented throw new CryptographicException (Locale.GetText ("Only SHA1 is supported.")); } return dsa.CreateSignature (rgbHash); } public bool VerifyData (byte[] rgbData, byte[] rgbSignature) { // right now only SHA1 is supported by FIPS186-2 HashAlgorithm hash = SHA1.Create(); byte[] toBeVerified = hash.ComputeHash (rgbData); return dsa.VerifySignature (toBeVerified, rgbSignature); } // LAMESPEC: MD5 isn't allowed with DSA public bool VerifyHash (byte[] rgbHash, string str, byte[] rgbSignature) { if (str == null) str = "SHA1"; // default value if (String.Compare (str, "SHA1", true, CultureInfo.InvariantCulture) != 0) { throw new CryptographicException (Locale.GetText ("Only SHA1 is supported.")); } return dsa.VerifySignature (rgbHash, rgbSignature); } public override bool VerifySignature (byte[] rgbHash, byte[] rgbSignature) { return dsa.VerifySignature (rgbHash, rgbSignature); } protected override byte[] HashData (byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) { if (hashAlgorithm != HashAlgorithmName.SHA1) { throw new CryptographicException(Environment.GetResourceString("Cryptography_UnknownHashAlgorithm", hashAlgorithm.Name)); } var hash = HashAlgorithm.Create (hashAlgorithm.Name); return hash.ComputeHash (data, offset, count); } protected override byte[] HashData (System.IO.Stream data, HashAlgorithmName hashAlgorithm) { if (hashAlgorithm != HashAlgorithmName.SHA1) { throw new CryptographicException(Environment.GetResourceString("Cryptography_UnknownHashAlgorithm", hashAlgorithm.Name)); } var hash = HashAlgorithm.Create (hashAlgorithm.Name); return hash.ComputeHash (data); } protected override void Dispose (bool disposing) { if (!m_disposed) { // the key is persisted and we do not want it persisted if ((persisted) && (!persistKey)) { store.Remove (); // delete the container } if (dsa != null) dsa.Clear (); // call base class // no need as they all are abstract before us m_disposed = true; } } // private stuff private void OnKeyGenerated (object sender, EventArgs e) { // the key isn't persisted and we want it persisted if ((persistKey) && (!persisted)) { // save the current keypair store.KeyValue = this.ToXmlString (!dsa.PublicOnly); store.Save (); persisted = true; } } // ICspAsymmetricAlgorithm [MonoTODO ("call into KeyPairPersistence to get details")] [ComVisible (false)] public CspKeyContainerInfo CspKeyContainerInfo { get { return null; } } [ComVisible (false)] public byte[] ExportCspBlob (bool includePrivateParameters) { byte[] blob = null; if (includePrivateParameters) blob = CryptoConvert.ToCapiPrivateKeyBlob (this); else blob = CryptoConvert.ToCapiPublicKeyBlob (this); return blob; } [ComVisible (false)] public void ImportCspBlob (byte[] keyBlob) { if (keyBlob == null) throw new ArgumentNullException ("keyBlob"); DSA dsa = CryptoConvert.FromCapiKeyBlobDSA (keyBlob); if (dsa is DSACryptoServiceProvider) { DSAParameters dsap = dsa.ExportParameters (!(dsa as DSACryptoServiceProvider).PublicOnly); ImportParameters (dsap); } else { // we can't know from DSA if the private key is available try { // so we try it... DSAParameters dsap = dsa.ExportParameters (true); ImportParameters (dsap); } catch { // and fall back DSAParameters dsap = dsa.ExportParameters (false); ImportParameters (dsap); } } } } }