.NET

in

Samples

 

Jan Šeda

jan.seda@skilldrive.com


1.  Foreword

Learning and using technologies is sometimes very boring and reading books takes too much time. Many developers use MSDN but there is a big issue - that there are too many articles and other sources that this huge quantity is not possible to absorb and confusing (maybe this is the reason why Russian search engine started a special indexer on MSDN itself, see http://msdn.rambler.ru). This is the reason why I don’t like reading technical books or MSDN articles like they would be bestsellers and searching on MSDN is terrifying experience at least for me).

That is why in December 2003 I have decided to write my own book (just for personal usage) with samples, descriptions and explanation of technologies – just short samples and many images where principles could be seen immediately so learning curve could be as short as possible. Later I’ve provided this book to my friends and they told me that it can be useful for other developers who want to learn fast and see results in a very short time.

 

So far I have been writing samples on „as-needed“ basis, many chapters are unfinished and cover specific topic just basically. Also my English translation has not being checked by a professional translator and I want to excuse myself for not being able to write perfect English expressions but I hope this book will be helpful to developers.


2.  Terms of Use

© 2004-2005 by Jan Šeda, Skilldrive

All rights reserved. Information in this document, including URL and other Internet Web site references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, people and events depicted herein are fictitious and no association with any real company, organization, product, person or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of the author.

The information in this book is distributed on an “as is” basis, without warranty. While every precaution has been taken in the preparation of this book, the author shall not have any liability to any person or entitle with respect to any liability, loss or damage caused or alleged to be caused directly or indirectly by instructions contained in this book or by the computer software or hardware products described herein.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does o

Active Directory, ActiveX, Authenticode, BizTalk, DirectX, IntelliSense, JScript, Microsoft, MSDN, Visual Basic, Visual C++, Visual J++, Visual SourceSafe, Visual Studio, Windows, Windows Media, Windows NT and Windows Server are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries.

All other product names and company names mentioned herein are the property of their respective owners.


Contents

1.      Foreword. 2

2.      Terms of Use. 3

3.      Windows Security. 12

3.1. Basic terms. 12

3.1.1. Principal 12

3.1.2. Authority. 12

3.1.3. Authentication. 12

3.1.4. Authorization. 12

3.1.5. Trust 13

3.1.6. Logon Session. 14

3.1.7. Token. 15

3.1.8. Get SID for current identity. 17

3.1.9. Get object name for SID.. 19

3.2. Protecting system resources. 21

3.2.1. Test yourself on security & protection of system resources. 21

3.2.2. Rules behind propagation of rights on objects. 22

3.2.3. Get ACLs/ACEs for a file. 23

3.2.4. Set DACL for a file. 24

4.      Security Ratings. 25

4.1.1. What is a Common Criteria?. 26

4.1.2. Why is Common Criteria important?. 26

5.      Security Concepts in .NET environment 26

5.1. Basic layout of .NET Framework – Security parts. 26

5.2. Assembly. 27

5.2.1. Runtime security policy. 28

5.2.2. Types of security context for assemblies. 31

5.2.3. Generate key pair with sn.exe tool 32

5.2.4. Give an assembly a strong name. 32

5.2.5. Delayed signing of assembly. 32

5.2.6. List of permissions in policy levels. 33

5.2.7. List of permissions assign to current assembly. 34

5.2.8. Get permission list for a custom evidence. 35

5.2.9. List of declarative permissions of assembly. 36

5.2.10. Output assembly evidence list to XML file. 36

5.2.11. List policy levels and code groups where current assembly belongs. 37

5.3. Type safety, metadata and code verification. 38

5.3.1. Get info about types in assembly. 40

5.4. Application domains. 42

5.4.1. Application domain boundaries and objects. 42

5.4.2. Create application domain programmatically. 44

5.4.3. Shadow copy enabled for application domain. 45

5.5. Security tools available in .NET.. 45

5.6. Code Access Security. 47

5.6.1. Stack-walk. 47

5.6.2. Limit access permissions for a method. 49

5.6.3. Add new code group to runtime security. 50

5.7. Role-based Security. 51

5.7.1. Identity classes (also Whidbey) 51

5.7.2. Principal policy. 51

5.7.3. Principal classes. 54

5.7.4. Using GenericPrincipal class. 54

5.7.5. Get list of groups for current thread’s identity. 55

5.7.6. Get current user name. 56

5.7.7. Impersonate as another user 56

5.7.8. Declarative principal permissions for Windows roles. 58

5.7.9. Declarative principal permissions for custom roles. 59

5.7.10. List running processes and user accounts. 59

6.      Cryptography & Security. 61

6.1. Buffer Overrun. 61

6.1.1. CodeRed Worm, Buffer Overrun attack. 62

6.1.2. SQLSlammer 63

6.2. Algorithms for Encryption. 63

6.2.1. Well Known Algorithms for Symmetric Encryption. 63

6.2.2. Well Known Algorithms for Asymmetric Encryption. 63

6.2.3. Well Known Hash Algorithms. 64

6.3. Digital Certificates. 64

6.4. Secure Communication Standards. 64

6.4.1. IPSec (Internet Protocol Security) 64

6.4.2. Kerberos. 64

6.4.3. SSL (Secure Socket Layer) 64

7.      Cryptography. 68

7.1. Basic terms in cryptography. 68

7.2. A little bit of history. 69

7.2.1. Caesar cipher 69

7.2.2. Progress in cryptography. 71

7.3. PKCS. 72

7.4. CMV (Cryptographic Module validation) 73

7.4.1. Microsoft FIPS 140 certification. 74

7.4.2. .NET classes and FIPS 140. 74

7.5. Cryptography in .NET.. 74

7.6. Configuring .NET cryptography. 75

7.7. Win32 Security API and .NET.. 75

7.8. Random number generators. 76

7.8.1. Generating random values. 76

7.8.2. Generating random nonzero values. 76

7.8.3. Random number generator and other CSPs (Cryptographic Service Provider) 76

7.9. Hashing algorithms. 77

7.10. Symmetric encryption. 78

7.10.1. Block ciphers. 79

7.10.2. Stream ciphers. 79

7.10.3. Key distribution problem.. 80

7.10.4. Data Encryption Standard (DES) 80

7.10.5. Blowfish. 86

7.10.6. Twofish. 86

7.10.7. MARS. 86

7.10.8. Rijndael 87

7.10.9. Ronald Rivest’s (RC) ciphers. 87

7.10.10. Hash value using MD5 and SHA.. 87

7.10.11. Collision in MD5 algorithm.. 88

7.10.12. Classes for symmetric algorithms in .NET.. 90

7.10.13. Deriving symmetric keys from passwords. 90

7.10.14. Creating symmetric encryption classes. 91

7.10.15. Symmetric encryption/decryption of plaintext using DES. 92

7.10.16. Symmetric encryption/decryption of plaintext using RC2. 93

7.10.17. Symmetric encryption/decryption of plaintext using Rijndael 93

7.10.18. Determining weak and semi-weak keys in DES. 94

7.10.19. Deriving symmetric key from password using PBKDF1. 95

7.10.20. Deriving symmetric key & IV from a password using PBKDF1. 95

7.10.21. Deriving symmetric key from a password using PBKDF2. 96

7.10.22. Check valid key size for symmetric encryption. 96

7.10.23. Hybrid usage of symmetric and asymmetric encryption. 97

7.10.24. Hashing of plaintext and encryption/decryption using DES. 98

7.10.25. Keyed hash algorithm HMACSHA1. 99

7.10.26. Keyed hash algorithm MACTripleDES. 100

7.11. Asymmetric encryption. 100

7.11.1. Certificates & Certification authorities. 100

7.12. Assymetric encryption. 101

7.12.1. Classes for asymmetric algorithms in .NET.. 102

7.12.2. Storing public and private RSA keys in XML file. 102

7.12.3. Storing keys by CSP (Crypto Service Provider) 102

7.12.4. Encryption of plaintext using RSA with XML-stored key. 103

7.12.5. Encryption/decryption of plaintext using RSA.. 103

7.12.6. Encryption/decryption of plaintext using RSA with XML-stored key. 104

7.12.7. Encryption of plaintext using RSAParameters. 105

7.12.8. Encryption/Decryption of plaintext by RSA.. 106

7.12.9. Encryption with public key (exception) 106

7.12.10. How to encrypt/decrypt large data using RSA?. 107

7.12.11. Calling RSA/DSA from a Web service, ASP or COM+. 107

7.13. Digital signatures. 108

7.13.1. Sign and verify data with RSA I 108

7.13.2. Sign and verify data with RSA II 110

7.13.3. Sign and verify data with RSA using SignatureFormatter 110

7.13.4. Sign and verify data with DSA.. 111

7.14. Key exchange methods and classes. 112

7.14.1. Exchange symmetric key between two clients using OAEP. 112

7.15. Certificates. 114

7.15.1. Create X509Certificate from file generated by makecert.exe. 114

7.15.2. Create X.509 certificate from base64 encoded certificates. 114

7.15.3. Source library with CryptoAPI certificate mappings. 115

7.15.4. List of installed client’s certificates. 115

7.15.5. List of installed intermediate certification authorities. 116

7.15.6. List of installed root certificate authorities. 116

7.16. Data Protection API 116

7.17. Basic principles of DPAPI 118

7.17.1. User’s profile. 120

7.17.2. Source library with DPAPI methods. 121

7.17.3. Use DPAPI to encipher application data into file. 127

7.17.4. Use DPAPI to decipher application data from file. 127

7.17.5. DPAPI used to encrypt data in file in isolated storage. 128

7.17.6. DPAPI used to decrypt data from file in isolated storage. 129

7.17.7. Encrypt/Decrypt database connection string using DPAPI 130

7.17.8. Issues with user’s store and web services and COM+. 131

7.17.9. Managed DPAPI 131

7.18. XML Signatures. 132

7.18.1. Sign XML. 132

7.19. Isolated storage. 132

7.19.1. Storeadm.exe – administration of isolated storage in .NET.. 134

7.19.2. Opening of isolated storages for current user and domain. 135

7.19.3. Store data in file in isolated storage. 136

8.      Network Operations. 137

8.1.1. Retrieve DNS computer name. 137

8.1.2. Retrieve NetBIOS computer name. 137

8.1.3. Obtain IP address and host 137

8.1.4. Send email in .NET environment 137

8.1.5. Getting online stock information. 138

8.1.6. Retrieve email from POP3 mail server 139

9.      File operations. 140

9.1. General IO operations. 140

9.1.1. Get executing application’s path with reflection. 140

9.1.2. Get executing application’s path. 140

9.1.3. Classes working with file and directory information. 141

9.1.4. Change file & folder attributes. 141

9.1.5. Recursive list of directories/subdirectories & files. 142

9.2. Reading and writing from/to files. 142

9.2.1. BufferedStream.. 143

9.2.2. Read from file using BufferedStream.. 143

9.2.3. Read text from file. 144

9.2.4. Write text to file. 144

9.2.5. Create file and write to it 144

9.2.6. Append text to file. 145

9.2.7. Read from binary file. 145

9.2.8. Write to binary file. 146

9.2.9. Watch file system for changes. 146

10.        Text Manipulation & Internationalization. 147

10.1. String operations. 147

10.1.1. Append string. 147

10.1.2. Inserting/Removing string. 148

10.1.3. Replace string. 148

10.1.4. Reverse string. 148

10.1.5. Reverse string using recursion. 149

10.2. Formatting numbers. 149

10.2.1. Table with number formatting options. 149

10.2.2. Formatting of numeric values to currency. 150

10.2.3. Formatting of numeric values to currency with NumberFormatInfo. 150

10.2.4. Formatting of floating point values to a scientific notation (exponential) 151

10.2.5. Formatting of floating point values to specific number of decimals (fixed-point) 151

10.2.6. Formatting of numeric value to local culture specific number 151

10.2.7. Formatting of floating point value to roundtrip (can be converted back to number) 151

10.2.8. Formatting of an integer value to a hexadecimal number 152

10.2.9. Formatting floating point values to a percentage. 152

10.2.10. Formatting floating point values to a percentage with limited number of decimals. 152

10.2.11. Formatting of floating point values to a percentage with NumberFormatInfo. 152

10.3. Formatting date and time. 153

10.3.1. Table with date&time formatting options. 153

10.3.2. Formatting DateTime to the short date&time pattern (dddd, MMMM dd, yyyy, hh:mm) 154

10.3.3. Formatting DateTime to the full date&time pattern (dddd, MMMM dd, yyyy hh:mm:ss) 154

10.3.4. Formating DateTime to the short date numerical pattern (M/d/yyyy) 154

10.3.5. Formatting DateTime to the full date numerical pattern (dddd, MMMM dd, yyyy) 154

10.3.6. Formatting DateTime to the short date&time numerical pattern (M/d/yyyy hh:mm) 154

10.3.7. Formatting DateTime to the full date&time numerical pattern (M/d/yyyy hh:mm:ss) 155

10.3.8. Formatting DateTime to the month name pattern (MMMM dd) 155

10.3.9. Formatting DateTime to the short date pattern (MMMM, yyyy) 155

10.3.10. Formatting DateTime to the long time pattern (hh:mm:ss) 155

10.3.11. Formatting DateTime to the short time pattern (hh:mm) 155

10.3.12. Formatting DateTime to the RFC1123 pattern (ddd, dd MMM yyyy HH':'mm':'ss 'GMT') 156

10.3.13. Formatting DateTime to sortable pattern. 156

10.3.14. Formatting DateTime to universal sortable pattern (yyyy'-'MM'-'dd HH':'mm':'ss'Z') 156

10.3.15. Formatting DateTime to full date&time using universal time. 156

10.3.16. Formatting DateTime to custom format using DateTimeFormatInfo. 156

10.4. Custom number formatting. 157

10.4.1. Formatting of number to specific number of decimals. 158

10.4.2. Formatting of number with adding zeros. 158

10.4.3. Formatting of number to custom positive, negative and zero sections. 158

10.4.4. Formatting of number using custom CultureInfo and custom format 159

10.5. Formatting strings. 159

10.5.1. Simple string formatting with number parameter 159

10.6. Conversions. 160

10.6.1. Class Convert (many convertion methods) 160

10.6.2. Convert string to integer 160

10.6.3. Convert string to double. 160

10.6.4. Convert string to double using CultureInfo. 161

10.6.5. Convert string to date. 161

10.6.6. Use regular expression to find and replace string inside of string. 161

10.6.7. Converting string to DateTime using CultureInfo. 162

10.6.8. Convert time_t to DateTime. 163

10.6.9. Convert time_t to DateTime (shorter code) 163

10.6.10. Convert base64 encoded number to float 163

10.6.11. Convert file1/encoding1 into file2/encoding2. 164

10.7. Internationalization. 165

10.7.1. American Standard Code for Information Interchange (ASCII) 165

10.7.2. ISO 10646 & Universal Character Set 166

10.7.3. Unicode. 166

10.7.4. Class CultureInfo. 167

11.        Collections. 169

11.1.1. ArrayList 169

11.1.2. BitArray. 169

11.1.3. HashTable. 170

11.1.4. Queue. 170

11.1.5. SortedList 171

11.1.6. Stack. 171

12.        Time Operations. 172

12.1.1. Time measuring (TickCount and Ticks property) 172

12.1.2. Accurate time measuring. 172

13.        Windows Management Instrumentation (WMI) 173

13.1. CIM Schema. 174

13.2. WMI Architecture. 175

13.3. WMI tools. 175

13.3.1. WMI Object Browser 175

13.3.2. WMI CIM Studio. 176

13.3.3. WMI Event Registration Tool 177

13.3.4. WMI Event Viewer 177

13.4. WMI plug-in for Visual Studio .NET 2003. 177

13.5. List of WMI Classes. 178

13.5.1. Working with WMI on remote machine. 178

13.5.2. Get computer info (domain, model etc.) 179

13.5.3. Get computer info (vendor, UUID, type) 179

13.5.4. Get data about operating system.. 180

13.5.5. Logoff, shutdown, reboot computer 184

13.5.6. Get user’s desktop info. 186

13.5.7. Determine computer type (workstation, server, controller etc.) 187

13.5.8. Determine physical computer features. 187

13.5.9. Rename computer name. 190

13.5.10. Get processor info. 191

13.5.11. Get memory info. 199

13.5.12. Getting list of file shares on local machine. 200

13.5.13. Get logical disk info. 200

13.5.14. Get environment variables. 201

13.5.15. Get CD-ROM/DVD information. 201

13.5.16. Get boot configuration. 204

13.5.17. Find a service by its name. 205

13.5.18. Get list of running/stopped services. 205

13.5.19. Getting partition info. 206

13.5.20. Get list of user’s account from local machine/domain. 207

13.5.21. Get list of user groups from local machine/domain. 209

13.5.22. Get list of installed codec files. 210

13.6. Watching for event 211

13.6.1. Watching for newly started processes. 211

14.        XML.. 213

14.1. What is SGML?. 213

14.2. What is XML?. 213

14.3. What is XHTML?. 213

14.4. Forward-only reading and writing XML. 214

14.5. XmlTextReader 215

14.5.1. XML file “Sample.xml” used in following samples. 215

14.5.2. What is a XML schema?. 216

14.5.3. XSD file “Sample.xsd” used in following samples. 216

14.5.4. Load and read XML from URL. 217

14.5.5. Load and read XML from file. 217

14.5.6. Load and read XML from memory-stored data. 218

14.5.7. Handle whitespaces in XML. 218

14.5.8. Read specific attribute in XML. 219

14.5.9. Step over attributes in XML. 219

14.5.10. Write string data to XML file. 220

14.5.11. Write characters to XML file. 220

14.5.12. Write comments to XML file. 221

14.5.13. Write processing instructions to XML file. 221

14.5.14. Write attributes to XML file. 221

14.5.15. What is it a XML namespace?. 222

14.5.16. Write namespace to XML file. 222

14.5.17. Write namespace with prefix to XML file. 223

14.5.18. Set format options when writing to XML file. 223

14.5.19. Set a single quote as formatting option for XML file. 224

14.6. Document Object Model (DOM) 224

14.6.1. What is a XML document?. 224

14.6.2. Open XML document from URL. 224

14.6.3. Open XML document from file. 225

14.6.4. Open XML document with memory-stored data. 225

14.6.5. Insert nodes into XML document 225

14.6.6. Finding nodes by their names. 226

14.6.7. XPath classes in .NET 1.1. 227

14.6.8. Quering XML using XPath. 227

14.6.9. Sum attribute values using XPath expression. 228

14.6.10. List of XPath axes. 228

14.6.11. What is DTD?. 229

14.6.12. Validate XML against XSD (Schema) 229

14.6.13. Validate XML against DTD.. 230

14.7. Extensible Stylesheet Language for Transformation (XSLT) 231

14.8. XML Encryption. 231

15.        Computer environment 231

15.1.1. Local computer environment properties. 231

15.1.2. Creating shortcut in special folders (Desktop, StartMenu, Startup) 232

15.1.3. Determine actual system power status. 233

15.1.4. Enumerate installed printers on local machine. 235

15.1.5. Set default printer on local machine. 235

15.1.6. Enumerate network drives. 235

15.1.7. Integration with Windows (Help, Shotdown, Suspend, Control Panels) 236

15.1.8. Open Control Panel items. 237

15.1.9. Get folder items using Windows folder dialog. 238

16.        Other features. 238

16.1.1. Get string resource from dll library. 238

16.1.2. Handle events from other applications. 240

16.1.3. Beep in application. 240

16.1.4. Beep in application in Whidbey. 241

16.1.5. Programming access to attributes. 241

16.1.6. Get full-path & name of current process. 242

16.1.7. Programmatically create virtual website in IIS. 242

16.1.8. Get topmost window title using Win32 API 242

17.        ADO.NET.. 243

17.1. Architecture of ADO.NET.. 243

17.1.1. Connecting to SQL Server, Oracle, MySQL and others. 246

17.1.2. Watching connection state events and messages. 247

17.1.3. Executing SQL command and reading data in SqlDataReader 248

17.1.4. Executing stored procedure and reading data in SqlDataReader 249

17.1.5. Executing multiple SQL statements (batch) 249

17.1.6. Executing stored procedure and reading data from multiple result sets in SqlDataReader 250

17.1.7. Executing stored procedure and getting data in DataSet 251

17.1.8. Updating database data with changes in DataSet 253

17.1.9. Accessing Excel data using ADO.NET.. 254

17.1.10. List available SQL servers. 254

18.        ADO.NET & System.Xml 2.0 (Whidbey) 255

18.1. Summary of new features in ADO.NET 2.0. 255

18.1.1. Asynchronous Data Access. 255

18.1.2. Batch Updates. 255

18.1.3. DataSet Performance. 255

18.1.4. MARS (Multiple Active Results Sets) 255

18.2. Summary of new features in System.Xml 255

19.        Appendix A - Fast-track to C# language. 256

19.1. Basic terms and definitions in .NET & C#. 256

19.2. What is C#?. 257

19.3. Hello world. 257

19.4. Assemblies. 258

19.4.1. Locating of assemblies. 258

19.4.2. Assembly layout 258

19.5. Identifiers. 258

19.6. Types. 259

19.6.1. Hierarchy of types. 259

19.6.2. Predefined types. 260

19.6.3. Integral types. 260

19.6.4. Floating-point types. 262

19.6.5. Decimal type. 262

19.6.6. Bool type. 262

19.6.7. Object type. 262

19.6.8. String type. 263

19.6.9. Implicit conversions of numeric values. 263

19.6.10. Boxing and unboxing. 263

19.7. Variables & parameters. 264

19.7.1. Types of variables & parameters. 264

19.7.2. Default values. 266

19.7.3. Enum.. 267

19.7.4. Struct 267

19.8. Expressions & Operators. 269

19.8.1. Operators. 269

19.8.2. Overflow check operators. 270

19.8.3. Operator typeof 270

19.8.4. Operator is. 271

19.8.5. Operator overloading. 271

19.9. Preprocesor 272

19.10. Statements. 274

19.11. C# namespaces. 277

19.12. Exceptions & exception handling. 279

19.12.1. Throwing exceptions. 280

19.12.2. Exception classes. 280

19.12.3. Monitoring of exception performance. 281

19.12.4. Checked & unchecked exceptions. 282

19.13. Delegates and events. 283

19.13.1. Simple delegate usage. 283

19.13.2. Delegate declaration. 284

19.13.3. Delegate instanciation. 284

19.13.4. Final code with delegates. 284

19.13.5. Multicast delegates usage. 286

19.13.6. Final code with multicast delegates. 286

19.13.7. Event usage. 288

19.13.8. Final code with events. 288

19.14. Attributes. 289

19.14.1. Predefined attributes. 289

19.15. Multithreading & synchronization. 290

19.15.1. Semaphores & mutexes. 291

19.15.2. Thread architecture. 292

19.15.3. Multithreading in C#. 292

19.15.4. Race condition & deadlock. 294

19.15.5. Lock statement 295

19.16. Garbage Collection. 295

19.16.1. Collection of memory space. 295

19.16.2. Garbage Collector’s methods explained. 297

19.16.3. Hotspot JVM... 298

19.17. Unsafe code. 299

20.        C# version 2.0. 300

20.1. Partial types. 301

21.        Alphabetical bibliography. 302

21.1. Security & Cryptography. 302

21.2. .NET Environment 302

21.3. Interop. 303

21.4. Others. 303

 


3.  Windows Security

The Windows Security is very important to understand to see other principles in .NET because .NET security stand above Windows security. Also till Whidbey many security concepts are provided just in unmanaged environment and many Win32 methods must be wrapped into the .NET environment (they are not provided in .NET framework 1.1 so far).

3.1. Basic terms

In this section are described some of the basic terms illustrated on a figure below:

3.1.1. Principal

Entity that can be authenticated.

3.1.2. Authority

Entity authenticating principals and managing principals.

3.1.3. Authentication

Process when principal proves its identity. Who am I?

3.1.4. Authorization

Process when principal receives its rights to access specific protected resources. What can I do?

3.1.5. Trust

Trust in authority that it is able to authenticate principals.

3.1.5.1. Windows LSA Trust

When dealing with local Windows accounts then we must trust to LSA that authentication works well and user’s can be authenticated. Local security is very specifics and has many issues generally in all operating systems because of principal reasons.

3.1.5.2. Windows Domain Trust

Today’s world requires many ways of trust like in Windows trust is used as term when connecting domains and establishing some level of trust between them.

3.1.5.3. CA Trust

Trust in cryptography (not just OS security like in previous chapters) is very important concept and without it working with public keys would be impossible. That is why there must be one entity which we can trust and we derive our way of trust to other entities derived from first one (root).

3.1.6. Logon Session

Logon Session is created when principal gets authorized and when operating system assigns rights to him. For developers logon session are abstract concept (even when they are physically implemented in Windows) but they can be reached through tokens. Also very important is to understand difference between logon sessions because they are the cause of many problems developers are facing (typically when using impersonation in ASP.NET).

3.1.7. Token

Token is an object accessible to programmer and representing a logon session. Figure below represents an important data contained in token.

The next figure shows physical structure of token:

In Windows are system objects and those objects are connected to concept of token-based security. It means that any object in operating system has it’s own “lock” (in Windows terminology this “lock” is called as security descriptor) and when anybody wants to access this objects then must provide his “key” to open this lock. And user’s tokens are keys used to open “lock” to get access to some resource.

 

So what happens when user is logged into the system? When you type your password correctly and you authorize yourself as authorized user then system starts your session and creates user’s token together with its security ID (SID). This SID is located in domain controller (when user is a member of domain) or in a local SAM database (when accessing local computer).

SIDs are very important giving uniqueness in Windows environment; they are variable-length and they are composed from many parts:

 

Following two sample on methods LookupAccountSid and LookupAccountName are very important when working with international environments. Operating systems can be localized into different languages with different general names for objects and that is why SIDs are the best way how to identify those objects independently from current language version. Those samples present how SID and object names can be found (see 3.1.8 and 3.1.9).

3.1.8. Get SID for current identity

This sample is a modification of sample from www.pinvoke.net (see http://pinvoke.net/default.aspx/advapi32.LookupAccountName).

 

using System;

using System.Runtime.InteropServices;

using System.Text;

using System.Security.Principal;

 

class FindSidForuser

{

      const int NO_ERROR = 0;

      const int ERROR_INSUFFICIENT_BUFFER = 122;

 

      enum SID_NAME_USE

      {

            SidTypeUser = 1,

            SidTypeGroup,

            SidTypeDomain,

            SidTypeAlias,

            SidTypeWellKnownGroup,

            SidTypeDeletedAccount,

            SidTypeInvalid,

            SidTypeUnknown,

            SidTypeComputer

      }

 

      [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError = true)]

      static extern bool LookupAccountName (

            string lpSystemName,

            string lpAccountName,

            [MarshalAs(UnmanagedType.LPArray)] byte[] Sid,

            ref uint cbSid,

            StringBuilder ReferencedDomainName,

            ref uint cchReferencedDomainName,

            out SID_NAME_USE peUse);               

 

 

      [DllImport("advapi32", CharSet=CharSet.Auto, SetLastError=true)]

      static extern bool ConvertSidToStringSid(

            [MarshalAs(UnmanagedType.LPArray)] byte [] pSID,

            out IntPtr ptrSid);

 

 

      [DllImport("kernel32.dll")]

      static extern IntPtr LocalFree(IntPtr hMem);

 

      [STAThread]

      static void Main(string[] args)

      {

            // get current user's identity

            WindowsIdentity wi = WindowsIdentity.GetCurrent();

            string accountName = wi.Name.ToString();

            byte [] Sid = null;

            uint cbSid = 0;

            StringBuilder referencedDomainName = new StringBuilder();

            uint cchReferencedDomainName = (uint)referencedDomainName.Capacity;

            SID_NAME_USE sidUse;

 

            int err = NO_ERROR;

            // get data for size of buffer in cbSid and cchReferencedDomainName

            if (!LookupAccountName(null,accountName,Sid,ref cbSid,referencedDomainName,ref cchReferencedDomainName,out sidUse))

            {

                  err = Marshal.GetLastWin32Error();

                  if (err == ERROR_INSUFFICIENT_BUFFER)

                  {

                        Sid = new byte[cbSid];

                        referencedDomainName.EnsureCapacity((int)cchReferencedDomainName);

                        err = NO_ERROR;

                        // !!! - FIND SID FOR USER !!!

                        if (!LookupAccountName(null,accountName,Sid,ref cbSid,referencedDomainName,ref cchReferencedDomainName,out sidUse))

                              err = Marshal.GetLastWin32Error();

                  }

            }

            if (err == 0)

            {

                  IntPtr ptrSid;

                  // convert sid value into well formatted string

                  if (!ConvertSidToStringSid(Sid,out ptrSid))

                  {

                        err = Marshal.GetLastWin32Error();

                        Console.WriteLine(@"Could not convert sid to string. Error : {0}",err);

                  }

                  else

                  {

                        string sidString = Marshal.PtrToStringAuto(ptrSid);

                        LocalFree(ptrSid);

                        Console.WriteLine(@"Found sid {0} : {1}",sidUse,sidString);

                  }

            }

            else

                  Console.WriteLine(@"Error : {0}",err);

      }

}

3.1.9. Get object name for SID

This sample is a modification of sample from www.pinvoke.net (see http://pinvoke.net/default.aspx/advapi32.LookupAccountSid).

using System;

using System.Runtime.InteropServices;

using System.Text;

 

class FindUserForSid

{

      const int NO_ERROR = 0;

      const int ERROR_INSUFFICIENT_BUFFER = 122;

 

      enum SID_NAME_USE

      {

            SidTypeUser = 1,

            SidTypeGroup,

            SidTypeDomain,

            SidTypeAlias,

            SidTypeWellKnownGroup,

            SidTypeDeletedAccount,

            SidTypeInvalid,

            SidTypeUnknown,

            SidTypeComputer

      }

 

      [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError = true)]

      static extern bool LookupAccountSid (

            string lpSystemName,

            [MarshalAs(UnmanagedType.LPArray)] byte[] Sid,

            System.Text.StringBuilder lpName,

            ref uint cchName,

            System.Text.StringBuilder ReferencedDomainName,

            ref uint cchReferencedDomainName,

            out SID_NAME_USE peUse);   

 

      [STAThread]

      static void Main(string[] args)

      {

            StringBuilder name = new StringBuilder();

            uint cchName = (uint)name.Capacity;

            StringBuilder referencedDomainName = new StringBuilder();

            uint cchReferencedDomainName = (uint)referencedDomainName.Capacity;

            SID_NAME_USE sidUse;

            // !!! Sid for BUILTIN\Administrators !!!

            byte[] Sid = new byte[] {1,2,0,0,0,0,0,5,32,0,0,0,32,2};

 

            int err = NO_ERROR;

            if (!LookupAccountSid(null,Sid,name,ref cchName,referencedDomainName,ref cchReferencedDomainName,out sidUse))

            {

                  err = System.Runtime.InteropServices.Marshal.GetLastWin32Error();

                  if (err == ERROR_INSUFFICIENT_BUFFER)

                  {

                        name.EnsureCapacity((int)cchName);

                        referencedDomainName.EnsureCapacity((int)cchReferencedDomainName);

                        err = NO_ERROR;

                        if (!LookupAccountSid(null,Sid,name,ref cchName,referencedDomainName,ref cchReferencedDomainName,out sidUse))

                              err = System.Runtime.InteropServices.Marshal.GetLastWin32Error();

                  }

            }

            if (err == 0)

                  Console.WriteLine(@"Found account {0} : {1}\{2}",sidUse,referencedDomainName.ToString(),name.ToString());

            else

                  Console.WriteLine(@"Error : {0}",err);

      }

}

 

Then when user is logged and his session exist in operating system then there is always his access token with his SID.

Except SID access token contains other very important ACEs

 

 

SID, DACL and other parts of token forms user’s “key” that is used to open any lock of system resource when user is trying to access it.

3.2. Protecting system resources

Protecting system resources is important and very programmer or architect should understand this otherwise security holes can be created and this can lead to terrible security issues. This chapter presents some of the principles on this together with samples.

But first, try to test yourself about your knowdledge J

3.2.1. Test yourself on security & protection of system resources

What do you think about situation presented on figure below? User A wants to access a file.txt where he has aquired full access rights.

But the same user has no right for parent folder DirA. What do you think that will happen when UserA would try to read this file?

 

3.2.2. Rules behind propagation of rights on objects

3.2.3. Get ACLs/ACEs for a file

To run this sample you’ll need to download this wrapper provided on GotDotNet.

http://www.gotdotnet.com/Community/UserSamples/Download.aspx?SampleGuid=E6098575-DDA0-48B8-9ABF-E0705AF065D9 and add reference to it.

 

Namespaces:

using System;

using System.Runtime.InteropServices;

// wrapper for pinvoke on Win32 APIs

using Microsoft.Win32.Security;

 

Code:

public static void Main()

{

      string filename = @"C:\boot.ini";

      SecurityDescriptor secDesc = SecurityDescriptor.GetFileSecurity(

            filename,

            SECURITY_INFORMATION.DACL_SECURITY_INFORMATION);

      using(secDesc)

      {

            foreach(Ace ace in secDesc.Dacl)

            {

                  Console.WriteLine("ACE SID:        {0} ", ace.Sid.CanonicalName);

                  Console.WriteLine("ACE Type:       {0} ", ace.Type);

                  Console.WriteLine("ACE AccessType: {0} (0x{0:X})", (FileAccessType)ace.AccessType);

            }

      }

      Console.ReadLine();

}

3.2.4. Set DACL for a file

To run this sample you’ll need to download this wrapper provided on GotDotNet.

http://www.gotdotnet.com/Community/UserSamples/Download.aspx?SampleGuid=E6098575-DDA0-48B8-9ABF-E0705AF065D9 and add reference to it.

 

Namespaces:

using System;

using System.Runtime.InteropServices;

// wrapper for pinvoke on Win32 APIs

using Microsoft.Win32.Security;

 

Code:

public static void Main()

{

      // you'll have to create file Sample.txt in root directory or change path appropriately

      string filename = @"C:\Sample.txt";

      // get security descriptor object for file with DACLs

      SecurityDescriptor secDesc = SecurityDescriptor.GetFileSecurity (filename, SECURITY_INFORMATION.DACL_SECURITY_INFORMATION);

      Dacl dacl = secDesc.Dacl;

      // add new ACE to DACLs (you must create user SampleUser in your system)

      dacl.AddAce (new AceAccessAllowed (new Sid ("SampleUser"), AccessType.GENERIC_ALL));

 

      // set DACLs to security descriptor

      secDesc.SetDacl(dacl);

 

      // update file security settings

      secDesc.SetFileSecurity(filename, SECURITY_INFORMATION.DACL_SECURITY_INFORMATION);

      Console.ReadLine();

}

4. Security Ratings

Security is the most important problem in our real life and also in our computers and information systems. That is why are defined standards and ratings that help us to recognize security level that has been checked and approved by qualified agencies and professionals.

 

This is the reason why the Department of Defense assigned responsibility for computer security to the Director of the National Security Agency (NSA), then DoD Computer Security Center was formed in 1981 and finally renamed to the National Computer Security Center (NCSC – www.radium.ncsc.mil). The primary task was defined in DoD Directive 5215.1, specifically tasked the center to establish and maintain…

 

“"... technical standards and criteria for the security evaluation of trusted computer systems that can be incorporated into the Department of Defense component life-cycle management process...”

 

The NCSC issued the first DoD Trusted Computer System Evaluation Criteria (TCSEC), commonly referred to as the "Orange Book." in August 1983. It was reissued in December 1985 as a DoD Standard (DOD 5200.28-STD). The TCSEC Standard serves the following purposes:

 

  1. Provide product manufacturers with a standard of security features to build into their products.
  2. Provide DoD components with a metric to evaluated how much trust can be placed in an automated information system for secure processing of classified or other sensitive data.
  3. Provide a basis for specifying security requirements in acquisition specifications.

 

The TCSEC Standard specifies degrees of trust with increasing level of trust ratings. Each level builds upon the previous one by adding security features and assurance to the user that the features work as designed.

 

Rating

Description

A1

Verified design.

B3

Security domains.

B2

Structured protection.

B1

Labeled security protection.

C2

Controlled access protection.

C1

Discretionary access protection.

D

Minimal protection.

4.1.1. What is a Common Criteria?

Common Criteria (CC) is an international standard - ISO 15408.  Common Criteria is the integration of information technology and computer security criteria.  The Common Criteria is used as the evaluation basis for security properties of IT products and systems.

4.1.2. Why is Common Criteria important?

ecent government and organizational mandates (within the US and other countries) require, and or encourage, Common Criteria Evaluations (CC) for computer security or computer security enabling products.  Computer security can best be defined as:

 

Operations that protect and defend information and information systems by ensuring their availability, integrity, authentication, confidentiality and non-repudiation.  Computer security also includes operations that provide for restoration of information systems by incorporating protection, detection, and reaction capabilities.

 

As a result, Common Criteria Evaluations have become a requirement for participation and growth in many government markets worldwide.  CC requirements are also starting to bleed into other markets such as finance and healthcare.

 

In order to participate in these markets, all of our computer security products and computer security enabling products will require an evaluation to determine if they need to go through the CC evaluation process.

5.  Security Concepts in .NET environment

5.1. Basic layout of .NET Framework – Security parts

.NET Framework security is composed from many technologies and approaches like:

 

The following figure presents basic layout of runtime environment and its security components.

Generally, the .NET platform is very advanced from security point of view, it brings many new approaches and today its one of the best (maybe the best) technical solution even when looking at security concepts. Today’s problems with viruses, buffer overrun and more can be solved by .NET environment and typical advantages will be seen with migration of Microsoft Office into the .NET environment (primitive viruses like MyDoom or similar will not be easy to write as now, we can hope J ).

The Microsoft .NET common language runtime (CLR) controls the execution of code, including just-in-time (JIT) compilation of Microsoft intermediate language code into native assembly code and garbage collection. Because of this CLR can prevent running code from inappropriate behavior and even to protect against security flaws.

As an assembly is loaded, JIT compiled, and executed, the security system verifies it for type safety and enforces code access security policy (see diagram).

5.2. Assembly

Assembly is a term used in .NET platform for a specific file generated by compilier after compilation. This file is similar to Windows binary files (at first sight with its extension .exe or .dll) and its layout is derived from standard PE file structure. But it is enhanced to support other features not included in native Windows binary files (for example assembly signature, version etc.).

5.2.1. Runtime security policy

Runtime security policy is essential to .NET security, it affects all assemblies running in .NET environment. But these is nothing magical on it – all assemblies are asking for some permissions which are needed to run and all  assemblies belond to specific groups depending on configured conditions. .NET environment sets 4 groups, in .NET terminology policy levels:

 

 

.NET security is similar to Windows security provided by operating system. User must provide his password and username, when he his authenticated against SAM database and access token is created and this token is used by process and threads to access system resources.

 

Similar approach is in .NET, when assembly is loaded it provides its evidences and asking for permissions based on those evidences. They are evaluated by runtime security policy management for each code group where assembly belongs to as it is configured for .NET environment (on figure below is sample code group with Intranet zone belonging to machine level security policy).

 

 

Finally assembly collects permissions from all code groups and when assembly is running and accessing any securable resource then those permissions are checked and access is granted or not.

Beside policy levels it is important to realize importance of code groups where permissions are defined. Code groups finally hold permissions and they associate assemblies with their permissions according to defined conditions (by default it is primary zone). On figure below is presented basic principle how code group works:

When policy levels work like an intersection of the same granted permissions, code groups join their permissions from one policy level.

Below is a sample with intranet application, when assembly is running in intranet environment (for instance run assembly from remote disk drive), then it is checked for all policy levels and assembly receives appropriate permissions (see figure).

In figure above application has been started from intranet (guess g:\sampleApp.exe). This application has a strong name and when started it is mapped to each levels and appropriate code groups. On enterprise level just All Code group is defined (the same is user level) with full trust permissions. On machine level are other sub-groups limiting permittions:

 

Code group

Description

My Computer (local)

Code is running on local machine and has full trust permissions.

Intranet

Code is executed from share or URL on LAN (or trusted enterprise network). Code has limited but still high permissions to access system resources.

Internet

Code is executed from internet and has limited permissions to a few resources like isolated storage, printing, dialogs.

Restricted

Code belongs to untrusted sites, it has no permissions.

Trusted

Code is executed from trusted sites and has the same permissions as in Internet code group.

 

Sample intranet application belongs to code group Intranet and will receive permissions defined in that group (environment variables, file dialog, isolated storage file, reflection, security, user interface, dns, printing, event log).

5.2.2. Types of security context for assemblies

Assembly must always run in security context which depends on behavior of assembly, code zone and type of assembly. Generally assembly can be running in three types of security contexts:

 

5.2.3. Generate key pair with sn.exe tool

First option is to generate file with keys which will be used to give a strong name to assembly:

 

 

sn -k myKey.snk

 

Second option is to store keys in CSP’s store, this is much more secure and recommended because keys are encrypted using DPAPI.

sn -i myKey.snk "SampleKeyStore"

5.2.4. Give an assembly a strong name

Assembly can be signed using file or CSP store, depending where keys are stored. If keys are stored in file then:

 

[assembly: AssemblyKeyFile(@"c:\@samples\MyKeys.snk")]

 

If keys are located in CSP store then use following attribute:

 

[assembly: AssemblyKeyName("SampleKeyStore")]

5.2.5. Delayed signing of assembly

This is a modification of signing an assembly with a strong name in previous chapter. There is different usage of keys because private key is not distributed and is kept hidden till final build is prepared and can be finally signed.

 

·        Locate a key pair generated by sn.exe tool.

·        Extract public key from myKey.snk file to new file myPublic.snk.

 

sn -p myKey.snk myPublic.snk

 

·        Set following attributes in AssemblyInfo.cs file:

 

[assembly: AssemblyDelaySign(true)]

// use public key file to sign

[assembly: AssemblyKeyFile("myPublic.snk")]

 

·        At the end of application development sign assembly with private key:

 

sn -r <assembly_name> myKey.snk

 

or

 

[assembly: AssemblyDelaySign(false)]

// use main key file to re-sign assembly with delay signing

[assembly: AssemblyKeyFile("c:\\signed\\myKey.snk")]

 

When assembly is not signed but AssemblyDelaySign is set to true, then in assembly is left enough space for latter signature. But problem is when assembly has to be installed into GAC (strong name is required). For this purpose is recommended to use a temporary private key and change it with final one when application is released.

5.2.6. List of permissions in policy levels

Namespaces:

using System;

using System.Security;

using System.Security.Policy;

using System.Collections;

 

Code:

static void Main(string[] args)

{

      IEnumerator policy = SecurityManager.PolicyHierarchy();

      while(policy.MoveNext())

      {

            PolicyLevel currentLevel = (PolicyLevel)policy.Current;

            IEnumerator namedPermission = currentLevel.NamedPermissionSets.GetEnumerator();

            while(namedPermission.MoveNext())

            {    

                  NamedPermissionSet permissionSet = (NamedPermissionSet)namedPermission.Current;

                  Console.WriteLine(permissionSet.Name);

                                                           

                  IEnumerator psEnumerator = permissionSet.GetEnumerator();

                  while (psEnumerator.MoveNext())

                  {

                        Console.WriteLine("\t" + psEnumerator.Current);

                  }

            }

      }

}

5.2.7. List of permissions assign to current assembly

Code:

using System;

using System.Reflection;

using System.Security;

using System.Security.Policy;

using System.Security.Permissions;

using System.Collections;

 

class AssemblyPermissions

{

      // name of buildin namedpermissionset for fulltrust

      const string sFullTrust = "FullTrust";

 

      static PermissionSet finalSet = new NamedPermissionSet("FinalAssemblySet");

      static PermissionSet permSet = null;

      // is it assembly with fulltrust permissions?

      static bool fullTrust = true;

 

      static void Main(string[] args)

      {

            IEnumerator policy = SecurityManager.PolicyHierarchy();

            while(policy.MoveNext())

            {

                  PolicyLevel currentLevel = (PolicyLevel)policy.Current;

                  CodeGroup group = currentLevel.ResolveMatchingCodeGroups(Assembly.GetExecutingAssembly().Evidence);

                  fullTrust &= ResolveGroups(group, currentLevel);

                  if (!fullTrust)

                  {

                        if (finalSet == null) finalSet = permSet;

                        else finalSet = finalSet.Intersect(permSet);

                        permSet = null;

                  }

            }

 

            if (fullTrust) Console.WriteLine("Assembly is running in full-trust mode.");

            else Output (finalSet);

      }

 

      static bool ResolveGroups(CodeGroup parent, PolicyLevel pl)

      {

            NamedPermissionSet nps = pl.GetNamedPermissionSet(parent.PermissionSetName);

            if (isFullTrust(nps)) return true;

 

            if (permSet == null) permSet = (PermissionSet)nps;

            else permSet = permSet.Union(nps);

 

            if (parent.Children.Count > 0)

            {

                  foreach (CodeGroup cp in parent.Children)

                  {

                        if (cp.Children.Count > 0) ResolveGroups(cp, pl);

                        else

                        {

                              NamedPermissionSet nps2 = pl.GetNamedPermissionSet(cp.PermissionSetName);

                              if (isFullTrust(nps2)) return true;

                              permSet = permSet.Union(nps2);

                        }

                  }

            }

 

            // fulltrust code group not found

            return false;

      }

 

      static bool isFullTrust(NamedPermissionSet nps)

      {

            if (nps.Name.Equals("FullTrust"))

            {

                  return true;

            }

            return false;

      }

 

      static void Output(PermissionSet ps)

      {

            IEnumerator psEnumerator = ps.GetEnumerator();

            while (psEnumerator.MoveNext())

            {

                  Console.WriteLine("\t" + psEnumerator.Current);

            }

      }

}

5.2.8. Get permission list for a custom evidence

Namespaces:

using System;

using System.Security;

using System.Security.Policy;

 

Code:

      static void Main(string[] args)

      {

            // set zone as Internet (default in runtime security settings with restricted permissions)

            Zone zone = new Zone(SecurityZone.Internet);

            // sample site of origin

            Site site = new Site("www.skilldrive.com");

 

            // create instance of evidence

            Evidence e = new Evidence();

            // add zone and site into evidence object

            e.AddHost(zone);

            e.AddHost(site);

            // resolve permissions

            PermissionSet permSet = SecurityManager.ResolvePolicy(e);

            Console.WriteLine(permSet);

      }

5.2.9. List of declarative permissions of assembly

.NET Framework provides tool permview.exe that can be used to get declarative permission requests in assembly.

This tool can be used as follows:

 

permview.exe assemblyName.exe

 

Output will be list of permissions declared in assemblyName.exe file.

5.2.10. Output assembly evidence list to XML file

Code:

using System;

using System.IO;

using System.Collections;

using System.Reflection;

 

namespace SampleAssembly

{

   class AsmEvidence

   {

         static void Main(string[] args)

         {

               // output file name

               string fileName = "asmevidence.xml";

               FileStream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);

               StreamWriter writer = new StreamWriter(stream);

 

               writer.WriteLine("<AssemblyList>", writer);

 

               // output current assembly to xml file

               outputAssembly(Assembly.GetExecutingAssembly(), writer);

 

               foreach (AssemblyName asmn in Assembly.GetExecutingAssembly().GetReferencedAssemblies())

               {

                     // output referencing assemblies to current assembly

                     outputAssembly(Assembly.Load(asmn), writer);

               }

              

               writer.WriteLine("</AssemblyList>");

               // close stream

               writer.Close();

         }

         static void outputAssembly(Assembly asm, StreamWriter writer)

         {

               writer.WriteLine("<Assembly name='{0}' version='{1}' codebase='{2}' culture='{3}'>", asm.GetName().Name, asm.GetName().Version,

                     asm.GetName().CodeBase, asm.GetName().CultureInfo);

               IEnumerator it = asm.Evidence.GetEnumerator();

               while (it.MoveNext())

               {

                     // dont output all raw data to keep file small and readable!!!!

                     if (it.Current.GetType() != typeof(System.Security.Policy.Hash))

                     writer.WriteLine(it.Current);

               }

               writer.WriteLine("</Assembly>");

         }

   }

}

5.2.11. List policy levels and code groups where current assembly belongs

Namespaces:

using System;

using System.Reflection;

using System.Security;

using System.Security.Policy;

using System.Collections;

 

Code:

class PolicyGroups

{

      static void Main(string[] args)

      {

            IEnumerator policy = SecurityManager.PolicyHierarchy();

            while(policy.MoveNext())

            {

                  PolicyLevel currentLevel = (PolicyLevel)policy.Current;

                  Console.WriteLine(currentLevel.Label);

                  CodeGroup group = currentLevel.ResolveMatchingCodeGroups(Assembly.GetExecutingAssembly().Evidence);

                  ResolveGroups(group);

            }

      }

 

      static void ResolveGroups(CodeGroup parent)

      {

            Console.WriteLine("\t" + parent.Name);

            if (parent.Children.Count > 0)

            {

                  foreach (CodeGroup cp in parent.Children)

                  {

                        if (cp.Children.Count >0) ResolveGroups(cp);

                        // code is not optimazed to work with many levels in console displaying

                        else Console.WriteLine("\t\t" + cp.Name);

                  }

            }

      }

}

5.3. Type safety, metadata and code verification

One of the most important part of .NET is the verifier which is the part of JIT compiler. Verifier ensures that executing code is safe and does some very important checks.

Programmers sometime are using scripting languages like JavaScript or VBScript allowing to use variables without declaration, initialization or assigning them very different types. This can lead to unintended behavior and possible security implications when program mysteriously crashes.

In compiled languages such as C and C++ is possible to do direct memory allocations or to take a pointer and do copy of memory data anywhere. This is a very powerful technique but also this is a source for many bugs and majority of security problems are cased by this.

.NET is very strict on type usage and verifier ensures that all types are declared properly and are properly used. CLR does checks on following issues:

 

·        Uninitialized variables

·        Unsafe variable casting

·        Out of bounds indexing of array

·        Buffer overrun

·        Bad use of pointers

 

Except type checking CLR is taking care of whole code when it loads it from assembly. But what is assembly? It is a package with PE (Portable Executable) format, where this format is similar to DLL structure. But this is extended with new areas like metadata, which has very useful data about classes, methods, fields, heaps, types contained in an assembly (more about PE format on MSDN).

The metadata can be seen as a detailed information section with data about variables, objects, types, security settings etc. One of the most important section of metadata are tables with definition of classes in assembly, table with methods and to this table is related table with method arguments (see diagram bellow).

This is a sample of code in assembly

 

      public class C

      {

            public void C1(string C11) {

                  // some code here

            }

}

 

which is then compiled to MS IL code stored in assembly. The metadata of that code contains following tables with appropriate code objects when each row is idenfied by a four-byte number – metadata token.

Type-safety verification is the cornerstone of .NET Framework security because it prevents access to unauthorized memory locations. This allows you to consistently enforce security policy. For example, code cannot overrun a buffer and cause execution to jump to an arbitrary memory location.

 

Metadata are very important to verify code – this process is called is code verification and occurs when assembly is being loaded. Those verifications are very important and should not be disabled (using SkipPermition

5.3.1. Get info about types in assembly

This is just simple sample about reflexion on assembly file and getting basic type info. For professional tool on reflection use .NET Reflector (see http://www.aisto.com/roeder/dotnet).

 

Namespaces:

using System;

using System.IO;

using System.Reflection;

 

Code:

class AssemblyInfo

{

      static void Main(string[] args)

      {

            // name of file with assembly information

            string fileName = "AssemblyInfo.txt";

            FileStream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);

            StreamWriter writer = new StreamWriter(stream);

 

            // use this to build large info file about assembly from .NET Framework

            // name of assembly file to examine

            // string asmFile = @"C:\Windows\Microsoft.NET\Framework\v1.1.4322\mscorlib.dll";

            // Assembly asm = Assembly.LoadFrom(asmFile);

 

            Assembly asm = Assembly.GetExecutingAssembly();

 

            // basic assembly properties

            writer.WriteLine("Location of assembly: " + asm.Location);

            writer.WriteLine("Assembly name: " + asm.FullName);

            writer.WriteLine("Entry point into assembly: " + asm.EntryPoint);

            writer.WriteLine("Assembly loaded from GAC: " + asm.GlobalAssemblyCache);

 

            writer.WriteLine("-------------- Resources --------------");

            // get resouce names for current assembly

            string[] names = asm.GetManifestResourceNames();

            for (int i=0; i<names.Length; i++)

            {

                  ManifestResourceInfo mri = asm.GetManifestResourceInfo(names[i]);

                  writer.WriteLine(mri.FileName + ", " + mri.ReferencedAssembly + ", " + mri.ResourceLocation);

            }

 

            writer.WriteLine("-------------- Types --------------");

            foreach (Type types in asm.GetTypes())

            {

                  // inpecting all classes, other types can be easily inspected with similar approach as demonstrated

                  if (types.IsClass)

                  {

                        writer.WriteLine("Class: "+types.Name);

                        BuildClass(types, writer);

                  }

            }

            // close file stream

            writer.Close();

      }

 

      private static void BuildClass(Type types, StreamWriter writer)

      {

            writer.WriteLine("\t"+"-------Constructors-------");

            foreach(ConstructorInfo ci in types.GetConstructors())

            {

                  writer.WriteLine("\t"+(ci.IsAbstract?"abstract ":"")+(ci.IsPrivate?"Private ":"")+(ci.IsPublic?"Public ":"")

                        +(ci.IsStatic?"Static ":"")+(ci.IsFinal?"Final ":"")+types.Name+", Parameters: "+ci.GetParameters().Length);

            }

            writer.WriteLine("\t"+"-------Methods-------");

            foreach(MethodInfo mi in types.GetMethods())

            {

                  writer.WriteLine("\t"+(mi.IsPrivate?"Private ":"")+(mi.IsPublic?"Public ":"")

                        +(mi.IsStatic?"Static ":"")+(mi.IsFinal?"Final ":"")+mi.Name+", Parameters: "+mi.GetParameters().Length);

            }

      }

}

5.4. Application domains

Application domains are very important enhancement in .NET platform providing better configuration, security and performance features. Domains can be understood as “smaller processes” running inside of process’s address space. In each process can be many domains representing different applications (typically this can be seen in web applications running on ASP.NET platform where each application is running inside of its own application domain).

5.4.1. Application domain boundaries and objects

Domain’s address space is protected by CLR separated from each others and can’t be accessed directly. Following picture shows how domains are separated within process and within whole system.

CLR protects every application domain and doesn’t allow them to access other domain’s address space. This can be done just by using web services, messaging or remoting (or by some direct memory operations around CLR environment).

When remoting is involved then there are two options how to pass objects between application domain boundaries:

 

·        Marshal-by-value (MBV)

Objects that are passed as MBV are serializable type and when object is passed then current state of object is serialized and new object in other domain is created and initialized with serialized values. Then in both domains exist two the same objects, but two instances.

This can be achieved by this declaration:

 

[System.Serializable]

class PassingMBV

{

// here are member variables and properties

}

 

When working with MBV objects then copy instances are created and to create a new instance in another application domain, metadata with object’s type must be loaded. This means that another assembly must be loaded and can’t be unloaded till domain is closed. This can lead to performance problems and that is why wrappers can be used instead of standard object’s instances.

For that purpose is designed class System.Runtime.Remoting.ObjectHandler which wraps object’s type and is used as a “proxy” that can be called to when object is needed.

 

 

 

·        Marshal-by-reference (MBR)

Objects that are passed as MBR are not cloned as MVB’s objects. First proxy of passed is created and this proxy is passed to other domain but still there is a connection with original object which is kept in first domain and the second one makes calls on this object using its proxy.

All MBR objects must be derived from System.MarshalByRefObject:

 

class PassingMBR : System.MarshalByRefObject

{

// here are member variables and properties

}

If objects are not MBV (serializable) or MBR (derived from MarshalByRefObject) then they can’t be passed between domain neither way.

5.4.2. Create application domain programmatically

Namespaces:

using System;

 

Code:

public static void Main(string[] args)

{

      AppDomainSetup setup = new AppDomainSetup();

      // setup path for a new appdomain, use base of current appdomain, runtime will use it to get private assemblies

      setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;

      // this is  a appdomain configuration file

      setup.ConfigurationFile = "app_domain.config";

      // this is a list of directories with private assembly, it's relative to ApplicationBase

      setup.PrivateBinPath = "first;second;third";

      // download or not assemblies over the network (http)

      setup.DisallowCodeDownload = false;

      // if configuration file is provided then this enables to use policy section in config file

      setup.DisallowPublisherPolicy = true;

 

      AppDomain newDomain = AppDomain.CreateDomain(

            "SecondAppDomain",

            AppDomain.CurrentDomain.Evidence,

            setup);

}

5.4.3. Shadow copy enabled for application domain

Sometimes it’s important to keep executable file unlocked (for instance when compiling or publishing new assembly version in real-time environment). To achieve this, application domain must be setup to enable shadow copy feature, then assembly is cached by a system and not locked. This feature is used extensively by ASP.NET and can be used in many scenarios like custom deploying and versioning system etc.

 

Namespaces:

using System;

using System.Diagnostics;

 

Code:

static void Main(string[] args)

{

      // get current executable file name

      string file = Process.GetCurrentProcess().MainModule.FileName;

 

      // create setup for main domain (this will hold shadowed copy of assembly)

      System.AppDomainSetup mySetup = new System.AppDomainSetup();

      mySetup.ApplicationName = "ShadowingDomain";

      // enable Shadowcopying, MUST be string!

      mySetup.ShadowCopyFiles = "true";

      // which directory will be shadowcopied

      mySetup.ShadowCopyDirectories = AppDomain.CurrentDomain.BaseDirectory;

      // where will go copied files

      mySetup.CachePath = AppDomain.CurrentDomain.BaseDirectory;

 

      // new application domain to be shadowed

      AppDomain domain = AppDomain.CreateDomain(AppDomain.CurrentDomain.FriendlyName,

            AppDomain.CurrentDomain.Evidence,

            mySetup);

 

      // if shadowing is not started yet

      if (!AppDomain.CurrentDomain.ShadowCopyFiles) domain.ExecuteAssembly(file);

}

5.5. Security tools available in .NET

Here is alphabetical list of available security tools in .NET Framework 1.1.

 

Tool name

Description

MSDN reference

Certificate Creation Tool

(makecert.exe)

Generates X.509 certificates for testing purposes only.

Details

Certificate Manager Tool

(certmgr.exe)

Manages certificates, certificate trust list (CTLs) and certificate revocation list (CRLs). Works with local account and can help with local testing of security features using certificates. The same tool is available through Internet Explorer (Internet options->Content->Certificates).

Details

Certificate Verification Tool

(chktrust.exe)

Checks the validity of a file signed with an Authenticode certificate.

Details

Code Access Security Policy

(caspol.exe)

Enables users and administrators to modify security policy for the machine policy level, and the enterprise policy level.

Details

File Signing Tool

(signcode.exe)

Signs portable executable file (.dll or .exe) with an Authenticode digital signature and required permissions for code are added. This gives a control over security restrictions placed on executable files.

Details

Isolated Storage Tool

(storeadm.exe)

Lists or removes existing stores for the current user. Sample isolated storages for Windows XP are:

  • Roaming enabled: <SYSTEMDRIVE>\Documents and Settings\<user>\Application Data
  • Non-roaming: <SYSTEMDRIVE>\Documents and Settings\<user>\Local Settings\Application Data

See chapter 7.19.

Details

Permissions View Tool

(permview.exe)

This tool is used to view the minimal, optional, and refused permissions sets requested by an assembly.

Details

PEVerify Tool

(peverify.exe)

It helps to verify if generated MSIL code meets type safety requirements (generally this tool is not useful for application programmers but just the system ones, who write compilers or when developers wants to use compilers provided from third party and check the compiler’s output).

Details

Secutil Tool

(secutil.exe)

 

Details

Set Registry Tool

(setreg.exe)

 

Details

Software Publisher Certificate Test Tool

(cert2spc.exe)

 

Details

String Name Tool

(sn.exe)

 

Details

5.6. Code Access Security

Code access security is a basic part of .NET security concepts enabling to identify code privilege to run specific type of operation requiring some type of authorization to do it.

CAS divides code trust into different levels depending on where the code originates and also other aspects of its identity (like strong name etc.).

In .NET terminology this is called as permission and .NET defines three types of them:

 

Those types of permissions derive from System.Security.CodeAccessPermission abstract class. From that class are derived other classes representing different permissions like System.Data.Common.DBDataPermission (ensures that user has a security level adequate for accessing data), System.Security.Permissions.FileIOPermission (it controls the ability to access files and folders) and many others.

Also identity permissions are classes derived from System.Security.CodeAccessPermission abstract class. However, those classes are used for a different purpose when compared with code access permissions. Identity permissions enable to securely run assemblies according to type of their origin. For instance, when assembly is downloaded from Internet, then it can be identified by System.Security.Permissions.ZoneIdentityPermission class.

5.6.1. Stack-walk

Stack walk is a basic part of .NET security. It checks calling queue for a specific system resource where are some security concerns. When code is accessing protected system resource and demanding a permission to access it, then stack walk is performed.

When function is called then there is created a frame in .NET security stack where all data related to this call are stored.

On figure above is presented situation when assemblies are nested with method calls finally asking for some system resource. Assembly C is calling Demand() casing to run through all stack frames in stack (there are two frames for methods from assembly A and B) and check their permission set for permission 1. If permission 1 is granted then it is ok, if not then SecurityException will be raised like in case of assembly A (there is just permission 2).

Stack walk is very important to protect against “luring attack” when some malicious applications takes advantage of some other one with higher privileges and does something harmful. Stack walk is great to improve protection against such type of attack but still there are many issues which must be considered like:

 

5.6.2. Limit access permissions for a method

Namespaces:

using System;

using System.Security;

using System.Threading;

using System.Security.Permissions;

using System.Security.Principal;

 

Code:

class LimitPermission

{

      static void Main(string[] args)

      {

            try

            {

                  // -------- uncomment this to run app correctly, otherwise it will rise an exception!!!!

                  // -------- set appdomain to use windows principal!!!

                  // Thread.GetDomain().SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

                  MethodClass.MethodA();

            }

            catch (SecurityException se)

            {

                  Console.WriteLine("You are not authorized to access MethodA! Change role name.");

                  Console.WriteLine(se.Message);

            }

 

            Console.ReadLine();

      }

}

 

// caller must be in group of administrators

[PrincipalPermissionAttribute(SecurityAction.Demand, Role=@"BUILTIN\Administrators")]

class MethodClass

{

      public static void MethodA()

      {

            Console.WriteLine("OK! YOU ARE CORRECT USER! UseName: " + Thread.CurrentPrincipal.Identity.Name);

            Console.WriteLine("MethodA was called!");

      }

}

5.6.3. Add new code group to runtime security

Namespaces:

using System;

using System.Security;

using System.Security.Permissions;

using System.Security.Policy;

using System.Collections;

 

Code:

class TestAddCodeGroup

{

      static void Main(string[] args)

      {

            IEnumerator polItem = SecurityManager.PolicyHierarchy();

            // move to enterprise policy level

            polItem.MoveNext();

            // move to machine policy level

            polItem.MoveNext();

            // cast to policylevel

            PolicyLevel policy = (PolicyLevel)polItem.Current;

            // show name of current policy level

            Console.WriteLine("Current policy level working on: "+(policy.Label));

            // get root codegroup for machine policy level

            CodeGroup rootGroup = policy.RootCodeGroup;

            SiteMembershipCondition memberShip = new SiteMembershipCondition("www.skilldrive.com");

            PermissionSet permSet = policy.GetNamedPermissionSet("FullTrust");

            PolicyStatement policyStm = new PolicyStatement(permSet, PolicyStatementAttribute.Exclusive);

            UnionCodeGroup newGroup = new UnionCodeGroup(memberShip, policyStm);

            newGroup.Name = "SampleCodeGroup";

           

            // add new code group

            rootGroup.AddChild(newGroup);

            policy.RootCodeGroup = rootGroup;

            SecurityManager.SavePolicy();

      }

}

5.7. Role-based Security

5.7.1. Identity classes (also Whidbey)

 

5.7.2. Principal policy

In .NET are recognized two types principals: windows principal and generic principal. The first one is related to Windows security context and when current Windows principal is associated with access token tight to each process under Windows. When generic principal is involved then this security context is independent from under laying environment like Windows. By default .NET principal policy is set to generic identity and anonymous user. To change default settings call SetPrincipalPolicy() method on the beginning. See following samples where is described how principal policy can be set.

In first sample is presented code where on first two lines is accesed and initialized principal object of current thread.

 

using System;

using System.Threading;

using System.Security;

using System.Security.Principal;

 

 

public static void Main(string[] args)

{

      // get default principal - this will be GenericPrincipal

      IPrincipal principal = Thread.CurrentPrincipal;

      Console.WriteLine("Default principal: "+ principal.GetType());

 

      // change default policy to Windows, this will not work!!! principal object has been activated already!!!

 

AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

 

      // output will be a GenericPrincipal again

      principal = Thread.CurrentPrincipal;

      Console.WriteLine("Changed principal: "+ principal.GetType());

}

 

Output of this program will be always GenericIdentity because this principal has been bind to current thread with call Thread.CurrentPrincipal and setting other principal policy will be ineffective. But consider next code section:

 

public static void Main(string[] args)

{

      // first change default policy to Windows principal

      AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

 

      // output will be WindowsPrincipal now!

      IPrincipal principal = Thread.CurrentPrincipal;

      Console.WriteLine("Changed principal: "+ principal.GetType());

}

 

Here is SetPrincipalPolicy() called first and it will be effective so console output will be WindowsIdentity class. If programmer needs to deal with different principals in application then new threads should be created and in ThreadStart delegate should be done their inicialization as shown in following sample:

 

using System;

using System.Threading;

using System.Security;

using System.Security.Principal;

 

class ManyPrincipalsSample

{

 

      private static PrincipalPolicy pp;

 

      public static void Main(string[] args)

      {

            // !!!!!!!!!!!!!!!!!!!!!!!!

            // whe this code is uncommented then all CurrentPrincipal objects will be GenericPrincipal

//          IPrincipal principal = Thread.CurrentPrincipal;

//          Console.WriteLine(principal.GetType());

 

            // set principal for thread t1 to WindowsPrincipal

            pp = PrincipalPolicy.WindowsPrincipal;

            // create a new thread

            Thread t1 = new Thread(new ThreadStart(SetThread));

            t1.Start();

            t1.Join();

 

            // set principal for thread t2 to UnauthenticatedPrincipal

            pp = PrincipalPolicy.UnauthenticatedPrincipal;

            Thread t2 = new Thread(new ThreadStart(SetThread));

            t2.Start();

            t2.Join();

 

            // set principal for thread t3 to NoPrincipal, no principal object will be created!!!

            pp = PrincipalPolicy.NoPrincipal;

            Thread t3 = new Thread(new ThreadStart(SetThread));

            t3.Start();

            t3.Join();

      }

 

      // this is a delegate for newly created thread object (ThreadStart)

      public static void SetThread()

      {

            // set principal policy of newly created thread

            Thread.GetDomain().SetPrincipalPolicy(pp);

            // get principal object

            IPrincipal principal = Thread.CurrentPrincipal;

            // output principal object's name

            if (principal != null) Console.WriteLine(principal.GetType());

            else Console.WriteLine("No principal object.");

      }

}

5.7.3. Principal classes

5.7.4. Using GenericPrincipal class

Namespaces:

using System;

using System.Threading;

using System.Security.Principal;

 

Code:

class SampleGeneric

{

      private static string user = "MyLovingUser";

 

      public static void Main(string[] args)

      {

            IIdentity gi = new GenericIdentity(user, "MyAuthenticationType");

            string[] roles = null;

            GenericPrincipal gp = new GenericPrincipal(gi, roles);

            Thread.CurrentPrincipal = gp;

 

            // call method

            SomeMethod();

      }

 

      private static void SomeMethod()

      {

            IPrincipal principal = Thread.CurrentPrincipal;

            if (principal.Identity.Name.Equals(user))

            {

                  Console.WriteLine("Jooooo, je to von! :) ");

            }

      }

}

5.7.5. Get list of groups for current thread’s identity

Namespaces:

using System;

using System.Collections;

using System.Threading;

using System.Security.Principal;

 

Code:

static void Main(string[] args)

{

      ArrayList array = new ArrayList();

      // set appdomain to bind threads to windows identity objects, then windows security api can be used

      AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

      // WindowsPrincipal is default principal object

      WindowsPrincipal wp = (WindowsPrincipal) Thread.CurrentPrincipal;

 

      // create array of mapped Builtin groups provided by .NET FW

      // there can be SecurityException when builtin group RID doesnt exist in all Windows systems

      try

      {

            // if (wp.IsInRole(WindowsBuiltInRole.AccountOperator)) array.Add("Managed - Account Operator");

            if (wp.IsInRole(WindowsBuiltInRole.Administrator)) array.Add("Managed - Administrator");

            if (wp.IsInRole(WindowsBuiltInRole.BackupOperator)) array.Add("Managed - Backup Operator");

            if (wp.IsInRole(WindowsBuiltInRole.Guest)) array.Add("Managed - Guest");

            if (wp.IsInRole(WindowsBuiltInRole.PowerUser)) array.Add("Managed - Power User");

            // if (wp.IsInRole(WindowsBuiltInRole.PrintOperator)) array.Add("Managed - Print Operator");

            if (wp.IsInRole(WindowsBuiltInRole.Replicator)) array.Add("Managed - Replicator");

            // if (wp.IsInRole(WindowsBuiltInRole.SystemOperator)) array.Add("Managed - System Operator");

            if (wp.IsInRole(WindowsBuiltInRole.User)) array.Add("Managed - User");

      }

      catch (ArgumentException se)

      {

            Console.WriteLine(se.Message);

      }

 

      // user can check if account is in builtin roles by those command (they should be changed appropriatelly to reflect used builtint accounts)

      if (wp.IsInRole(@"BUILTIN\Administrators")) array.Add("String - Administrators");

      if (wp.IsInRole(@"BUILTIN\Guests")) array.Add("String - Guests");

      if (wp.IsInRole(@"BUILTIN\Users")) array.Add("String - Users");

      IEnumerator en = array.GetEnumerator();

      while(en.MoveNext())

      {

            Console.WriteLine(en.Current);

      }

}

5.7.6. Get current user name

This sample presents two different approaches to getting current user information.

 

Namespaces:

using System;

using System.Net;

using System.Security.Principal;

 

Code:

static void Main(string[] args)

{

      // get info about current user using Environment class

      Console.WriteLine(Environment.UserDomainName + @"\" + Environment.UserName);

 

      // --------------------------

 

      // get current user from WindowsIdentity class

      WindowsIdentity user = WindowsIdentity.GetCurrent();

      // output current user name

      Console.WriteLine(user.Name.ToString());

}

5.7.7. Impersonate as another user

using System;

using System.Runtime.InteropServices;

using System.Security.Principal;

 

class ImpersonateUser

{

      // this implementation doesn't handle GetLastError function to catch error messages, it should be implemented in standard application

 

      // mapping of Win32 function to logon under another account

      [DllImport("advapi32.dll", SetLastError = true)]

      public static extern bool LogonUser(

            String lpszUsername,

            String lpszDomain,

            String lpszPassword,

            int dwLogonType,

            int dwLogonProvider,

            ref IntPtr phToken);

 

      // this will duplicate access token based on current user's one

      [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]

      public extern static bool DuplicateToken(

            IntPtr ExistingTokenHandle,

            int SECURITY_IMPERSONATION_LEVEL,

            ref IntPtr DuplicateTokenHandle);

 

      [DllImport("kernel32.dll", CharSet = CharSet.Auto)]

      public extern static bool CloseHandle(IntPtr handle);

 

      static void Main(string[] args)

      {

            const int LOGON32_LOGON_INTERACTIVE = 2;

            const int LOGON32_PROVIDER_DEFAULT = 0;

            const int SecurityImpersonation = 2;

            // handle of access token of current user

            IntPtr token = IntPtr.Zero;

            // new token based on the old one

            IntPtr duplicateToken = IntPtr.Zero;

 

            // this method returns handle to access token of user we want to use to logon, user is check just in local database

            if (LogonUser("TestUser", ".", "Test1234]", LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token))

            {

                  // token is duplicated according to the token of impersonated user

                  if (DuplicateToken(token, SecurityImpersonation, ref duplicateToken))

                  {

                        Console.WriteLine("Current user name: " + WindowsIdentity.GetCurrent().Name);

                        // new identity is created

                        WindowsIdentity newIdentity = new WindowsIdentity(duplicateToken);

                        // !!!! This is the impersonation !!!!

                        WindowsImpersonationContext impersonatedUser = newIdentity.Impersonate();

                        Console.WriteLine("Current user name: " + WindowsIdentity.GetCurrent().Name);

                        // return to the old user

                        impersonatedUser.Undo();

                        Console.WriteLine("Current user name: " + WindowsIdentity.GetCurrent().Name);

 

                        // close handles to tokens

                        CloseHandle(token);

                        CloseHandle(duplicateToken);

                  }

                  else { Console.WriteLine("Error duplicate."); }

            }

            else { Console.WriteLine("Error logon."); }

      }

}

5.7.8. Declarative principal permissions for Windows roles

This sample demonstrates usage of declarative principal permissions integrated with Windows groups. There is class MethodClass with MethodA and this class demands its callers to be members of group of administrators. To run it correctly, Thread line must be uncommented otherwise application will rise an exception because caller will not be authorized to use class MethodClass.

 

Namespaces:

using System;

using System.Security;

using System.Threading;

using System.Security.Permissions;

using System.Security.Principal;

 

Code:

class TestPrincipalPermission

{

      static void Main(string[] args)

      {

            try

            {

                  // -------- uncomment this to run app correctly, otherwise it will rise an exception!!!!

                  // -------- set appdomain to use windows principal!!!

                  // Thread.GetDomain().SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

                  MethodClass.MethodA();

            }

            catch (SecurityException se)

            {

                  Console.WriteLine("You are not authorized to access MethodA! Change role name.");

                  Console.WriteLine(se.Message);

            }

      }

}

 

// caller must be in group of administrators

[PrincipalPermissionAttribute(SecurityAction.Demand, Role=@"BUILTIN\Administrators")]

class MethodClass

{

      public static void MethodA()

      {

            Console.WriteLine("MethodA was called!");

      }

}

5.7.9. Declarative principal permissions for custom roles

Namespaces:

using System;

using System.Security;

using System.Threading;

using System.Security.Permissions;

using System.Security.Principal;

 

Code:

class TestPrincipal

{

      static void Main(string[] args)

      {

            try

            {

                  GenericPrincipal gp = new GenericPrincipal(new GenericIdentity("Jan Seda"), new string[] {"SampleRole"});

                  // set generic principal object, this will be used to authenticate to MethodClass

                  Thread.CurrentPrincipal = gp;

                  // call method

                  MethodClass.MethodA();

            }

            catch (SecurityException se)

            {

                  Console.WriteLine("You are not authorized to access MethodA! Change role name.");

                  Console.WriteLine(se.Message);

            }

      }

}

 

// caller must have "SampleRole"

[PrincipalPermissionAttribute(SecurityAction.Demand, Role=@"SampleRole")]

class MethodClass

{

      public static void MethodA()

      {

            Console.WriteLine("MethodA was called!");

      }

}

5.7.10. List running processes and user accounts

Namespaces:

using System;

using System.Runtime.InteropServices;

using System.Security.Principal;

using System.Collections;

using System.Diagnostics;

 

Code:

class ListOfProcessAccounts

{

      class ProcessIdentity

      {

            public Process Process;

            public WindowsIdentity Identity;

 

            public ProcessIdentity(Process process, WindowsIdentity identity)

            {

                  this.Process = process;

                  this.Identity = identity;

            }

      }

 

      [Flags]

            enum TOKEN_ACCESS : uint

      {

            TOKEN_ASSIGN_PRIMARY = 0x0001,

            TOKEN_DUPLICATE = 0x0002,

            TOKEN_IMPERSONATE = 0x0004,

            TOKEN_QUERY = 0x0008,

            TOKEN_QUERY_SOURCE = 0x0010,

            TOKEN_ADJUST_PRIVILEGES = 0x0020,

            TOKEN_ADJUST_GROUPS = 0x0040,

            TOKEN_ADJUST_DEFAULT = 0x0080,

            TOKEN_ADJUST_SESSIONID = 0x0100,

            TOKEN_READ = 0x00020000 | TOKEN_QUERY,

            TOKEN_WRITE = 0x00020000 | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT,

            TOKEN_EXECUTE = 0x00020000,

      };

 

      [DllImport("Advapi32.dll", SetLastError = true)]

      extern static int OpenProcessToken(IntPtr processHandle, TOKEN_ACCESS desiredAccess, out IntPtr tokenHandle);

 

      [DllImport("kernel32.dll", SetLastError = true)]

      extern static bool CloseHandle(IntPtr handle);

 

      static ProcessIdentity[] GetProcessesIdentities()

      {

            ArrayList list = new ArrayList();

 

            foreach( Process process in Process.GetProcesses() )

            {

                  try

                  {

                        IntPtr token = IntPtr.Zero;

                        if( OpenProcessToken(process.Handle, TOKEN_ACCESS.TOKEN_QUERY, out token) == 0 )

                        {

                              throw new ApplicationException("Can't open process token for: " + process.ProcessName);

                        }

 

                        list.Add(new ProcessIdentity(process, new WindowsIdentity(token)));

                        CloseHandle(token);

                  }

                  catch( Exception ex )

                  {

                        list.Add(new ProcessIdentity(process, null));

                        System.Diagnostics.Debug.WriteLine(ex.Message);

                  }

            }

 

            return (ProcessIdentity[])list.ToArray(typeof(ProcessIdentity));

      }

 

      static void Main(string[] args)

      {

            ProcessIdentity[] normalProcIDs = GetProcessesIdentities();

            foreach( ProcessIdentity procid in normalProcIDs )

            {

                  if( procid.Identity != null )

                  {

                        Console.WriteLine("{0} running under {1}", procid.Process.ProcessName, procid.Identity.Name);

                  }

                  else

                  {

                        Console.WriteLine("{0} *probably* running under SYSTEM", procid.Process.ProcessName);

                  }

            }

      }

}

6. Cryptography & Security

6.1. Buffer Overrun

Buffer overrun is one of the most common and the most dangerous security risks. This vulnerability exists because of low level of memory management that exists in languages like C/C++. Generally, buffer overruns occurs when the size of a variable is not large enough to hold a given value and the memory buffer is overwritten with inappropriate values. There are three kinds of buffer overruns:

So how buffer overrun works? The basic principle can be seen on the following diagram

As can be seen on a diagram, green data panel is correct with its size. It holds 256 bytes and its stccpy()s to stack as appropriately. But because there is no size check in method someMethod, it is possible to send unlimited data, like in second example char[260]. The attacker can do some tests on buffer overflow and if he is lucky he can override system values and get weird results, in better case he gets just access violation error and process shuts down. In case hacker makes successful attack, he will be able to overwrite the method’s return address and execute some arbitrary code with the same privileges as the running process.

6.1.1. CodeRed Worm, Buffer Overrun attack

In July 2001 worm CodeRed started to propagate itself over the Internet and affected Microsoft IIS. The problem was cased by usage of Unicode and ANSI format, when developer writing a small part of code in IIS forgot to do a check on passed parameter and its size calculated to Unicode format (its two bytes long, not one byte as ANSI).

This is a sample of affected code:

...

// cchAttribute is a byte count of user input

WCHAR wcsAttribute[200];

if (cchAttribute >= sizeof wcsAtrribute)

      THROW (CException(DB_E_ERRORSINCOMMAND));

DecodeURLEscapes((BYTE*) pszAttribute, cchAttribute,

                         wcsAttribute, webServer.CodePage());

...

As been seen, first two rows are the reason for existence of CodeRed worm. There is a check on cchAttribute size, where sizeof is calculated on ANSI size of wcsAttribute. But WCHAR is Unicode and that is why it is two time bigger that developer was expecting it should be. With this error hacker has 200 bytes available for attack.

This error can be fixed by a following check:

...

// cchAttribute is a byte count of user input

WCHAR wcsAttribute[200];

if (cchAttribute >= sizeof wcsAtrribute / sizeof WCHAR)

      THROW (CException(DB_E_ERRORSINCOMMAND));

...

When wcsAttribute is divided by size of WCHAR, then we get a correct value and check is fine. If code should be absolutely correct we should make division by a first array member:

...

// cchAttribute is a byte count of user input

WCHAR wcsAttribute[200];

if (cchAttribute >= sizeof wcsAtrribute / sizeof wcsAttribute[0])

      THROW (CException(DB_E_ERRORSINCOMMAND));

...

and not by using WCHAR because value of WCHAR can be changed and our code would be again insecure. With this approach we are fine for all times (we can hope for some time J).

6.1.2. SQLSlammer

Another type of very dangerous worm is SQLSlammer that used a security hole in Microsoft SQL Server 2000, but this attack used buffer underrun when UDP packets were sent to port 1433 with length of 376 bytes. This cased buffer underrun and worm was loaded as resident program and started to scan and send other internet addresses.

6.2. Algorithms for Encryption

6.2.1. Well Known Algorithms for Symmetric Encryption

Algorithm

Description

Data Encryption Standard (DES)

(see chapter 7.10.4)

Relatively slow, key - 56 bits, not suitable for high-security encryption

Triple DES

Performs three DES roundtrips, equivalent of 168-bit key, relatively slow, widely used

Advanced Encryption Standard (AES)

128-, 192-, 256-bit keys, current standard used by U.S. government

International Data Encryption Algorithm (IDEA)

128-bit key, requires licensing for commercial use

RC2

8- to 128-bit keys, stream cipher

6.2.2. Well Known Algorithms for Asymmetric Encryption

Algorithm

Description

RSA

384 – 16384 bit keys, used to encrypt data and generate digital signatures, de-facto standard for asymmetric encryption

Diffie-Helman

768 – 1014 bit keys, fast asymmetric algorithm

ElGamal

 

DSA

512 – 1024 bit keys, only supports digital signatures

6.2.3. Well Known Hash Algorithms

Algorithm

RFC

Description

Message Digest 4 (MD4)

RFC 1320

128 bits, very fast, appropriate for medium security usage

Message Digest 5 (MD5)

RFC 1321

128 bits, fast, more secure then MD4

Secure Hash Standard (SHA-1)

FIPS PUB 180-1

160 bits, slower then MD5, standard for U.S. government

 

6.3. Digital Certificates

A digital certificate is an item of information that binds the details about individual or organization to the individual’s or organization’s public key. Digital certificates can be used to verify the identity of both clients and servers.

A digital certificate is a binary structure that contains information about the holder of a public key. The most common form of certificate is the X.509 certificate. There are three versions of this certificate:1, 2 and 3.

6.4. Secure Communication Standards

6.4.1. IPSec (Internet Protocol Security)

IPSec is a framework of open standards you can use to ensure secure, private communication over IP networks by using a combination of cryptography security services that are negotiated between client and server. IPSec is build by using other encryption standards, including symmetric algorithms such as DES, 3DES and RC5 and hashes such as MD5 and SHA-1.

6.4.2. Kerberos

Kerberos is one of the most important security protocols. His name is derived from mythological three-headed dog guarding entrance into the Hades. But this is mythology but in computer science Kerberos means a new standard developed by MIT to keep primary network authentication secure and prevent sending passwords as plaintext over the network.

6.4.3. SSL (Secure Socket Layer)

SSL (Secure Sockets Layer) is a protocol for session-based encryption and authentication. The advantage of is that it lends itself to applications that require a trust relationship between client and server and want to defeat themselves from eavesdropping, tampering and message forgery.

SSL can be thought of as a pipeline between a client and server protecting transferred data.

6.4.3.1. History of SSL

SSL protocol was developed by Netscape in 1994 and now it is widely accepted as a secure standard for internet communication (today it’s implemented in nearly all web servers and browsers). The protocol comes in three versions:

 

 

SSL version 3 is the predominant protocol used worldwide as an secure standard. This version solves many issues like requesting a new handshake from client or from server in any time to change keys used to encipher client-server communication.

6.4.3.2. Description of SSL

In TCP stack of protocols SSL is at the transport layer and is independent of the application protocol. That is why application protocols can use SSL as it is shown in figure bellow.

 

 

SSL encryption relies on the server’s public key and private key. The private key exists only on the Web server and is used by the Web server to encrypt and decrypt secure messages. The public key exists on any client computer that has installed a certificate for that Web server. After the public key is installed, the user can send encrypted messages to, and decrypt messages received from, the Web server.

When SSL is used, the following occurs:

  1. The user browses to the secure Web server’s site.
  2. 2. The browser creates a unique session key and encrypts it by using the Web server’s public key, which is generated from the root certificate. Each session key that the Web server’s certificate generates is unique. Therefore, even if a hacker has the same certificate installed on their browser, the hacker cannot decrypt the message. The browser where the encrypted message originated cannot decrypt the message. Only the Web server with the appropriate private key can decrypt the message.
  3. The Web server receives the message and decrypts it by using its private key. This phase of SSL communication is often called the SSL handshake. A hacker may intercept a message protected with SSL. However, the hacker cannot decrypt the message, because the hacker does not have the Web server’s private key.
  4. After the connection is established, all communication between the browser and the Web server is secure.
  5. When the session ends, the session key is destroyed.

6.4.3.3. SSL Handshake

But thse steps are a very short and brief description of what happens when SSL is getting involved. But sometimes (primary when calling from application other services like .aspx pages and we want to use SSL from application).

That is why deeper knowledge is essential. One of the most asked question on SSL is regarding the SSL Handshake when connection is established. That is why there are the steps of handshake to understand this process:

 

  1. The client sends the server client’s SSL version number, proposed type of cipher, session specific data and other data required by server to communicate with the client.
  2. The server sends to client the same request with the same type of data: the server’s SSL version, type of cipher, session specific data and other data required by client to communicate with the server. The server also sends the server’s certificate and client can send its own client certificate if it’s required by server to access some resources.
  3. The client uses data sent by server to authenticate the server according to process in figure below (more details on Microsoft’s web site):

  1. The client uses all data generated in handshake up to now to create pre-master secret for current session (pre-master secret is 48 bytes large). This “pre-master” is encrypted by the server’s public key from server’s certificate (see step 2) and sends it to the server. The pre-master secret is
  2. This is an optional step in handshake – client authentication. In case that server has requested client’s authentication to access some resources and in such a situation the client’s certificate is used. The clients signs data known to both client and server and together he sends the client’s certificate along with the pre-master secret. When client’s authentication is required then server runs according to following diagram:

  1. When client is authenticated (when required) server uses its private key to decipher the pre-master secret from and then generates master secret. This happens on both sides – on the client and the server simultaneously:

"master_secret =

       MD5(pre_master_secret + SHA('A' + pre_master_secret +

           ClientHello.random + ServerHello.random)) +

       MD5(pre_master_secret + SHA('BB' + pre_master_secret +

           ClientHello.random + ServerHello.random)) +

       MD5(pre_master_secret + SHA('CCC' + pre_master_secret +

           ClientHello.random + ServerHello.random));"

  1. Both the client and the server use the master secret to generate session keys, which are used for symmetric encryption used during the SSL’s session.
  2. Finally the client sends to server a message informing that future messages will be encrypted by symmetric key.

7. Cryptography

7.1. Basic terms in cryptography

Cryptography – is the science concerting ciphering and deciphering of some data to protect it from unauthorized access.

Steganography – this is a collection of techniques hiding data (for instance encrypted data) into some medium carrying them (like microfilm, images etc.). Generally steganography isn’t difficult to break (when compared with cryptography), but it hides data that could become a target for attack. Generally steganography is used together with cryptography to cover the encrypted data by some unimportant wrapper (like image) and not to show them to possible attacker to use on them some cryptoanalytic attacks.

Cryptoanalysis – this is the science related to cryptography, but its purpose isn’t to protect data but to find ways how to decrypt data without knowledge of secret key used for encryption.

Plain text – this term defines data, that are clear before encryption (generally readable text or similar form of data).

Cipher text – when plain text is encrypted, then cipher text is created. Input data (plain text) are now transformed to cipher text form, which is unreadable to unauthorized person.

Symmetric algorithms – A symmetric algorithm uses the same key for encryption and also for decryption of the same data.

Asymmetric algorithms – An asymmetric algorithm is based on research of Diffie and Hellman in 1976, when they published the first publication on this topic. The basic principle is presence of private and public keys when data can be encrypted and decrypted just by the adequate pair of those keys.

Confusion – is a method of mixing-up input data so it’s difficult to “mix” them back (decipher), this process is based on substitution when letter or a bit array is exchanged by another.

Diffusion – is a principle when every change in a plain text cases many changes in cipher text and this is based on transposition when letter or a bit array order is changed. Together with confusion, diffusion is a basic part of cryptographic algorithms because together they create too many alternatives when used (just imagine that 26 characters in alphabet used for substitution of plain text N characters long give (26n)! of cipher text modifications).

Initialization vector – when using block ciphers like DES encrypt/decrypt data in blocks when for block n is used defined encryption operation together with key and n-1 block. This prevents from finding any patterns in encryption process. But problem is with first block where is no previous block. That is why initialization vector is defined and it is block with random data giving higher secrecy.

Salt – salt is a term used for non-secret bits which are added to data before encryption. When salt is used then attacks with precomputed databases or plain-text attacks are much harder.

7.2. A little bit of history

7.2.1. Caesar cipher

Cryptography is the word derived from Greek words kỳptus (hidden) and gráphein (write). The first use of cryptography has been identified to 1900 BC in Egypt. Since them cryptography made a big progress in its algorithms and techniques, but the purpose is always the same. One of the most famous ciphers is Caesar cipher, which has been extensively used by Julius Ceasar who even developed it. This cipher is very easy but efficient, it is based on character rotation, when each character is mapped to another one according to a number of rotation steps (see figure below)

When rotation is finished, then there are two patterns available. The first one is the normal with alphabet as we know it (the green pattern). The second one (blue pattern) is the pattern after 2 round of shifting. It’s obvious how cipher works now.

 

Here is a code representing Caesar cipher (code is not optimized, but it is easy to reduce its size):

 

using System;

using System.Text;

 

class CaesarCipher

{

      static void Main(string[] args)

      {

            string ciphertext = encrypt(".NET in Samples", 2);

            Console.WriteLine("Ciphertext: " + ciphertext);

            Console.WriteLine("Plaintext: " + decrypt(ciphertext, 2));

      }

 

      public static string encrypt(string plaintext, int key)

      {

            StringBuilder ciphertext = new StringBuilder();

            // for simplicity this implementation works just with uppercases

            char[] c = plaintext.ToUpper().ToCharArray();

 

            for (int i = 0; i < c.Length; i++)

            {

                  if (c[i] < 'A' || c[i] > 'Z') ciphertext.Append(c[i]);

                  else

                  {

                        // shift the character accoring to key and add it to cipher text

                        ciphertext.Append(shift((int)c[i], key));

                  }

            }

            return ciphertext.ToString();

      }

 

      public static string decrypt(string ciphertext, int key)

      {

            StringBuilder decipher = new StringBuilder();

            // for simplicity this implementation works just with uppercases

            char[] c = ciphertext.ToUpper().ToCharArray();

 

            for (int i = 0; i < c.Length; i++)

            {

                  if (c[i] < 'A' || c[i] > 'Z') decipher.Append(c[i]);

                  else

                  {

                        // now minus is used with key to invert the reverted values

                        decipher.Append(shift((int)c[i], -key));

                  }

            }

            return decipher.ToString();

      }

 

      // !!! algorithm is not optimized and could be written in one line, this is JUST for sampling purposes

      private static char shift(int iC, int key)

      {

            // subtract value of the lowest character in ASCII, it is 'A'

            iC = iC - 'A';

            // add key to shift the character as is needed

            iC = iC + key;

            // check if it is out of bounds, then return a reminder which represents shift character to a new possition

            iC = iC % 26;

            // add ASCII value of 'A' character to make correct representation as on the beginning

            iC = iC + 'A';

            return (char)iC;

      }

}

 

Caesar cipher is quite easy to brake because there are only 26 possible permutations before finding intelligible word. That is why its practical usage is zero, but it’s important to understand it and to know the historical development of cryptography.

7.2.2. Progress in cryptography

The substitution ciphers are just the beginning of development of cryptography algorithms. Major progress has been made during World Wars, when cryptography and cryptanalysis played a very important role. Notoriously known is decoding of message sent from German Foreign Minister Arthur Zimmerman to German Minister to Mexico offering Mexico United States’ land in exchange for support to Germany over the World War I. When Americans deciphered the message, they joined the war thereafter against Germany.

Second historically known case of cryptography importance is during World War II, when Germans used special machine called Enigma (developed by Arthur Scherbius), to encrypt their communication with U-boats. When Enigma cipher was broken, nearly all U-boats had been destroyed and this was very important for survival of United Kingdom and destruction of Bismark.

All of these algorithms were sophisticated and hard to brake and their mechanism was not so simple as Caesar cipher or similar ones.

But modern cryptography can be dated from 1952, when the National Security Agency was established and this organization played a key role during the Cold War. The most famous work of NSA is their research based on “Feistel ciphers” (according to Dr. Horst Feistel who establish this cryptographic concept) and published as FIPS PUB-46, but it’s more known under the name DES (see chapter 7.10.4).

7.2.2.1. Milestones in cryptography

Year

Event

1379

Compilation of first European manual on cryptography by Gabriele de Lavinde of Pharma.

1466

First cipher disk was described by Leon Battista Alberti.

1562

A French diplomat Blaise de Vigenère invented special matrix 26x26 called “Vigenere square”.

1854

Charles Babbage developed the method of statistical analysis that had been sucessfuly used to decrypt messages encrypted by Vigenere square.

1918

German engineer Arthur Scherbius invented Enigma.

1930

Japanese used the first rotor machine called “RED”.

1939

Japanese introduced a new cipher machine with code name “PURPLE”.

1943

German’s ENIGMA rotor setting could be rapidly found by “Bomba” machines and cipher texts could be decrypted.

1952

NSA was established.

1976

Diffie and Hellman published famous “New directions in cryptography”, as the beginning of asymmetric cryptography.

http://www.cs.rutgers.edu/~tdnguyen/classes/cs671/presentations/Arvind-NEWDIRS.pdf

1977

DES (Data Encryption Standard) was adopted.

1977

Ron Rivest, Adi Shamir and Leonard Adleman publish their proposal on public key cryptography concept known today as RSA and based on proposal from Diffie and Hellman.

7.3. PKCS

With development of cryptography methods software companies realized the importance of standards to define how to deal with data in secure and standard manner. Sun Microsystem, Microsoft, Applet and others joined this process in RSA and together they defined PKCS standards (Public Key Cryptography Standards).

For application programmers working with any cryptographical functions it’s important to known them because they’re referred in any crypto-documentation and are essential terms. That is why here are listed active PKCS standards with basic introduction (but more can be found on RSA website).

 

Standard

Description

PKCS#1

The RSA encryption standard. Defines mechanisms for encrypting and signing data using RSA system.

PKCS#3

The Diffie-Hellman key agreement standard. It defines Diffie-Hellman key agreement protocol.

PKCS#5

The password-based encryption standard (PBE).It defines a method to generate secret key on a password.

PKCS#6

The extended certificate syntax standard. It’s going to be exchange in favour of X509v3.

PKCS#7

The cryptographic message standard. It defines syntax of messages on which cryptography were used.

PKCS#8

The private key information syntax standard. It defines how to store private key information.

PKCS#9

It defines a selected attribute types for use with other PKCS.

PKCS#10

The certification request syntax standard. It defines syntax of certification requests.

PKCS#11

The cryptographic token interface standard. It defines technology independent programming interface for crypto devices as smartcards.

PKCS#12

The personal information exchange syntax standard. It defines a portable format for storage and transportation of user private keys & certificates etc.

PKCS#13

The elliptic curve cryptography standard. It defines mechanism how to encrypt and sign data using ECC.

PKCS#14

The pseudo random number generation. It defines mechanism of pseudorandom number generation process.

PKCS#15

The cryptographic token information format standard. It defines standard for the format of cryptographic credentials stored on cryptographic tokes.

7.4. CMV (Cryptographic Module validation)

Cryptographical algorithms are studied on mathematical basis but their implementation is the same important. If alrgorithms are not properly implemented than they are opened to attacks. That is why NIST (National Institute of Standards and Technology) has started program CMV which allows software vendors to demonstrate that they comply with the security standards and their implementations are certified as trustworthy (more details about CMV program can be found here: http://csrc.nist.gov/cryptval/).

This is very important for programmers because they have to prove their products are certified if they want to sell to the government or army.

By now there are two types of certification process when FIPS 140 define a framework and methodology for cryptographic standards.

 

 

Comparition of both models can be found here: http://csrc.nist.gov/publications/nistpubs/800-29/sp800-29.pdf.

7.4.1. Microsoft FIPS 140 certification

Microsoft is keen on FIPS certification because they are required by security agencies and governments (not just in USA) as FIPSs are becoming “de-facto” standard for implementation of cryptographic features.

More up-to-date details about certified Microsoft products are published on Microsoft’s website (http://www.microsoft.com/technet/security/topics/issues/fipseval.mspx).

7.4.2. .NET classes and FIPS 140

Only those classes are FIPS 140 certified (they are wrappers around CSP, primary CryptoAPI with FIPS 140 certification):

 

 

So far managed .NET crypto classes are not certified and it seems there are no plans to do it in the future. This is not good for those who develop enterprise applications for government and army. The only solution is to use .NET classes provided by thirt party companies other then Microsoft like:

 

7.5. Cryptography in .NET

Cryptography in .NET environment is based on CryptoAPI provided by Windows. But this doesn’t mean that .NET layer would be just a wrapper around this security feature in Windows. .NET’s namespace System.Security.Cryptography brings many new concepts and approaches to working with cryptography and other security related principles. There’re three primary characteristics:

7.6. Configuring .NET cryptography

Cryptographic namespaces in .NET can use XML configuration to setup the environment to work with appropriate classes and their implementations. Configuration is usually stored in machine.config in this way:

 

<configuration>

      <mscorlib>

            <cryptographySettings>

 

            ..........

 

            </cryptographySettings>

      </mscorlib>

</configuration>

 

Typically there can be setup mapping for Create methods of abstact classes.

This is shown in following sample configuration section:

 

7.7. Win32 Security API and .NET

GotDotNet provides excellent wrapping classes for security features in Windows API. The library can be obtained from http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=e6098575-dda0-48b8-9abf-e0705af065d9 and it is referred by many samples here.

7.8. Random number generators

7.8.1. Generating random values

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // array to be filled with strong random bytes

      byte[] plaindata = new byte[16];

      // abstract class represents specific RNG implementation

      RandomNumberGenerator rng = new RNGCryptoServiceProvider();

      // generate random value including zero values

      rng.GetBytes(plaindata);

      Console.WriteLine("This is a random value: "+Encoding.ASCII.GetString(plaindata));

}

7.8.2. Generating random nonzero values

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // array to be filled with strong random bytes

      byte[] plaindata = new byte[16];

      // abstract class represents specific RNG implementation

      RandomNumberGenerator rng = new RNGCryptoServiceProvider();

      // generate random nonzero value

      rng.GetNonZeroBytes(plaindata);

      Console.WriteLine("This is a random value: "+Encoding.ASCII.GetString(plaindata));

}

7.8.3. Random number generator and other CSPs (Cryptographic Service Provider)

For RNG can be used other CSPs, as they are defined in wincrypt.h and are described in CryptoAPI documentation.

 

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // constants are defined in wincrypt.h in VC SDK

      const int PROV_RSA_FULL = 1;

 

      // array to be filled with strong random bytes

      byte[] plaindata = new byte[16];

      CspParameters csp = new CspParameters(PROV_RSA_FULL);

      RandomNumberGenerator rng = new RNGCryptoServiceProvider(csp);

      // generate random value including zero values

      rng.GetBytes(plaindata);

      Console.WriteLine("This is a random value: "+Encoding.ASCII.GetString(plaindata));

}

 

The table bellow presents list of typical providers:

 

Constant

Wincrypto.h’s name

String name

1

PROV_RSA_FULL

-

2

PROV_RSA_SIG

Microsoft RSA Signature Cryptographic Provider

3

PROV_DSS

Microsoft Base DSS Cryptographic Provider

4

PROV_FORTEZZA

Not supported by .NET FW

5

PROV_MS_EXCHANGE

Not supported by .NET FW

6

PROV_SSL

-

12

PROV_RSA_SCHANNEL

Microsoft RSA SChannel Cryptographic Provider

13

PROV_DSS_DH

Microsoft Base DSS and Diffie-Hellman

18

PROV_DH_SCHANNEL

Microsoft DH SChannel Cryptographic Provider

24

PROV_RSA_AES

Microsoft Enhanced RSA and AES Cryptographic Provider

 

7.9. Hashing algorithms

Hashing algorithms are one-way functions. What it means? In general it’s that such an algorithm takes plaintext data on input and digests them to unique fixed-length output that is nearly impossible to be constructed back to plaintext.

7.10. Symmetric encryption

Symmetric encryption is very important for nearly all today’s activities. It uses a same key for encryption and decryption and that is why it faces a problem to distribution of keys and their number when many nodes are included and need to communicate securely.

Generally cryptography is facing a problem of brute-force attacks, but this is primary an issue for symmetric algorithms, where brute-force attacks are used more often.

Also symmetric algorithms are very fast and it takes very little time to try a key (when compared with asymmetric algorithms).

According to mathematical probability attacker should try half of all possible keys to get to big probability of finding a correct key. So how long should be a key? The answer depends on how long a secret should be kept safe. When we would use 40-bit keys, them key size is from 0 to about 1 trillion, what is a very low number of key to try and attacker will be successful when today’s modern computers would be used.

Currently, 128-bits keys are considered as secure and are the most commonly used. There can be expected that technology will be advancing and attackers will use stronger machines and better types of attacks. But when algorithms will be safe from mathematical point of view, then we can expect to use 512 bits keys as the largest ones (number of operations needed to do brute-force attack would need all atoms in universe as computers and even more time then this universe exists from Big Bang, about 224 millennia). But this is not the only type of attack, brute-forcing a key size, but there was encounter an attack on PRNG (pseudo-random number generator) and its seed.

Netscape was the inventor of SSL protocol and Netscape used it in its browser Netscape Communicator with custom PRNG using as a seed process ID, year, month, day, hours and seconds. On September 17, 1995, Goldberg and Wagner reported that they found the seed and also the key for SSL session in less then minute. It was not important if key size was 40 or 128 bits, it took only one minute to break it.

7.10.1. Block ciphers

Block cipher is a term used to symmetric algorithms that operate on a specific and defined block of data. It works with given data as with blocks, simply plaintext is divided into blocks (say 64 bits) and algorithm is working on them with strictly defined operations.

7.10.1.1. Advanced Encryption Standard

With ageing of DES, NIST started to work on a new security standard. This work began on January 2, 1997, when NIST asked for proposals on a new algorithm, which would be freely available. NIST named 15 candidates on August 20, 1998 and after one year in August 1999 this list was trimmed to 5 proposals.

Finally, on October 2, 2000, NIST named the winner algorithm called Rijndael (pronounced as “Rhine-doll”) developed by two Belgian researchers Joan Daemen and Vincent Rijmen.

This standard is now freely available and can be used, sold or developed by anybody.

7.10.2. Stream ciphers

Stream ciphers can be seen as very fast algorithms, faster then block ciphers. Stream ciphers are similar to concept called one-time pad, known in cryptography. Typically this technique was used in World War II, when headquarters gave to their spies one pad with printed numbers and the other copy was in headquarters. Then spy encrypted his message by a number corresponding to some number in the alphabet.

Stream ciphers work in similar way (but terms are different). In cryptography regarding stream ciphers the term pad is not used but instead of it key stream is more appropriate.

The steps of encryption are similar to one-time pad, when stream ciphers take usually one byte of plaintext and XOR it with one byte of key stream and throwing out one byte of ciphertext. Then used key stream byte is thrown away and key table is remixed.

7.10.3. Key distribution problem

When working with private keys, both side have to share the same key to communicate. The problem starts when keys must be distributed to another side and when many nodes are involved. Suppose that person A wants to securely communicate with person A, to encrypt their communication, but they have to exchange their keys securely. This is a paradox, because if they aren’t able to communicate securely, how could they make a secure exchange of keys? Of course in real life they could use a special agent to fly abroad and to carry their private keys in the protected package and make this “communication” secure. But imagine, when hundreds or thousands are involved (like when communicating over the Internet). Just use the following formula for some number of involved people in communication:

 

Number_of_keys = (n * (n-1))/2

 

where n represents number of people using private keys. As can be seen, this would be unacceptable expensive and even impossible to manage distribution of those keys when too much people are getting involved. The solution is asymmetric cryptography and concepts related to certification authorities (see chapter 7.11);

7.10.4. Data Encryption Standard (DES)

In 1970s IBM researchers started to work on the new encryption algorithm suited for computer age. This algorithm was based on scheme called LUCIFER and was developed by known and famous cryptographer Horst Feistel. Together with NSA they created the Digital Encryption Standard (DES).

DES is a block cipher using 56-bit key to build a key table. After researches introduced DES a new proposed standard, it became freely available and many other cryptographers started to study it. In 1980s DES was accepted as the standard because cryptographers agreed that has no weak parts and the only way how to break it is using of brute force attack (56-bit wide key can produce about 72 quadrillion of possibilities to try when brute force attack is applied).

The DES algorithm is defined in Federal Information Procession Standard (FIPS) 46-2 (or together with TripleDES in FIPS 46-3) and guidelines for its using are in FIPS 74.

7.10.4.1. DES modes

There are many different ways of using keys to cipher plaintext and this usage depends on application where algorithm is used. Here is a list of those modes as defined in FIPS 81 (see http://csrc.nist.gov/publications/fips/fips81/fips81.htm):

Encryption is performed on 8-byte blocks and this mode produces the same cipher text whenever the same plain text is encrypted using the same key and initialization vector.

This mode depends on 8-byte blocks and when provided data is less then this size, then additional bytes must be added to form 64-bit block. This process is known as padding. Padding is one of the points to study in cryptanalysis because there could be found some optimizations how to brake the algorithm. Regarding padding RSA Data Security, Inc. defines its standard PKCS#5 (see http://www.rsasecurity.com/rsalabs/pkcs/pkcs-5/) which works as presents following figure:

Figure presents, how padding works and it can be defined in two rules:

These two rules are the main principle of padding defined in PKCS#5 and they work very simple because when cipher text is decrypted, then on the end are always padding bytes with number representing number of padded bytes. Then it’s simple to remove date from padding bytes.

Back to ECB mode. The advantage of ECB is that 8-byte blocks are independent from each other. This means that when some of that block would be corrupted (for instance by transfer error) then this would not affect the other blocks. This is great when users want to keep higher reliability of data which can be transferred over not such a reliable medium. Then this can reduce a risk of data corruption.

But also ECB has disadvantages, like when working with generally known format of data (like letters or some XML documents). Because in this mode the cipher text will be always the same for the same plaintext, then attacker can watch a communication and can map some special data that are repeated. For instance when application sends email starting by “Dear Mr. Smith”, then the cipher text will be always the same and this can be tracked by attacked and this communication can be them used for other types of attacks.

CFB is a stream method of encryption in which the DES is used to generate pseudorandom bits which are XORed with binary plain text to form a cipher text. This mode is typically designed for situation, when application is not able to provide 64-bit blocks and needs to react in “real-time” manner. For instance when user press key on terminal and wants to see a result of this action immediately (like banking terminal or similar) then CFB is appropriate mode.

Usually CFB works with 8-bits representing one byte information (like when sending key pressed code over the network) and this mode is called as CFB8.

So what happens in CFB mode:

OFB mode is very similar to CFB mode, but there is one exception and this is that bits in shirting register aren’t replaced by ciphertext bits, but there is done a shifting of bits inside of this register (see figure bellow).

Encryption is performed on 8-byte blocks and as against ECB mode, the first block of plaintext is XORed with initialization vector (IV) and encrypted. This will produce the first block of cipher text, which is then XORed with next plain text block and again and again till the last plain text block.

When encryption proceeds to the last plain text block, padding can be used and this is done in the same way as in the ECB mode.

This is similar mode like CBC, but it differs just by adding plain text block to XOR operation. Today PCBC is very popular in use. The advantage of PCBC is that when error occurs, it’s propagated to all encrypted message and that is why attacker can’t make modifications to parts of message and change its content (for instance according to statistical analysis etc.).

The disadvantage is based on its advantage, because ciphertext can’t be decrypted on separated blocks but as a whole message. Then any error in encryption process cases incorrect message.

7.10.4.2. TripleDES

When DES started to be considered as obsolete algorithm, triple DES was introduced as the one widely used replacement. In its name is give a whole description of the enhancement – DES is performed three times at once with three different 56-bit keys (this will produce a result like a 168-bit key). But Triple DES is not as secure as it seems now. Cryptographers have found new ways how to reduce brute force attack to 108-bit key. Although this is still very secure key length, the problem is finding some workarounds how to reduce Triple DES key length and if this research can bring better results in brute force attacks area.

7.10.5. Blowfish

This algorithm was designed by Bruce Schneier, the author of the known book Applied Cryptography. Blowfish is not patented and it’s royalty free to use.

Blowfish is 64-bit block algorithm and was intended as a replacement for DES, because it’s faster and even more secure. Key length of this cipher is from 32 up to 448 bits.

More details on http://www.schneier.com/blowfish.html.

7.10.6. Twofish

This is Feistel algorithm designed by Bruce Schneier and its company Counterpane (colleges John Keisey, Dough Whiting, David Wagner, Chris Hall, Niels Ferguson). It was one of the five AES finalists. It’s 128-bit block cipher working with 128, 192 and 256-bits keys. Also like Blowfish, Twofish isn’t patented and it’s royalty free and source codes are available non-copyrighted.

It uses 16 rounds modified by one-bit rotation; the proposal uses different operations like multiplying in Galois element GF (28), arithmetic addition, XOR and S-boxes (those are 8x8 S-boxes, which are created by composition of key and 4x4 S-boxes).

Twofish is theoretically vulnerable to timing and power attacks and primary this was criticized because of a large complexity of this algorithm making analysis of it too hard (according to NIST).

7.10.7. MARS

MARS is one of the five finalists in AES contest. This algorithm had been proposed by IBM researchers and was considered as a very secrete one and according the authors algorithm has no predecessor. MARS has variable key length 128, 192 and 256-bits keys, but in general it supports keys up to 448-bits large.

Though MARS is based on conventional cryptographic methods it brings a new ideas like thesis that middle of algorithm is more significant them beginning or end etc.

Finally MARS was rated as a very secure one even though it is very complex (primary because of two kinds of rounds).

7.10.8. Rijndael

Rijndael has SPN (Substitution-Permutation Network) structure because it is based on cipher Square, where has been used for a first time square attack (more details about attacks on Rijndael can be found here http://www.schneier.com/paper-rijndael.pdf).

7.10.9. Ronald Rivest’s (RC) ciphers

Ronald Rivest, one of the founders of RSA Data Security, is one of the very famous persons in modern cryptography. He is the co-inventor of RSA public key encryption algorithm and as well he worked on symmetric ciphers series called RCx (from RC1 to RC6) according to his name (Ron’s Code).

The ciphers RC1 and RC3 were never used too much and that is why they’re not covered here.

7.10.9.1. RC2

This is a proprietary symmetric cipher of RSA Data Security. It works with 64-bit blocks and has a variable key length. Its speed is much higher then DES.

Together with RC4, RC2 has been widely used because of less stringent export rules applied to these ciphers.

7.10.9.2. RC4

A proprietary symmetric stream cipher of RSA Data Security. It works with variable key length. It operates in OFB-like mode (see 7.10.4.1) and like RC2 it’s faster then DES.

This cipher has become known primary by its usage in SSL and export laws enabling RC to be used worldwide (not DES).

7.10.9.3. RC5

This is a patented block cipher of RSA Data Security. It can be used with variable key and block sizes and it uses data-dependent rotations.

7.10.9.4. RC6

RC6 was RSA’s candidate for the AES where it entered the final round (like Twofish by Counterpane, see 7.10.6). It is a Feistel block cipher based on RC5 and as well as RC5 it can use variable number of key and block sizes (RC6 is a derivative of RC5 and that is why it is based on many previous studies). RC6 is very well designed for high-end smartcard usage where 32-bit instructions are available (then the performance of RC6 is excellent) but when compared with other finalists, RC6 has a relatively low security margin.

RC6 is theoretically vulnerable to timing and power attacks.

7.10.10. Hash value using MD5 and SHA

Namespaces:

using System;

using System.IO;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // data to be hashed

      string cipherData = "This is a sample plaintext";

 

      // data with hash value of plaintext

      byte[] hashbytes;

 

      // create MD5 provider to do hashing on plaintext

      MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();

      // calculate hash value on plaintext to be encrypted

      hashbytes = md5.ComputeHash(Encoding.ASCII.GetBytes(cipherData));

      Console.WriteLine("MD5 Hash value: "+Encoding.ASCII.GetString(hashbytes));

 

      // this is a 160-bit sha hash provider

      SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider();

            hashbytes = sha.ComputeHash(Encoding.ASCII.GetBytes(cipherData));

      Console.WriteLine("SHA 160-bit hash value: "+Encoding.ASCII.GetString(hashbytes));

 

      // this is a 256-bit managed sha hash provider

      SHA256Managed sha256 = new SHA256Managed();

      // hashbytes = sha256.ComputeHash(Encoding.ASCII.GetBytes(cipherData));

      Console.WriteLine("SHA 256-bit hash value: "+Encoding.ASCII.GetString(sha256.Hash));

 

      // this is a 512-bit managed sha hash provider

      SHA384Managed sha384 = new SHA384Managed();

      // hashbytes = sha384.ComputeHash(Encoding.ASCII.GetBytes(cipherData));

      Console.WriteLine("SHA 384-bit hash value: "+Encoding.ASCII.GetString(sha384.Hash));

 

      // this is a 512-bit managed sha hash provider

      SHA512Managed sha512 = new SHA512Managed();

            hashbytes = sha512.ComputeHash(Encoding.ASCII.GetBytes(cipherData));

      Console.WriteLine("SHA 512-bit hash value: "+Encoding.ASCII.GetString(hashbytes));

}

7.10.11. Collision in MD5 algorithm

Namespaces:

using System;

using System.Security.Cryptography;

using System.Text;

Code:

class MD5Collision

{

      static void Main(string[] args)

      {

            byte [] b1= {0xd1,0x31,0xdd,0x02,0xc5,0xe6,0xee,0xc4,0x69,0x3d,0x9a,0x06,0x98,0xaf,0xf9,0x5c,

                                    0x2f,0xca,0xb5,0x87,0x12,0x46,0x7e,0xab,0x40,0x04,0x58,0x3e,0xb8,0xfb,0x7f,0x89,

                                    0x55,0xad,0x34,0x06,0x09,0xf4,0xb3,0x02,0x83,0xe4,0x88,0x83,0x25,0x71,0x41,0x5a,

                                    0x08,0x51,0x25,0xe8,0xf7,0xcd,0xc9,0x9f,0xd9,0x1d,0xbd,0xf2,0x80,0x37,0x3c,0x5b,

                                    0x96,0x0b,0x1d,0xd1,0xdc,0x41,0x7b,0x9c,0xe4,0xd8,0x97,0xf4,0x5a,0x65,0x55,0xd5,

                                    0x35,0x73,0x9a,0xc7,0xf0,0xeb,0xfd,0x0c,0x30,0x29,0xf1,0x66,0xd1,0x09,0xb1,0x8f,

                                    0x75,0x27,0x7f,0x79,0x30,0xd5,0x5c,0xeb,0x22,0xe8,0xad,0xba,0x79,0xcc,0x15,0x5c,

                                    0xed,0x74,0xcb,0xdd,0x5f,0xc5,0xd3,0x6d,0xb1,0x9b,0x0a,/**/0xd8,0x35,0xcc,0xa7,0xe3};

                 

            byte [] b2 = {0xd1,0x31,0xdd,0x02,0xc5,0xe6,0xee,0xc4,0x69,0x3d,0x9a,0x06,0x98,0xaf,0xf9,0x5c,

                                     0x2f,0xca,0xb5,0x07,0x12,0x46,0x7e,0xab,0x40,0x04,0x58,0x3e,0xb8,0xfb,0x7f,0x89,

                                     0x55,0xad,0x34,0x06,0x09,0xf4,0xb3,0x02,0x83,0xe4,0x88,0x83,0x25,0xf1,0x41,0x5a,

                                     0x08,0x51,0x25,0xe8,0xf7,0xcd,0xc9,0x9f,0xd9,0x1d,0xbd,0x72,0x80,0x37,0x3c,0x5b,

                                     0x96,0x0b,0x1d,0xd1,0xdc,0x41,0x7b,0x9c,0xe4,0xd8,0x97,0xf4,0x5a,0x65,0x55,0xd5,

                                     0x35,0x73,0x9a,0x47,0xf0,0xeb,0xfd,0x0c,0x30,0x29,0xf1,0x66,0xd1,0x09,0xb1,0x8f,

                                     0x75,0x27,0x7f,0x79,0x30,0xd5,0x5c,0xeb,0x22,0xe8,0xad,0xba,0x79,0x4c,0x15,0x5c,

                                     0xed,0x74,0xcb,0xdd,0x5f,0xc5,0xd3,0x6d,0xb1,0x9b,0x0a,/**/0x58,0x35,0xcc,0xa7,0xe3};

                      

 

 

            MD5CryptoServiceProvider md5=new MD5CryptoServiceProvider();

            string h1 = ASCIIEncoding.ASCII.GetString(md5.ComputeHash(b1));

            string h2 = ASCIIEncoding.ASCII.GetString(md5.ComputeHash(b2));

 

            System.Console.WriteLine(h1);

            System.Console.WriteLine(h2);

 

      }

}

7.10.12. Classes for symmetric algorithms in .NET

7.10.13. Deriving symmetric keys from passwords

Sometimes password is used to derive a key to encrypt data by symmetric encryption algorithm. For this purpose are defined standards that specify how to use passwords and derive from them correct symmetric key.

.NET provides two classes related to deriving keys from passwords (Rfc2898DerivedBytes is available only with Whidbey .NET version):

The first class, PasswordDerivedBytes, is based on PBKDF1 (see RFC2898 section 5.1) and this should be used just for compatibility purposes since it can produce smaller keys than it should be used when higher security is required. So what PBKDF1 defines? Here is a list of operations that PBKDF1 does:

To this key can be used dictionary attacks and that is why there is salt which can improve security when using larger salt. Also number of iterations is very important and RSA recommends to set IterationCount to 1000.

Also PBKDF1 is bound to output size of hash algorithm but .NET implementation solves this issue so user needs not to care about it.

 

Second class, Rfc2898DerivedBytes, is available with Whidbey .NET Framework and provides better deriving mechanism for symmetric keys. It’s based on PBKDF2 (see

RFC2898 section 5.2). PBKDF2 makes a big improment when there isn’t bound to the size of hash algorithm. It’s recommended to use this class instead of PasswordDerivedBytes for higher security.

7.10.14. Creating symmetric encryption classes

In .NET Framework is available abstract base class SymmetricAlgorithm, which is able to provide general class to work with symmetric encryption classes. The code bellow shows how to use it to create each class:

Namespaces:

using System;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // call static method Create on SymmetricAlgorithm class

      // create DES instance

      SymmetricAlgorithm des = SymmetricAlgorithm.Create("DES");

      // create TripleDES instance (can be used string '3DES'

      SymmetricAlgorithm des3 = SymmetricAlgorithm.Create("TripleDES");

      // SymmetricAlgorithm des3 = SymmetricAlgorithm.Create("3DES");

      // create RC2 instance

      SymmetricAlgorithm rc2 = SymmetricAlgorithm.Create("RC2");

      // create Rijndael instance

      SymmetricAlgorithm rdm = SymmetricAlgorithm.Create("Rijndael");

}

 

Here is a table with possible strings representing each cryptographic method as a parameter for Create() method:

 

Cryptographic algorithm

String

DES

"DES", "System.Security.Cryptography.DES"

TripleDES

"3DES", "TripleDES", "Triple DES", "System.Security.Cryptography.TripleDES"

RC2

"RC2", "System.Security.Cryptography.RC2"

Rijndael

"Rijndael", "System.Security.Cryptography.Rijndael"

7.10.15. Symmetric encryption/decryption of plaintext using DES

Namespaces:

using System;

using System.IO;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // data to be encrypted

      string cipherData = "This is a sample plaintext";

      // final encrypted data

      byte[] cipherbytes;

      // byte form of plaintext

      byte[] plainbytes = Encoding.ASCII.GetBytes(cipherData);

 

      // creating instance of DES class

      SymmetricAlgorithm desObj = DES.Create();

      // generating symmetric key

      desObj.GenerateKey();

      // generating vector

      desObj.GenerateIV();

      // choose other appropriate modes (CBC, CFB, CTS, ECB, OFB)

      desObj.Mode = CipherMode.CBC;

      // setting the padding mode

      desObj.Padding = PaddingMode.PKCS7;

 

      // --------------- ECRYPTION ---------------

      // memory stream used as a target to write enrypted data

      MemoryStream ms = new MemoryStream();

      // transforms and encrypts plaintext data to memorystream object

      CryptoStream cs = new CryptoStream(ms,

            desObj.CreateEncryptor(),

            CryptoStreamMode.Write);

      cs.Write(plainbytes, 0, plainbytes.Length);

      cs.Close();

      // getting encrypted data from memorystream to bytes

      cipherbytes = ms.ToArray();

      ms.Close();

 

      Console.WriteLine("Cipher result: "+Encoding.ASCII.GetString(cipherbytes));

 

      // --------------- DECRYPTION ---------------

      MemoryStream ms1 = new MemoryStream(cipherbytes);

      CryptoStream cs1 = new CryptoStream(ms1,

            desObj.CreateDecryptor(),

            CryptoStreamMode.Read);

      // allocate array of bytes equal on lenght with ciphertext array

      plainbytes = new Byte[cipherbytes.Length];

      // decrypt the ciphertext from previous section

      cs1.Read(plainbytes, 0, cipherbytes.Length);

      cs1.Close();

      ms1.Close();

      Console.WriteLine("Decipher result: "+Encoding.ASCII.GetString(plainbytes));

}

7.10.16. Symmetric encryption/decryption of plaintext using RC2

See sample “Symmetric encryption/decryption of plaintext using DES” and change line

 

SymmetricAlgorithm desObj = DES.Create();

 

with following line

 

SymmetricAlgorithm desObj = RC2.Create();

 

Change other settings appropriately.

7.10.17. Symmetric encryption/decryption of plaintext using Rijndael

See “Symmetric encryption/decryption of plaintext using DES” and change line

 

SymmetricAlgorithm desObj = DES.Create();

 

SymmetricAlgorithm desObj = Rijndael.Create();

 

Change other settings appropriately.

7.10.18. Determining weak and semi-weak keys in DES

This is a very specific to DES/3DES where have been found weak and semi-weak keys. This function checks these keys. But according to RSA this is not an issue, because the probability that such a key would be used is 2-52 and those keys can be safely ignored. But this test makes nearly no impact on performance.

 

Here is the list of weak and semi-weak keys:

Type of key

Key value (in hexadecimal)

weak

00000000000000

weak

0000000FFFFFFF

weak

FFFFFFF0000000

weak

FFFFFFFFFFFFFF

semi-weak

01FE01FE01FE01FE

semi-weak

FE01FE01FE01FE01

semi-weak

1FE01FE00EF10EF1

semi-weak

E01FE01FF10EF10E

semi-weak

01E001E001F101F1

semi-weak

E001E001F101F101

semi-weak

1FFE1FFE0EFE0EFE

semi-weak

FE1FFE1FFE0EFE0E

semi-weak

011F011F010E010E

semi-weak

1F011F010E010E01

semi-weak

E0FEE0FEF1FEF1FE

semi-weak

FEE0FEE0FEF1FEF1

 

Namespace:

using System;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // creating instance of DES class

      DES desObj = DES.Create();

      // generating symmetric key (this method will never generate a weak key, included just to compile a sample program)

      desObj.GenerateKey();

      // checking for a weak key

      DES.IsWeakKey(desObj.Key);

      // checking for a semi-weak key

      DES.IsSemiWeakKey(desObj.Key);

}

7.10.19. Deriving symmetric key from password using PBKDF1

This sample uses CryptDeriveKey method which returns appropriate symmetric key.

 

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      const string password = "your password";

 

      // salt used to be add to password to make the process more random and secure

      byte[] salt = new byte[16];

      byte[] iv = new byte[8];

      RandomNumberGenerator rng = new RNGCryptoServiceProvider();

      // salt for password

      rng.GetBytes(salt);

 

      PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt);

pdb.IterationCount = 1000;

      // initialization vector

      rng.GetBytes(iv);

      // generate key based on a password (change settings to other algorithms to get different keys)

      byte[] rc2key = pdb.CryptDeriveKey("RC2", "MD5", 64, iv);

 

      Console.WriteLine("This is a RC2 key: " + Encoding.ASCII.GetString(rc2key) + " for password: " + password);

}

7.10.20. Deriving symmetric key & IV from a password using PBKDF1

In this sample key and initialization vector is acquired using GetBytes method.

 

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      const string password = "your password";

 

      // salt used to be add to password to make the process more random and secure

      byte[] salt = new byte[16];

      // initialization vector

      byte[] iv;

      // RC2 key

      byte[] rc2key;

      RandomNumberGenerator rng = new RNGCryptoServiceProvider();

      // salt for password

      rng.GetBytes(salt);

 

      PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt);

      // this iteration value is recommended by RSA

      pdb.IterationCount = 1000;

      pdb.HashName = "MD5";

      rc2key = pdb.GetBytes(16);

      iv = pdb.GetBytes(8);

 

      Console.WriteLine("This is a RC2 key: " + Encoding.ASCII.GetString(rc2key));

      Console.WriteLine("This is a IV: " + Encoding.ASCII.GetString(iv));

}

7.10.21. Deriving symmetric key from a password using PBKDF2

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      const string password = "your password";

 

      // salt used to be add to password to make the process more random and secure

      byte[] salt = new byte[16];

      // initialization vector

      byte[] iv;

      // RC2 key

      byte[] rc2key;

      RandomNumberGenerator rng = new RNGCryptoServiceProvider();

      // salt for password

      rng.GetBytes(salt);

 

      // PBKDF2 is used with 1000 of iterations

      Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, salt, 1000);

      rc2key = pdb.GetBytes(16);

      iv = pdb.GetBytes(8);

 

      Console.WriteLine("This is a RC2 key: " + Encoding.ASCII.GetString(rc2key));

      Console.WriteLine("This is a IV: " + Encoding.ASCII.GetString(iv));

}

7.10.22. Check valid key size for symmetric encryption

Namespaces:

using System;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // this is a desired size of the key

      int keysize = 64;

 

      SymmetricAlgorithm rc2Obj = RC2.Create();

      // array with legal key sizes for symmetric algorithm

      KeySizes[] legalsize = rc2Obj.LegalKeySizes;

      foreach (KeySizes ks in legalsize)

      {

            if (keysize >= ks.MinSize &&

                  keysize <= ks.MaxSize &&

                  keysize % ks.SkipSize == 0)

                  Console.WriteLine("This is a legal key size");

      }

}

7.10.23. Hybrid usage of symmetric and asymmetric encryption

See chapter “How to encrypt/decrypt large data using RSA?” for explanation why asymmetric encryption should not be used when encrypting data.

The best way is hybrid combination of those two approaches and it’s happening every day in many different situations when we do online shopping using SSL protected communication channels, payed TVs, banking systems and transactions etc. See in the figure below how this hybric system works:

 

 

Let’s see in more details what happens on the picture.

Bob and Alice want to share some private data and Alice want to send it to Bob. But how to do it? The answer is combination of symmetric and asymmetric encryption. Let’s say that Bob has his own public and private keys that match together. So what happens?

  1. Bob sends his public key to Alice (sending a public key is not so easy and this would be a very special topic related to existence of certificates, for now suppose that this public key is delivered securely and Alice can trust it).
  2. Alice is wise and will not use Bob’s public key to encrypt here private data as a video or any similar large binary format. She’ll use here own symmetric key that she protects. She encrypts here video using her symmetric key.
  3. Now Alice encrypts here symmetric key with Bob’s public key (something like when she would store her symmetric key in a Bob’s safe) .
  4. Alice sends all data to Bob (encrypted video together with symmetric key encrypted by Bob’s public key).
  5. Bob’s can open his safe using his private key and he gets Alice’s symmetric key which he can use to decrypt her video and see it.

 

7.10.24. Hashing of plaintext and encryption/decryption using DES

Namespaces:

using System;

using System.IO;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // data to be encrypted

      string cipherData = "This is a sample plaintext";

      // final encrypted data

      byte[] cipherbytes;

 

      // data with hash value of plaintext

      byte[] hashbytes;

      // decrypted hash value from ciphertext

      byte[] dechashbytes;

 

      // create MD5 provider to do hashing on plaintext

      MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();

      // calculate hash value on plaintext to be encrypted

      hashbytes = md5.ComputeHash(Encoding.ASCII.GetBytes(cipherData));

      Console.WriteLine("Hash value: "+Encoding.ASCII.GetString(hashbytes));

 

      // creating instance of DES class, use default keys and settings

      DES desObj = DES.Create();

 

      // --------------- ECRYPTION OF HASH VALUE---------------

      MemoryStream ms = new MemoryStream();

      CryptoStream cs = new CryptoStream(ms, desObj.CreateEncryptor(), CryptoStreamMode.Write);

      cs.Write(hashbytes, 0, hashbytes.Length);

      cs.Close();

      cipherbytes = ms.ToArray();

      ms.Close();

 

      Console.WriteLine("Cipher result: "+Encoding.ASCII.GetString(cipherbytes));

 

      // --------------- DECRYPTION OF HASH VALUE---------------

      MemoryStream ms1 = new MemoryStream(cipherbytes);

      CryptoStream cs1 = new CryptoStream(ms1,

            desObj.CreateDecryptor(),

            CryptoStreamMode.Read);

      // allocate array of bytes equal on lenght with ciphertext array for hash value

      dechashbytes = new Byte[cipherbytes.Length];

      // decrypt the hash from previous section

      cs1.Read(dechashbytes, 0, cipherbytes.Length);

      cs1.Close();

      ms1.Close();

      Console.WriteLine("Decrypted hash: "+Encoding.ASCII.GetString(dechashbytes));

}

7.10.25. Keyed hash algorithm HMACSHA1

A keyed hash algorithm is a cryptographic method that is used to hash date. In case of HMAC is this mechanism used for message authentication using SHA algorithm, see RFC 2104.

Namespaces:

using System;

using System.IO;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // data to be encrypted

      string cipherData = "This is a sample plaintext";

      byte[] cipherbytes = Encoding.ASCII.GetBytes(cipherData);

      byte[] key = new byte[16];

 

      HMACSHA1 hmac = new HMACSHA1(key);

      CryptoStream cs = new CryptoStream(Stream.Null, hmac, CryptoStreamMode.Write);

      cs.Write(cipherbytes, 0, cipherbytes.Length);

      cs.Close();

 

      Console.WriteLine("HMACSHA1 value is: "+Encoding.ASCII.GetString(hmac.Hash));

}

7.10.26. Keyed hash algorithm MACTripleDES

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // data to be encrypted

      string cipherData = "This is a sample plaintext";

      PasswordDeriveBytes pdb = new PasswordDeriveBytes("your string", null, "SHA1", 10);

      // MACTripleDES uses a key of length 8, 16 or 24 bytes

      byte[] keybytes = pdb.GetBytes(16);

 

      MACTripleDES mac3des = new MACTripleDES(keybytes);

      byte[] result = mac3des.ComputeHash(Encoding.ASCII.GetBytes(cipherData));

      Console.WriteLine("MACTripleDES method value is: "+Encoding.ASCII.GetString(result));

}

7.11. Asymmetric encryption

7.11.1. Certificates & Certification authorities

In Windows certificates can be seen using Internet Explorer or .NET tool certmgr.exe (see 5.3.1).

Here is the sample screenshot of what can be found by this tool; there is the list of all supported certification authorities (like Verisign, Thawte etc. as a root CAs). Also list of personal certificates is enlisted here on other tabpane.

 

7.12. Assymetric encryption

In previous chapters we have dealed with symmetric encryption and secret keys. But secret kes face many problems primary with delivering those keys securily to the other side. This is a key distribution problem and public key encryption helps to solve it. But this isn’t the only one purpose of asymmetric encryption because it stands as another ecryption possibility to keep data safe.

7.12.1. Classes for asymmetric algorithms in .NET

7.12.2. Storing public and private RSA keys in XML file

Namespaces:

using System;

using System.IO;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // by default it will create RSA keys

      RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)RSA.Create();

      // write to this file all rsa data

      FileStream fs = new FileStream("rsaxmldata.xml", FileMode.OpenOrCreate,

                        FileAccess.Write);

      // this will produce all RSA data, including private key

      byte[] rsadata = Encoding.ASCII.GetBytes(rsa.ToXmlString(true));

      fs.Write(rsadata, 0, (int)rsadata.Length);

      fs.Close();

}

7.12.3. Storing keys by CSP (Crypto Service Provider)

 

7.12.4. Encryption of plaintext using RSA with XML-stored key

This sample is able to be compiled by Visual Studio, but will throw an exception. This is cased by current version of RSACryptoServiceProvider, where method EncryptValue is not defined. This is because CryptoAPI doesn’t provide direct encryption/decription schema using RSA. Functional version follows in next chapter.

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // this is a rsa key serialized by ToXmlString method, see chapter 'Storing public and private RSA keys in XML file'

      const string rsakey = "<RSAKeyValue><Modulus>yf4I7PVef43rZ2NdPFA5FQFb/y/k/5Awqrwc+/VDUimthRg4C5K2P6EUhU5n2m4HUiz102LIuwsYDYuyHwG3VUbAb4zjqxiOwrSpsHfCvgOdLsb4DBrXFFGp5kMtoZrDzl84tnDlyYgy8v3o5Qp2eeQgDaau2PhYUxoco6IArHU=</Modulus><Exponent>AQAB</Exponent><P>74LA9574+jZbn2FoJ1QLqX1osXcUra/aKN9d58zO9XOexI+aPp9KEkOFifXVw3gQdSe4ZVv4UB/cEN4Z2X0pGw==</P><Q>1+YKCRr9I0K3/BA4ermfZaVrjAKPD1KGR+pfzf6vagMcZ0kUnRcug84oNu6WEfzYQhVfLfuZ7bqa0dgjGZjprw==</Q><DP>jZ9jOwhlcI5z3upaC+dGfhIJteYT9B/ngAOUI1yXg8u6NcA0FJNb2TDT5Z/Xpp14Hc4+2rBnQ/mSxuaNomy/wQ==</DP><DQ>sR8SgKHZpwHney27iEOc14E8mCLJRyLG81z+uDsHogtnU/0Kok4QZSXOrDJUf/FVYfGyokDV6ci7lwig0zE2FQ==</DQ><InverseQ>0rYiRKwxftLS8nR8STgqup+LtVjsfVRbfoZEFxiD6n1jhAweHRACPz6cF6uPI60b1QWYjBCz17EE/EdkTSvH9w==</InverseQ><D>JdLb+QM5XslEe2ev3ctn5PcMMwzU5MYrVs1C4CtdH9WOGI4gcIpYdjHDlfLIn65a0Jh6r8qfq+a36lFuWUAJBCmkT7uvU6RwuwkN5mkuLw54mtuEQCbR3Z0OXImdBtvj4uSL5t+YrJ+qezyhydCTKowtM8cLYMMrLkFxe2jlpUE=</D></RSAKeyValue>";

 

      // this is a text to be encrypted

      string plaintext = "This is a sample text.";

      // create RSA class

RSA rsa = RSA.Create();

      // Initialize RSA object with key

      rsa.FromXmlString(rsakey);

      // encipher plaintext by RSA

      byte[] ciphertext = rsa.EncryptValue(Encoding.ASCII.GetBytes(plaintext));

      Console.WriteLine("Value '"+plaintext+"' was encrypted to: ");

                  Console.WriteLine(Encoding.ASCII.GetString(ciphertext));

}

7.12.5. Encryption/decryption of plaintext using RSA

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // this is a text to be encrypted

      string plaintext = "This is a sample text.";

      // create RSA class

RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)RSA.Create();

      // encipher plaintext by RSA with PKCS1 1.5 padding (lower security)

      byte[] ciphertext = rsa.Encrypt(Encoding.ASCII.GetBytes(plaintext), false);

      Console.WriteLine("Value '"+plaintext+"' was encrypted to: ");

                  Console.WriteLine(Encoding.ASCII.GetString(ciphertext));

      Console.WriteLine("Decrypted text: "+Encoding.ASCII.GetString(rsa.Decrypt(ciphertext, false)));

}

7.12.6. Encryption/decryption of plaintext using RSA with XML-stored key

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      const string rsakey = "<RSAKeyValue><Modulus>yf4I7PVef43rZ2NdPFA5FQFb/y/k/5Awqrwc+/VDUimthRg4C5K2P6EUhU5n2m4HUiz102LIuwsYDYuyHwG3VUbAb4zjqxiOwrSpsHfCvgOdLsb4DBrXFFGp5kMtoZrDzl84tnDlyYgy8v3o5Qp2eeQgDaau2PhYUxoco6IArHU=</Modulus><Exponent>AQAB</Exponent><P>74LA9574+jZbn2FoJ1QLqX1osXcUra/aKN9d58zO9XOexI+aPp9KEkOFifXVw3gQdSe4ZVv4UB/cEN4Z2X0pGw==</P><Q>1+YKCRr9I0K3/BA4ermfZaVrjAKPD1KGR+pfzf6vagMcZ0kUnRcug84oNu6WEfzYQhVfLfuZ7bqa0dgjGZjprw==</Q><DP>jZ9jOwhlcI5z3upaC+dGfhIJteYT9B/ngAOUI1yXg8u6NcA0FJNb2TDT5Z/Xpp14Hc4+2rBnQ/mSxuaNomy/wQ==</DP><DQ>sR8SgKHZpwHney27iEOc14E8mCLJRyLG81z+uDsHogtnU/0Kok4QZSXOrDJUf/FVYfGyokDV6ci7lwig0zE2FQ==</DQ><InverseQ>0rYiRKwxftLS8nR8STgqup+LtVjsfVRbfoZEFxiD6n1jhAweHRACPz6cF6uPI60b1QWYjBCz17EE/EdkTSvH9w==</InverseQ><D>JdLb+QM5XslEe2ev3ctn5PcMMwzU5MYrVs1C4CtdH9WOGI4gcIpYdjHDlfLIn65a0Jh6r8qfq+a36lFuWUAJBCmkT7uvU6RwuwkN5mkuLw54mtuEQCbR3Z0OXImdBtvj4uSL5t+YrJ+qezyhydCTKowtM8cLYMMrLkFxe2jlpUE=</D></RSAKeyValue>";

      // this is a text to be encrypted

      string plaintext = "This is a sample text.";

      // create RSA class

      RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)RSA.Create();

      // inicialize RSA instance with key from XML data

      rsa.FromXmlString(rsakey);

      // encipher plaintext by RSA with PKCS1 1.5 padding (lower security)

      byte[] ciphertext = rsa.Encrypt(Encoding.ASCII.GetBytes(plaintext), false);

      Console.WriteLine("Value '"+plaintext+"' was encrypted to: ");

      Console.WriteLine(Encoding.ASCII.GetString(ciphertext));

      Console.WriteLine("Decrypted text: "+Encoding.ASCII.GetString(rsa.Decrypt(ciphertext, false)));

}

7.12.7. Encryption of plaintext using RSAParameters

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // this text to encrypt

      string plaintext = "Some text to be encrypted";

 

      const string publickey = @"yf4I7PVef43rZ2NdPFA5FQFb/y/k/5Awqrwc+/VDUimthRg4C5K2P6EUhU5n2m4HUiz102LIuwsYDYuyHwG3VUbAb4zjqxiOwrSpsHfCvgOdLsb4DBrXFFGp5kMtoZrDzl84tnDlyYgy8v3o5Qp2eeQgDaau2PhYUxoco6IArHU=";

      const string exponent = @"AQAB";

 

      RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)RSA.Create();

 

      RSAParameters rsaKeyInfo = new RSAParameters();

      rsaKeyInfo.Modulus = Encoding.ASCII.GetBytes(publickey);

      rsaKeyInfo.Exponent = Encoding.ASCII.GetBytes(exponent);

 

      rsa.ImportParameters(rsaKeyInfo);

      byte[] ciphertext = rsa.Encrypt(Encoding.ASCII.GetBytes(plaintext), false);

 

      Console.WriteLine("\nValue '" + plaintext + "' was enciphered to: ");

      Console.WriteLine(Encoding.ASCII.GetString(ciphertext));

      try

      {

            // this will rise an exception because private key is unknown

            Console.WriteLine("\nDeciphered text: " + Encoding.ASCII.GetString(rsa.Decrypt(ciphertext, false)));

      }

      catch(Exception e)

      {

            Console.WriteLine(e.Message);

      }

}

7.12.8. Encryption/Decryption of plaintext by RSA

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // this is a text to be encrypted

      string plaintext = "This is a sample text.";

      // create RSA class

RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)RSA.Create();

      // encipher plaintext by RSA with PKCS1 1.5 padding (lower security)

      byte[] ciphertext = rsa.Encrypt(Encoding.ASCII.GetBytes(plaintext), false);

      Console.WriteLine("Value '"+plaintext+"' was encrypted to: ");

                  Console.WriteLine(Encoding.ASCII.GetString(ciphertext));

      Console.WriteLine("Decrypted text: "+Encoding.ASCII.GetString(rsa.Decrypt(ciphertext, false)));

}

7.12.9. Encryption with public key (exception)

Namespaces:

using System;

using System.IO;

using System.Text;

using System.Security.Cryptography;

 

Code:

class RSAEncryptPubKey

{

      static void Main(string[] args)

      {

            // this text to encrypt

            string plaintext = "Some text to be encrypted";

 

            const string publickey = @"yf4I7PVef43rZ2NdPFA5FQFb/y/k/5Awqrwc+/VDUimthRg4C5K2P6EUhU5n2m4HUiz102LIuwsYDYuyHwG3VUbAb4zjqxiOwrSpsHfCvgOdLsb4DBrXFFGp5kMtoZrDzl84tnDlyYgy8v3o5Qp2eeQgDaau2PhYUxoco6IArHU=";

            const string exponent = @"AQAB";

 

            RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)RSA.Create();

 

            RSAParameters rsaKeyInfo = new RSAParameters();

            rsaKeyInfo.Modulus = Encoding.ASCII.GetBytes(publickey);

            rsaKeyInfo.Exponent = Encoding.ASCII.GetBytes(exponent);

 

            rsa.ImportParameters(rsaKeyInfo);

            byte[] ciphertext = rsa.Encrypt(Encoding.ASCII.GetBytes(plaintext), false);

 

            Console.WriteLine("\nValue '" + plaintext + "' was enciphered to: ");

            Console.WriteLine(Encoding.ASCII.GetString(ciphertext));

            try

            {

                  // this will rise an exception because private key is unknown

                  Console.WriteLine("\nDeciphered text: " + Encoding.ASCII.GetString(rsa.Decrypt(ciphertext, false)));

            }

            catch(Exception e)

            {

                  Console.WriteLine(e.Message);

            }

      }

}

7.12.10. How to encrypt/decrypt large data using RSA?

There are too many questions on this topic and that is why here is the answer. Many people use RSA to encrypt large blocks of data and then they’re surprised why performance is low and they think that .NET Framework isn’t well implemented. But this is a very wrong explanation because problem don’t relates to problems in .NET FW but to knowledge of programmer.

By default RSA instance (RSACryptoServiceProvider) is created to work with 1024 bits large keys and in such a case RSA can work just with data 1024 bits large (128 bytes). But this is not correct answer because PCKS#1 defines how to work with padding and here goes 11 bytes off. So finally a programmer can work with just 117 bytes when 1024-bits key are involved. This is a reason why programmers divide their data into each blocks that RSA can handle and encrypte each block separately. But this is wrong and this makes many performance problems.

The solution is to combine using of asymmetric and symmetric cryptography together. RSA is able to handle all keys of current symmetric ciphers (even when just 1024 bits are used) and finally symmetric encryption is fast enough to process any type of data.

7.12.11. Calling RSA/DSA from a Web service, ASP or COM+

When RSACryptoServiceProvider/DSACryptoServiceProvider classes are called from Web service, ASP or COM+ application then CryptographicException will rise. This problem was presented by many times in newsgroups and even Microsoft created a specific “Q” on this (Q322371). When RSACryptoServiceProvider/DSACryptoServiceProvider classes are created keys are stored in key containers in user profiles (see chapter 7.15.3 about DPAPI). When those classes are called from Web service, ASP or COM+ then CryptographicException is rise because user profiles are not loaded in these scenarios (for performance reasons) and key container can’t be accessed. Solution is to set CspParameters as presents the following code:

 

Namespaces:

using System;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      CspParameters cspParams = new CspParameters();

      // this is important to set DPAPI not to use user profiles but machine store!!!

      cspParams.Flags = CspProviderFlags.UseMachineKeyStore;

      // create provider with custom settings

      RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cspParams);

 

      // then follows the code using RSA/DSA appropriately

      // ....

}

7.13. Digital signatures

7.13.1. Sign and verify data with RSA I

RSA allows to define hash algorithm, this is a second parameter in SignData() method. For that example MD5 is used.

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // public key to check valid signature

      const string publickey = "<RSAKeyValue><Modulus>tlBhWUIdXAOQatzRKHnD+UFBQFMjZHEyMCUw3Z0zxAUyti1rPSKQBtpk3Si3j2pHt3bmKvLWThc7GOum8enSP2sdLWbcyOnYaV66UuR58g9DUdTR3QoXSTA0PcZIp6eXzyOVHSpIuhxeLQ836PvBto1NXUPvFeQedh7UCwQ/tDM=</Modulus><Exponent>AQAB</Exponent><P>7PEKBMcU4ehzmyNNFVk67G3t1R0emckztT9vcGGTUvAt/b5HpSUNftZZU8xvn+kaxdRhgFlo+cdLyeuAIQFzxQ==</P><Q>xPp7eyy7XejuR1rtUJ0bOTHC1b+rbw7TGVHjAoD3EPu63tAM145na1/quikdj9amT11Jbo/vzaDb3kwSp4pvlw==</Q><DP>N1RySntS3Q4zMN8leP7FS8C/8SxDoRXjBUgy9cNTa+K6Wq68fEwSwrO7WF49EtKUde4KdrZqVSm9AQIFga+dIQ==</DP><DQ>EPITiWcxv0R4qz7RR6wcWXFEd6sDjoxR8M2wn9iEaLufOefgEvM3Rm97/APpfSRULmOyG4badHAwOhGFUVCBhQ==</DQ><InverseQ>bWsonmY91jc1Zz+MWVEjudRKYi19ipgGsSsbH9BokrvdJPSw9Uxst5PT4Ev2kH0AGMxZwZfxnGPsYCgB+tnQ+g==</InverseQ><D>TqmN50pMiqgLBuCx6knnkcNjGRMGIU9p/TX+yJAMhtZLVClyrNUd2acfgAESenG78d/+XaebaeRCHnWG+bgOe41NbImIGcqa79Ldxx/HP11muaH12TZfig/AkNLSK2xIXjplJe8nicyjRbjCXgM/ER+6q45dxIP/8QjiaEoyQrk=</D></RSAKeyValue>";

      // private key to sign signature

      const string privatekey = "<RSAKeyValue><Modulus>tlBhWUIdXAOQatzRKHnD+UFBQFMjZHEyMCUw3Z0zxAUyti1rPSKQBtpk3Si3j2pHt3bmKvLWThc7GOum8enSP2sdLWbcyOnYaV66UuR58g9DUdTR3QoXSTA0PcZIp6eXzyOVHSpIuhxeLQ836PvBto1NXUPvFeQedh7UCwQ/tDM=</Modulus><Exponent>AQAB</Exponent><P>7PEKBMcU4ehzmyNNFVk67G3t1R0emckztT9vcGGTUvAt/b5HpSUNftZZU8xvn+kaxdRhgFlo+cdLyeuAIQFzxQ==</P><Q>xPp7eyy7XejuR1rtUJ0bOTHC1b+rbw7TGVHjAoD3EPu63tAM145na1/quikdj9amT11Jbo/vzaDb3kwSp4pvlw==</Q><DP>N1RySntS3Q4zMN8leP7FS8C/8SxDoRXjBUgy9cNTa+K6Wq68fEwSwrO7WF49EtKUde4KdrZqVSm9AQIFga+dIQ==</DP><DQ>EPITiWcxv0R4qz7RR6wcWXFEd6sDjoxR8M2wn9iEaLufOefgEvM3Rm97/APpfSRULmOyG4badHAwOhGFUVCBhQ==</DQ><InverseQ>bWsonmY91jc1Zz+MWVEjudRKYi19ipgGsSsbH9BokrvdJPSw9Uxst5PT4Ev2kH0AGMxZwZfxnGPsYCgB+tnQ+g==</InverseQ><D>TqmN50pMiqgLBuCx6knnkcNjGRMGIU9p/TX+yJAMhtZLVClyrNUd2acfgAESenG78d/+XaebaeRCHnWG+bgOe41NbImIGcqa79Ldxx/HP11muaH12TZfig/AkNLSK2xIXjplJe8nicyjRbjCXgM/ER+6q45dxIP/8QjiaEoyQrk=</D></RSAKeyValue>";

 

      // this is a text to be signed

      string plaintext = "This is a sample text.";

      byte[] plainbytes = Encoding.ASCII.GetBytes(plaintext);

      // RSA to sign a plaintext

      RSACryptoServiceProvider rsasign = new RSACryptoServiceProvider();

      // RSA checking valid signature

      RSACryptoServiceProvider rsacheck = new RSACryptoServiceProvider();

      MD5 md5 = MD5.Create();

      rsasign.FromXmlString(privatekey);

      // signing data

      byte[] signature = rsasign.SignData(plainbytes, md5);

 

      // validating signed data

      rsacheck.FromXmlString(publickey);

      if (rsacheck.VerifyData(plainbytes, md5, signature))

            Console.WriteLine("Signature is correct.");

}

7.13.2. Sign and verify data with RSA II

This is a simpler form of previous sample, when keys are generated directly in code and not provided in variables (like it could be in some calling function in application).

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // this is a text to be signed

      string plaintext = "This is a sample text.";

      byte[] plainbytes = Encoding.ASCII.GetBytes(plaintext);

      // this instance represents keys used to signing and verification

      RSACryptoServiceProvider rsakey = new RSACryptoServiceProvider();

      // RSA to sign a plaintext

      RSACryptoServiceProvider rsasign = new RSACryptoServiceProvider();

      // RSA checking valid signature

      RSACryptoServiceProvider rsacheck = new RSACryptoServiceProvider();

      MD5 md5 = MD5.Create();

                  rsasign.FromXmlString(rsakey.ToXmlString(true));

      // signing data

      byte[] signature = rsasign.SignData(plainbytes, md5);

 

      // validating signed data

      rsacheck.FromXmlString(rsakey.ToXmlString(false));

      if (rsacheck.VerifyData(plainbytes, md5, signature))

            Console.WriteLine("Signature is correct.");

}

7.13.3. Sign and verify data with RSA using SignatureFormatter

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // this is a text to be signed

      string plaintext = "This is a sample text.";

      byte[] plainbytes = Encoding.ASCII.GetBytes(plaintext);

      // this instance represents keys used to signing and verification

      RSACryptoServiceProvider rsakey = new RSACryptoServiceProvider();

      // RSA to sign a plaintext

      RSACryptoServiceProvider rsasign = new RSACryptoServiceProvider();

      // RSA checking valid signature

      RSACryptoServiceProvider rsacheck = new RSACryptoServiceProvider();

 

      // setting private key to signing RSA class

      rsasign.FromXmlString(rsakey.ToXmlString(true));

      // setting public key to checking RSA class

      rsacheck.FromXmlString(rsakey.ToXmlString(false));

 

      // signature formatter used to sign a data

      AsymmetricSignatureFormatter signform = new RSAPKCS1SignatureFormatter(rsasign);

      signform.SetHashAlgorithm("SHA1");

      // create hash instance

      HashAlgorithm hashAlg = HashAlgorithm.Create("SHA1");

      // signing data

      byte[] signature = signform.CreateSignature(hashAlg.ComputeHash(plainbytes));

      // deformatter is used to check signed data

      AsymmetricSignatureDeformatter signcheck = new RSAPKCS1SignatureDeformatter(rsacheck);

      signcheck.SetHashAlgorithm("SHA1");

      // check signature

      if (signcheck.VerifySignature(hashAlg.ComputeHash(plainbytes), signature))

            Console.WriteLine("Signature is correct.");

}

7.13.4. Sign and verify data with DSA

DSA uses as a hash algorithm SHA, there is not possible to choose other hashing method.

That is why SHA is used in example bellow.

 

Namespaces:

using System;

using System.Text;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // this is a text to be signed

      string plaintext = "This is a sample text.";

      byte[] plainbytes = Encoding.ASCII.GetBytes(plaintext);

      DSACryptoServiceProvider dsa = new DSACryptoServiceProvider();

      DSASignatureFormatter dsaform = new DSASignatureFormatter();

      SHA1 sha = new SHA1Managed();

      dsaform.SetKey(dsa);

      // sign data with hash and key

      byte[] signature = dsaform.CreateSignature(sha.ComputeHash(plainbytes));

 

      DSASignatureDeformatter dsadeform = new DSASignatureDeformatter(dsa);

      // check signed data if correct

      if (dsadeform.VerifySignature(sha.ComputeHash(plainbytes), signature))

            Console.WriteLine("Signature is correct.");

}

7.14. Key exchange methods and classes

7.14.1. Exchange symmetric key between two clients using OAEP

Namespaces:

using System;

using System.Text;

using System.IO;

using System.Security.Cryptography;

 

Code:

static void Main(string[] args)

{

      // this is a key pair for a second client B who will recieve symmetric key

      RSACryptoServiceProvider rsaKeyB = new RSACryptoServiceProvider();

      // create instance of symmetric key used by client A and send to client B

      RC2 rc2A = RC2.Create();

      // generate key to be sent to client B

      rc2A.GenerateKey();

      // this is a formatter for client A, it will be used for RC2 key from client A

      RSAOAEPKeyExchangeFormatter formA = new RSAOAEPKeyExchangeFormatter();

      // client A knows public key of client B, he can use it to encrypt his RC2 key

      // client A creates object representing client B's public key

      RSACryptoServiceProvider rsaPublicB = new RSACryptoServiceProvider();

      // sets public key using XML (see chapter '')

      rsaPublicB.FromXmlString(rsaKeyB.ToXmlString(false));

      // set key to formatter

      formA.SetKey(rsaPublicB);

      // key for exchange to send it to client B

      byte[] dataKeyA = formA.CreateKeyExchange(rc2A.Key);

 

      // now use data recieved by client B

      // first decrypt data encrypted by client A's public key

      // use OAEP deformatter, deformatter must be always the same type (PKCS1 or OAEP)

      RSAOAEPKeyExchangeDeformatter deformB = new RSAOAEPKeyExchangeDeformatter();

      deformB.SetKey(rsaKeyB);

      // decrypt symmetric key from recieved data

      byte[] dataKeyB = deformB.DecryptKeyExchange(dataKeyA);

 

      // create symmetric class instance

      RC2 rc2B = RC2.Create();

      rc2B.Key = dataKeyB;

      //!!!! NOTE !!!!

      // All other data related to symmetric cipher must be set, this is just principal

      // sample but to make it functional vector, Mode and Padding must be set!

      // for sampling purposes it's done here directly without any transmission

 

      // see what happens when algorithm is not correctly set even with using correct key

      check(rc2A, rc2B);

 

      // now set other properties

      rc2B.IV = rc2A.IV;

      rc2B.Mode = rc2A.Mode;

      rc2B.Padding = rc2A.Padding;

 

      // test symmetric encryption of both clients A & B

      check(rc2A, rc2B);

}

 

public static void check(RC2 rc2A, RC2 rc2B)

{

      // data to be encrypted

      string cipherData = "This is a sample plaintext";

      // final encrypted data

      byte[] cipherbytes;

      // byte form of plaintext

      byte[] plainbytes = Encoding.ASCII.GetBytes(cipherData);

 

      // --------------- Client A encrypts plaintext ---------------

      MemoryStream ms = new MemoryStream();

      CryptoStream cs = new CryptoStream(ms,

            rc2A.CreateEncryptor(),

            CryptoStreamMode.Write);

      cs.Write(plainbytes, 0, plainbytes.Length);

      cs.Close();

      cipherbytes = ms.ToArray();

      ms.Close();

 

      // --------------- Client B decrypts plaintext ---------------

      MemoryStream ms1 = new MemoryStream(cipherbytes);

      CryptoStream cs1 = new CryptoStream(ms1,

            rc2B.CreateDecryptor(),

            CryptoStreamMode.Read);

      plainbytes = new Byte[cipherbytes.Length];

      cs1.Read(plainbytes, 0, cipherbytes.Length);

      cs1.Close();

      ms1.Close();

      Console.WriteLine("Cipher result: "+Encoding.ASCII.GetString(plainbytes));

}

7.15. Certificates

7.15.1. Create X509Certificate from file generated by makecert.exe

Microsoft provides tool called makecert.exe, which can be used for development purposes to generate certification file.

It can be used from command line in a following form:

 

makecert -n "CN=Skilldrive.com" sampleCertificate.cer

 

This will create certification file DER encoded and it can be directly used to create X509Certificate class instance. See sample code bellow:

 

7.15.2. Create X.509 certificate from base64 encoded certificates

.NET Framework provides classes to work with certificates, but those classes of version 1.0 and 1.1 are tight to DER encoding of .cer files. That is why base64 encoded certificates can’t be created and used. This is a workaround how to avoid this problem and to use base64 DER encoded certificates.

Namespaces:

using System;

using System.IO;

using System.Text;

using System.Security.Cryptography.X509Certificates;

 

Code:

      public static void Main(string[] args)

      {

            StreamReader stream = File.OpenText("samplecertificate.cer");

            String stringdata = stream.ReadToEnd();

            stream.Close();

 

            StringBuilder sb = new StringBuilder(stringdata);

            // this part of certificate must be removed to be able to create X509Certificate

            sb.Replace("-----BEGIN CERTIFICATE-----", "");

            sb.Replace("-----END CERTIFICATE-----", "");

   

            // convert base64 to bytes

            byte[] certbytes = Convert.FromBase64String(sb.ToString());

            X509Certificate cert = new X509Certificate(certbytes);

            Console.WriteLine(cert.GetName());

      }

7.15.3. Source library with CryptoAPI certificate mappings

public sealed class CertificateAPI

{

      [DllImport("CRYPT32.DLL", CharSet = CharSet.Auto, SetLastError = true)]

      public static extern uint CertOpenSystemStore(int hCryptProv, string storename);

      [DllImport("CRYPT32.DLL", CharSet = CharSet.Auto, SetLastError = true)]

      public static extern bool CertCloseStore(uint storeProvider, int flags);

      [DllImport("CRYPT32.DLL", CharSet = CharSet.Auto, SetLastError = true)]

      public static extern uint CertEnumCertificatesInStore(uint storeProvider, uint prevCertContext);

}

7.15.4. List of installed client’s certificates

Use CertificateAPI class.

Namespaces:

using System;

using System.Runtime.InteropServices;

using System.Security.Cryptography.X509Certificates;

 

Code:

static void Main(string[] args)

{

      uint hStore = CertificateAPI.CertOpenSystemStore(0, "My");

      uint hContext = 0;

      // list client certificates in "My" store (current user)

      // second parametr in method referes to previous context

      while ((hContext = CertificateAPI.CertEnumCertificatesInStore(hStore, hContext)) != 0)

      {

            // create certificate instance

            X509Certificate x509 = new X509Certificate((IntPtr)hContext);

            // get client name

            Console.WriteLine(x509.GetName());

            // get certificate issuer name

            Console.WriteLine(x509.GetIssuerName());

      }

      // store must be closed when used

      CertificateAPI.CertCloseStore(hStore, 0);

}

7.15.5. List of installed intermediate certification authorities

The code for this sample is the same as in 7.15.4 but initialization of system store is different.

 

Code:

uint hStore = CertificateAPI.CertOpenSystemStore(0, "CA");

7.15.6. List of installed root certificate authorities

The code for this sample is the same as in 7.15.4 but initialization of system store is different.

 

Code:

uint hStore = CertificateAPI.CertOpenSystemStore(0, "ROOT");

7.16. Data Protection API

Data Protection API (DPAPI) provides is a password-based protection service integrated with Windows’s CryptoAPI (DPAPI is provided since Windows 2000) and DPAPI is intended to be used to protect some sensitive data like passwords, private keys or database connection strings. DPAPI is very easy to use and very strong because it uses cryptographic standards to protect data but it’s password-based and the user password is the only one weakness of this technology.

There’re two modes how DPAPI can be used:

 

In this mode just logon user can access encrypted data and nobody else. In provided source library it’s set up by flag DataProtection.Store.USE_USER_STORE.

When machine mode is used then data are encrypted for specific machine and every user that logs on to that machine can access them. In provided source library it’s set up by flag DataProtection.Store.USE_MACHINE_STORE.

 

When application calls DPAPI methods (like CryptProtectData or CryptUnprotectData) then DPAPI calls LSA (Local Security Authority) over the RPC and encrypts data.

Also how data are encrypted differs from operating system (and on non-domain accounts this is extremely important), see list bellow:

 

Operating system/account

Encryption scheme

Non-domain accounts on Windows 2000

DPAPI encrypts its master keys with the user password, and stores a backup copy encrypted with an LSA secret. The LSA secret in turn is encrypted with the syskey.

Domain accounts on Windows 2000

DPAPI encrypts its master keys with the user password, and stores a backup copy encrypted with a domain secret.

Non-domain accounts on Windows XP

DPAPI encrypts its master keys with the user password, and stores a backup copy encrypted with the public key corresponding to the private key on the password reset disk.

Domain accounts on Windows XP

DPAPI encrypts its master keys with the user password, and stores a backup copy encrypted with a domain secret.

 

As can be seen, user’s passwords are the basic concept for encryption in DPAPI and also previous operating systems. This was the main issue in Windows till Windows 2000 because previous version were using primary LM Hash (or NTLM) to generate keys to encrypt data.

7.16.1.1. LM Hash

LM Hash was inveted by IBM (and not by Microsoft as many people think) in 70th to be used in IBM 360/370 series. Later Microsoft adopted it in its products in LAN Manager and started to call this technology LM Hash. LAN Manager was a technology used by IBM and Microsoft when those two companies was in alliance and latter when Microsoft droped this alliance LM Hash concept was kept in futher Windows versions to keep backward compatibility with previous ones (primary Windows 3.x).

7.17. Basic principles of DPAPI

According to Microsoft’s researches security is the most important topic of today’s IT specialist. This series of articles is trying to help to understand some of the core security concepts and how to use them in our applications.

Microsoft is marketing of its technology called DPAPI – Data Protection API. But what is this technology and how does it work? So first let’s see what DPAPI is and understand its principles.

 

DPAPI is a one of the core parts of CryptoAPI and was introduced in Windows 2000 operating system and it is implemented in crypt32.dll. Its purpose is to give protection to data encrypted by keys specific to a user and this protection mechanism is password based. This means that all power of DPAPI is as strong as strong user’s passwords are.

 

How DPAPI works?

The process of encryption/decryption through DPAPI is very simple and is illustrated on the next figure:

As it can be seen on the figure the process takes 7 basic conceptual steps:

 

  1. First primary user’s key is generated from credentials as user’s password or some user’s data on smartcard (this is another scenario with smartcard and is not covered here).
  2. After that master key is generated and it is encrypted by primary user’s key using Triple-DES.
  3. Whenever a call is made to DPAPI functions then session key is generated.
  4. Session key is derived from master key, random data and optionally from entropy provided to called function.
  5. Session key is used to encrypt data. This key is not stored anywhere, just random data and entropy is added to encrypted data and stored together.
  6. When user decrypts data using DPAPI it generates session key from master key and random data plus entropy stored with encrypted data.
  7. Master keys are usually regenerated after some time period (usually months) and those master keys are encrypted by primary user’s key and stored in user’s profile. When user’s password is changed (and primary key is derived from password) then master keys are re-encrypted using a new user’s key derived from a new password.

 

This is an encryption process and decryption runs in a way described further in second figure:

 

More details about this process can be found on http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/windataprotection-dpapi.asp.

 

As it can be seen session keys are used to encrypt/decrypt data and those keys are derived from master keys that are stored in profile and thay are changed in some period of time (each three months a new master key is generated). But all previous master keys are stored in profile and are encrypted by current password.

What happens when your application will try to decrypt some data using DPAPI and you’ll have to call an old master key? First it’s important to know that data encrypted with DPAPI store together random data, entropy and also GUID uniquely identifying master key. With this GUID master key can be found in user’s profile and then it’s used to generate session key (as described in first figure) and finally decrypt data.

Here we move to the most important part of DPAPI – user’s profile. It’s essential to understand how master keys are stored otherwise many applications will raise many exceptions in production environment while on developer machines will be working fine.

As second figure presents, all master keys are stored in profile and this profile must be loaded otherwise master keys will be inaccessible and all DPAPI encrypted data too.

This is the most important think to remember that master keys are stored in profiles and now we’ll see how to use DPAPI and how to work with profiles in different scenarios.

7.17.1. User’s profile

User’s profile is related to any account working on a machine. When user logs on to a machine a new profile is created in user’s directory together with some specific files (primary ntuser.dat and ntuser.dat.log) and subdirectories. This profile is automatically created only when you log on even when account is setup as batch job by administrator.

All user’s data are stored and encrypted in user’s profile including his registry, settings and relevant keys used by encryption services like DPAPI.

Generally there are four types of user profiles:

 

 

 

7.17.2. Source library with DPAPI methods

Here is the code that maps DPAPI methods to .NET environment and it can be used then by any .NET application. It’s important to mapped references to library crypt32.dll.

This code has been provided by J.D. Meier, Alex Mackman, Michael Dunner and Srinath Vasireddy on MSDN in this article. It’s very nicely written common library mapping DPAPI functions to .NET and it’s recommended to use. Here is just a copy for offline usage in other samples (more details on MSDN).

 

Code:

using System;

using System.Text;

using System.Runtime.InteropServices;

namespace dpapiLibrary

{

      public class DataProtection

      {

            [DllImport("Crypt32.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]

            private static extern bool CryptProtectData(ref DATA_BLOB pDataIn, String szDataDescr, ref DATA_BLOB pOptionalEntropy, IntPtr pvReserved, ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct, int dwFlags, ref DATA_BLOB pDataOut);

            [DllImport("Crypt32.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]

            private static extern bool CryptUnprotectData(ref DATA_BLOB pDataIn, String szDataDescr, ref DATA_BLOB pOptionalEntropy, IntPtr pvReserved, ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct, int dwFlags, ref DATA_BLOB pDataOut);

            [DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]

            private unsafe static extern int FormatMessage(int dwFlags, ref IntPtr lpSource, int dwMessageId, int dwLanguageId, ref String lpBuffer, int nSize, IntPtr * Arguments);

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]

            internal struct DATA_BLOB

            {

                  public int cbData;

                  public IntPtr pbData;

            }

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]

            internal struct CRYPTPROTECT_PROMPTSTRUCT

            {

                  public int cbSize;

                  public int dwPromptFlags;

                  public IntPtr hwndApp;

                  public String szPrompt;

            }

            static private IntPtr NullPtr = ((IntPtr)((int)(0)));

            private const int CRYPTPROTECT_UI_FORBIDDEN = 0x1;

            private const int CRYPTPROTECT_LOCAL_MACHINE = 0x4;

            public enum Store { USE_MACHINE_STORE = 1, USE_USER_STORE };

            private Store store;

            public DataProtection(Store tempStore)

            {

                  store = tempStore;

            }

            public byte[] Encrypt(byte[] plainText, byte[] optionalEntropy)

            {

                  bool retVal = false;

                  DATA_BLOB plainTextBlob = new DATA_BLOB();

                  DATA_BLOB cipherTextBlob = new DATA_BLOB();

                  DATA_BLOB entropyBlob = new DATA_BLOB();

                  CRYPTPROTECT_PROMPTSTRUCT prompt = new CRYPTPROTECT_PROMPTSTRUCT();

 

                  InitPromptstruct(ref prompt);

 

                  int dwFlags;

 

                  try

                  {

                        try

                        {

                              int bytesSize = plainText.Length;

 

                              plainTextBlob.pbData = Marshal.AllocHGlobal(bytesSize);

                              if (IntPtr.Zero == plainTextBlob.pbData)

                              {

                                    throw new Exception("Unable to allocate plaintext buffer.");

                              }

 

                              plainTextBlob.cbData = bytesSize;

                              Marshal.Copy(plainText, 0, plainTextBlob.pbData, bytesSize);

                        }

                        catch (Exception ex)

                        {

                              throw new Exception("Exception marshalling data. " + ex.Message);

                        }

                        if (Store.USE_MACHINE_STORE == store)

                        {//Using the machine store, should be providing entropy.

                              dwFlags = CRYPTPROTECT_LOCAL_MACHINE | CRYPTPROTECT_UI_FORBIDDEN;

 

                              //Check to see if the entropy is null

                              if (null == optionalEntropy)

                              {//Allocate something

                                    optionalEntropy = new byte[0];

                              }

 

                              try

                              {

                                    int bytesSize = optionalEntropy.Length;

 

                                    entropyBlob.pbData = Marshal.AllocHGlobal(optionalEntropy.Length);;

                                    if (IntPtr.Zero == entropyBlob.pbData)

                                    {

                                          throw new Exception("Unable to allocate entropy data buffer.");

                                    }

 

                                    Marshal.Copy(optionalEntropy, 0, entropyBlob.pbData, bytesSize);

                                    entropyBlob.cbData = bytesSize;

                              }

                              catch (Exception ex)

                              {

                                    throw new Exception("Exception entropy marshalling data. " + ex.Message);

                              }

                        }

                        else

                        {//Using the user store

                              dwFlags = CRYPTPROTECT_UI_FORBIDDEN;

                        }

 

                        retVal = CryptProtectData(ref plainTextBlob, "", ref entropyBlob, IntPtr.Zero, ref prompt, dwFlags, ref cipherTextBlob);

                        if (false == retVal)

                        {

                              throw new Exception("Encryption failed. " + GetErrorMessage(Marshal.GetLastWin32Error()));

                        }

                  }

                  catch (Exception ex)

                  {

                        throw new Exception("Exception encrypting. " + ex.Message);

                  }

 

                  byte[] cipherText = new byte[cipherTextBlob.cbData];

 

                  Marshal.Copy(cipherTextBlob.pbData, cipherText, 0, cipherTextBlob.cbData);

                  return cipherText;

            }

            public byte[] Decrypt(byte[] cipherText, byte[] optionalEntropy)

            {

                  bool retVal = false;

                  DATA_BLOB plainTextBlob = new DATA_BLOB();

                  DATA_BLOB cipherBlob = new DATA_BLOB();

                  CRYPTPROTECT_PROMPTSTRUCT prompt = new CRYPTPROTECT_PROMPTSTRUCT();

 

                  InitPromptstruct(ref prompt);

                  try

                  {

                        try

                        {

                              int cipherTextSize = cipherText.Length;

 

                              cipherBlob.pbData = Marshal.AllocHGlobal(cipherTextSize);

                              if (IntPtr.Zero == cipherBlob.pbData)

                              {

                                    throw new Exception("Unable to allocate cipherText buffer.");

                              }

 

                              cipherBlob.cbData = cipherTextSize;

                              Marshal.Copy(cipherText, 0, cipherBlob.pbData, cipherBlob.cbData);

                        }

                        catch (Exception ex)

                        {

                              throw new Exception("Exception marshalling data. " + ex.Message);

                        }

 

                        DATA_BLOB entropyBlob = new DATA_BLOB();

                        int dwFlags;

 

                        if (Store.USE_MACHINE_STORE == store)

                        {//Using the machine store, should be providing entropy.

                              dwFlags = CRYPTPROTECT_LOCAL_MACHINE | CRYPTPROTECT_UI_FORBIDDEN;

 

                              //Check to see if the entropy is null

                              if (null == optionalEntropy)

                              {//Allocate something

                                    optionalEntropy = new byte[0];

                              }

 

                              try

                              {

                                    int bytesSize = optionalEntropy.Length;

 

                                    entropyBlob.pbData = Marshal.AllocHGlobal(bytesSize);

                                    if (IntPtr.Zero == entropyBlob.pbData)

                                    {

                                          throw new Exception("Unable to allocate entropy buffer.");

                                    }

 

                                    entropyBlob.cbData = bytesSize;

                                    Marshal.Copy(optionalEntropy, 0, entropyBlob.pbData, bytesSize);

                              }

                              catch (Exception ex)

                              {

                                    throw new Exception("Exception entropy marshalling data. " + ex.Message);

                              }

                        }

                        else

                        {//Using the user store

                              dwFlags = CRYPTPROTECT_UI_FORBIDDEN;

                        }

 

                        retVal = CryptUnprotectData(ref cipherBlob, null, ref entropyBlob, IntPtr.Zero, ref prompt, dwFlags, ref plainTextBlob);

                        if (false == retVal)

                        {

                              throw new Exception("Decryption failed. " + GetErrorMessage(Marshal.GetLastWin32Error()));

                        }

 

                        //Free the blob and entropy.

                        if (IntPtr.Zero != cipherBlob.pbData)

                        {

                              Marshal.FreeHGlobal(cipherBlob.pbData);

                        }

 

                        if (IntPtr.Zero != entropyBlob.pbData)

                        {

                              Marshal.FreeHGlobal(entropyBlob.pbData);

                        }

                  }

                  catch (Exception ex)

                  {

                        throw new Exception("Exception decrypting. " + ex.Message);

                  }

 

                  byte[] plainText = new byte[plainTextBlob.cbData];

 

                  Marshal.Copy(plainTextBlob.pbData, plainText, 0, plainTextBlob.cbData);

                  return plainText;

            }

            private void InitPromptstruct(ref CRYPTPROTECT_PROMPTSTRUCT ps)

            {

                  ps.cbSize = Marshal.SizeOf(typeof(CRYPTPROTECT_PROMPTSTRUCT));

                  ps.dwPromptFlags = 0;

                  ps.hwndApp = NullPtr;

                  ps.szPrompt = null;

            }

            private unsafe static String GetErrorMessage(int errorCode)

            {

                  int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;

                  int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;

                  int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;

                  int messageSize = 255;

                  String lpMsgBuf = "";

                  int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;

                  IntPtr ptrlpSource = new IntPtr();

                  IntPtr prtArguments = new IntPtr();

                  int retVal = FormatMessage(dwFlags, ref ptrlpSource, errorCode, 0, ref lpMsgBuf, messageSize, &prtArguments);

 

                  if (0 == retVal)

                  {

                        throw new Exception("Failed to format message for error code " + errorCode + ". ");

                  }

 

                  return lpMsgBuf;

            }

      }

}

7.17.3. Use DPAPI to encipher application data into file

Namespaces:

using System;

using System.IO;

using System.Text;

// this is a reference on namespace of the above provided library in chapter 0

using dpapiLibrary;

 

Code:

static void Main(string[] args)

{

      // create new class instance from dpapiLibrary

      DataProtection dp = new DataProtection(DataProtection.Store.USE_MACHINE_STORE);

 

      // data to be encrypted

      string cipherData = "This is a sample plaintext";

 

      // final encrypted data

      byte[] cipherbytes;

 

      // byte form of plaintext

      byte[] plainbytes = Encoding.ASCII.GetBytes(cipherData);

 

      // encrypt data using DPAPI

      // there isn't used and entropy, so the security level is smaller

      cipherbytes = dp.Encrypt(plainbytes, null);

 

      // store data in local file

      StreamWriter writer = new StreamWriter(@".\Encrypted.txt");

 

      writer.WriteLine(Convert.ToBase64String(cipherbytes));

      writer.Close();

}

7.17.4. Use DPAPI to decipher application data from file

Namespaces:

using System;

using System.IO;

using System.Text;

// this is a reference on namespace of the above provided library in chapter

using dpapiLibrary;

 

Code:

static void Main(string[] args)

{

      // encrypted data in file

      string cipherData;

 

      // byte form of plaintext

      byte[] cipherbytes;

 

      // create new class instance from dpapiLibrary

      DataProtection dp = new DataProtection(DataProtection.Store.USE_MACHINE_STORE);

      StreamReader reader = new StreamReader(@".\Encrypted.txt");

 

      cipherbytes = Convert.FromBase64String(reader.ReadLine());

      // decrypt data using DPAPI

      // there isn't used and entropy, so the security level is smaller

      cipherData = Encoding.ASCII.GetString(dp.Decrypt(cipherbytes, null));

      Console.WriteLine(cipherData);

      reader.Close();

}

7.17.5. DPAPI used to encrypt data in file in isolated storage

Namespaces:

using System;

using System.IO;

using System.IO.IsolatedStorage;

using System.Text;

// this is a reference on namespace of the above provided library in chapter

using dpapiLibrary;

 

Code:

static void Main(string[] args)

{

      // data to be encrypted

      string cipherData = "This is a sample plaintext";

 

      // final encrypted data

      byte[] cipherbytes;

 

      // byte form of plaintext

      byte[] plainbytes = Encoding.ASCII.GetBytes(cipherData);

 

      // create new class instance from dpapiLibrary

      DataProtection dp = new DataProtection(DataProtection.Store.USE_MACHINE_STORE);

 

      // open isolated storage file

      // create new file to store data

      IsolatedStorageFile domStorage = IsolatedStorageFile.GetUserStoreForDomain();

      IsolatedStorageFileStream isfs = new IsolatedStorageFileStream("Encrypted.txt", FileMode.Create, domStorage);

      StreamWriter writer = new StreamWriter(isfs);

 

      cipherbytes = dp.Encrypt(plainbytes, null);

      writer.WriteLine(Convert.ToBase64String(cipherbytes));

      writer.Close();

      domStorage.Close();

}

7.17.6. DPAPI used to decrypt data from file in isolated storage

Namespaces:

using System;

using System.IO;

using System.IO.IsolatedStorage;

using System.Text;

// this is a reference on namespace of the above provided library in chapter

using dpapiLibrary;

 

Code:

static void Main(string[] args)

{

      // encrypted data in file

      string cipherData;

 

      // byte form of plaintext

      byte[] cipherbytes;

 

      // create new class instance from dpapiLibrary

      DataProtection dp = new DataProtection(DataProtection.Store.USE_MACHINE_STORE);

 

      // open isolated storage file

      // create new file to store data

      IsolatedStorageFile domStorage = IsolatedStorageFile.GetUserStoreForDomain();

      IsolatedStorageFileStream isfs = new IsolatedStorageFileStream("Encrypted.txt", FileMode.Open, domStorage);

      StreamReader reader = new StreamReader(isfs);

 

      cipherbytes = Convert.FromBase64String(reader.ReadLine());

 

      // decrypt data using DPAPI from isolated storage file

      cipherData = Encoding.ASCII.GetString(dp.Decrypt(cipherbytes, null));

      Console.WriteLine(cipherData);

      reader.Close();

      domStorage.Close();

}

7.17.7. Encrypt/Decrypt database connection string using DPAPI

This sample demonstrates using of DPAPI to keep connection string secret.

The scenario is simplified but can be easily extended to real usage in applications and ASP.NET.

First get connection string (from application parameter, from WebForm in case of ASP.NET or any other custom source) . In this sample we work with sample connection string (server=(local);Integrated Security=SSPI;database=Northwind) in application configuration file applicationName.exe.config:

 

<?xml version="1.0" encoding="utf-8"?>

<configuration>

<appSettings>

<add key="connectionstring" value="AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAatVYYV9QZUSJOPI1kIBbWwQAAAACAAAAAAADZgAAqAAAABAAAACVYDurlVoXSld1tYV3Edi3AAAAAASAAACgAAAAEAAAAFWaEZK3Rv37Vut7Z8KwDe5A_u65 ?AAHjftc1NhxvZylAoKQshCXTiv3aGdCH3/1Gl5UQQwJbT+lvkspwIgvuz2uIFIZ2IFngbYPOUlv7wy3eJOglNtJBQAAAAUHl7qdEeucMlGMKi8CJm23iCCVQ==" />

</appSettings>

</configuration>

 

Don’t use this XML file, it contains just sample connection string encrypted on different computer with different encryption key. Just insert there your one encrypted with DPAPI as described in previous samples and update your own configuration file. Your configuration file keep in application directory and then run the following code:

 

using System;

using System.Text;

using dpapiLibrary;

using System.Configuration;

 

class ConnectionString

{

      static void Main(string[] args)

      {

            // create new class instance from dpapiLibrary

            // for better security use DataProtection.Store.USE_USER_STORE

            DataProtection dp = new DataProtection(DataProtection.Store.USE_MACHINE_STORE);

           

            // Encrypt connection string

            string cipherData = ConfigurationSettings.AppSettings["connectionstring"];

 

            byte[] cipherbytes = Convert.FromBase64String(cipherData);

            string plaintext = Encoding.ASCII.GetString(dp.Decrypt(cipherbytes, null));

            Console.WriteLine(plaintext);

      }

}

7.17.8. Issues with user’s store and web services and COM+

User’s store is a good option with higher level of security when compared with machine store. It’s because data encrypted on machine level are accessible to all users who can access machine, but when USE_MACHINE_STORE flag is used then data can be encrypted and decrypted just by the only one user (specific user profile). User profile is important to encrypt/decrypt data and without this profile is impossible to get DPAPI data.

In case of web services and COM+ components (like Enterprise Services) user profile is not loaded to optimize performance and when programmer is calling DPAPI from them then exception is rised.

This can be solved by two solutions:

Win32 API provides method that can explicitly load user’s profile but this method requires administrator’s privileges to be called.

When service is started than Windows Service Control Manager (SCM) loads user’s profile automatically. This service can encapsulate web services or COM+ components (Enterprise Services) or service can load those components under the same user’s account (with loaded user’s profile).

7.17.9. Managed DPAPI

With new version of .NET Framework will be brought the possibility to use managed DPAPI that will encapsulate DPAPI in CryptoAPI package. There will be two primary classes:

Those two classes provide DPAPI functionality in those methods:

7.18. XML Signatures

7.18.1. Sign XML

Add reference to your project to system.security.dll (go to Solution Explorer, right click on References tree item, from popup menu choose Add reference... and on table pane .NET select library system.security.dll).

XML signatures are applied to arbitrary data within or external XML document. Signed data can assure of:

So what is a signed XML? It is XML document consisting of data objects that were hashed and encrypted into a digest, digest was then placed into an element together with other information. All that is signed by cryptographic methods.

XML signatures are represented by Signature element. Syntax of that element is following:

 

 

7.19. Isolated storage

Many applications stores data in files and their name and location must be carefully chosen with respect to protection of data from other applications (that could access those files and make data not consistent) or from security point of view. Also registry became popular one time and this idea was smart but also it caused many problems (like many problems with searching, backup and very confusing organization). That is why Microsoft introduced the idea of isolated storages which can be seen as a separate “file system and store” specific to each user and application domain (or other extending specifics) solving problems with separation of stored data by applications.

With isolated storage concept, data is always isolated by:

 

Isolated storages can be managed by administrators to enable access to them based on an appropriate trust level or administrators can remove all persisted user’s data. That access to isolated storage is based on permission carried by System.Security.Permissions.IsolatedStoragePermission class. Of course this is abstract class and is never instantiated there’s derived one (IsolatedStorageFilePermission) which specifies allowed usage of a private virtual file system.

 

Sometimes developers need to know location of isolated storage files. This location depends on operating system used by client and following table shows locations where are isolated storages created for some common Windows systems.

Operating system

Location in file system

Windows 98, Windows ME

- user profiles not enabled

Roaming = <SYSTEMROOT>\Application Data

Non-roaming = <SYSTEMROOT>\Local Settings\Application Data

Windows 98, Windows ME

- user profiles enabled

Roaming = <SYSTEMROOT>\Profiles\<USER>\Application Data

Non-roaming = Windows\Local Settings\Application Data

Windows NT 4.0

<SYSTEMROOT>\Profiles\<USER>\Application Data

Windows NT 4.0

- Service Pack 4

Roaming = <SYSTEMROOT>\Profiles\<USER>\Application Data

Non-roaming = <SYSTEMROOT>\Profiles\<USER>\Local Settings\Application Data

Windows 2000, Windows XP, Windows Server 2003

- upgrade from NT 4.0

Roaming = <SYSTEMROOT>\Profiles\<USER>\Application Data

Non-roaming = <SYSTEMROOT>\Profiles\<USER\Local Settings\Application Data

Windows 2000

- clean install (and upgrades from Windows 98 and NT 3.51)

Roaming = <SYSTEMDRIVE>\Documents and Settings\<USER\Application Data

Non-roaming = <SYSTEMDRIVE>\Documents and Settings\<USER>\Local Settings\Application Data

7.19.1. Storeadm.exe – administration of isolated storage in .NET

This tool is shipped with .NET Framework SDK and it enables to work with isolated storage. It can list isolated storages and managed them like cleaning of their content.

Storeadm.exe is located in SDK directory in subdirectory <SDKDIRECTORY>\FrameworkSDK\Bin.

7.19.1.1. Cleaning of isolated storage

Explicit removing of stored files is very important because isolated storage doesn’t work like garbage collector reclaiming all unused memory back again. With isolated storage storeadm.exe must be used to clean those stores and limitations of such work must be known and realized. The most important is that cleaning of isolated storage by storeadm.exe can be done only on behalf of current user (not all users), when calling this tool from command line. But this can be solved by using of MSI files which are deployed via Group Policy affecting all users. This is a standard solution, when MSI includes storeadm.exe with flag /quiet keeping running it without any dialog box.

7.19.1.2. Listing of content of isolated storage

7.19.2. Opening of isolated storages for current user and domain

This sample presents how to start working with isolated storages using the basic class IsolatedStorageFile. Using it it’s possible to create directories, files and do other I/O operations in that virtual file system. The code will create sample directory as shows the figure bellow

 

 

and then destroys it.

In the code are used two IsolatedStorageFile instances (domStorage and domStorageShort) presenting possible methods of their obtaining.

 

Namespaces:

using System;

using System.IO.IsolatedStorage;

 

Code:

static void Main(string[] args)

{

      // application domain specific storage

      IsolatedStorageFile domStorage = IsolatedStorageFile.GetStore(IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain | IsolatedStorageScope.User, null, null);

      // this is the same function as previous

      IsolatedStorageFile domStorageShort = IsolatedStorageFile.GetUserStoreForDomain();

      // create the directory in isolated storage

      domStorageShort.CreateDirectory("SampleFolderDomain");

      string[] dirs = domStorageShort.GetDirectoryNames("*");

      // ensure that directory has been created

      Console.WriteLine(dirs[0]);

      // delete the directory using second instance of IsolatedStorageFile class

      domStorage.DeleteDirectory("SampleFolderDomain");

      // ensure that directory has been deleted

      dirs = domStorageShort.GetDirectoryNames("*");

      if (dirs.Length == 0) Console.WriteLine("There are no directory in isolated storage");

      else Console.WriteLine(dirs[0]);

      // close isolated store files

      domStorageShort.Close();

      domStorage.Close();

}

7.19.3. Store data in file in isolated storage

Namespaces:

using System;

using System.IO;

using System.IO.IsolatedStorage;

 

Code:

static void Main(string[] args)

{

      IsolatedStorageFile domStorage = IsolatedStorageFile.GetUserStoreForDomain();

      // create new file to store data in

      IsolatedStorageFileStream isfs = new IsolatedStorageFileStream("SampleFile.txt", FileMode.Create, domStorage);

      // now do it like in the sample in chapter 9.2.4

      StreamWriter writer = new StreamWriter(isfs);

      // write data to file

      writer.WriteLine("This is a first line stored in isolated storage");

      writer.Flush();

      // close resources

      isfs.Close();

      domStorage.Close();

}

8. Network Operations

8.1.1. Retrieve DNS computer name

Code:

public static void Main(string[] args) {

      Console.WriteLine(“DNS: {0}”, System.Net.Dns.GetHostByName(“LocalHost”).HostName);

}

8.1.2. Retrieve NetBIOS computer name

Code:

      public static void Main(string[] args) {

      Console.WriteLine(“NetBIOS: {0}”, System.Environment.MachineName);

}

8.1.3. Obtain IP address and host

Namespaces:

using System;

using System.Net;

 

Code:

static void Main(string[] args)

{

      string host = Dns.GetHostName();

      Console.WriteLine("Hostname is: {0}", host);

      IPHostEntry entry = Dns.GetHostByName(host);

      foreach (IPAddress ip in entry.AddressList)

      {

            Console.WriteLine("IP address: " + ip.ToString());

      }

}

8.1.4. Send email in .NET environment

Namespaces:

using System;

using System.Web.Mail;

 

Code:

static void Main(string[] args)

{

      MailMessage mailMsg = new MailMessage();

      mailMsg.From = "jan.seda@skilldrive.com";

      mailMsg.To = "jseda@microsoft.com";

      mailMsg.Cc = "";

      mailMsg.Bcc = "";

      mailMsg.Subject = "Here goes a subject";

      mailMsg.Body = "Here goes email body";

      mailMsg.Priority = (MailPriority)1;

      mailMsg.Attachments.Add(new MailAttachment("c:\\links.txt"));

      SmtpMail.SmtpServer = "smarthost";

      SmtpMail.Send(mailMsg);

}

8.1.5. Getting online stock information

Namespaces:

using System;

using System.Net;

using System.IO;

 

Code:

class StockInfo

{

     static void Main()

     {

           HttpWebRequest  httprequest;

           HttpWebResponse httpresponse;

           Uri             responseUri = null, requestUri;

           StreamReader    bodyreader;

           string          bodytext = "", line;

           Stream          responsestream;

 

           requestUri = new Uri("http://finance.yahoo.com/d/quotes.csv?s=MSFT&f=sl1d1t1c1");

           httprequest = (HttpWebRequest) WebRequest.Create(requestUri);

           httpresponse = (HttpWebResponse) httprequest.GetResponse();

           responseUri = httpresponse.ResponseUri;

           responsestream = httpresponse.GetResponseStream();

 

           if (responsestream != null)

           {

                bodyreader = new StreamReader(responsestream);

 

                while ((line=bodyreader.ReadLine())!=null)

                {

                     bodytext+=line+"\r\n";

                }

                bodyreader.Close();

                bodyreader = null;

           }

 

           httpresponse.Close();

           httpresponse = null;

           httprequest = null;

 

           Console.WriteLine("{0} returns:\r\n{1}", responseUri.ToString(), bodytext);

     }

}

8.1.6. Retrieve email from POP3 mail server

Namespaces:

using System;

using System.IO;

using System.Text;

using System.Net.Sockets;

 

Code:

public static void Main ()

{

      const string host = "pop3.yourdomain.com";

      const string user = "youruseraccount";

      const string password = "yourpassword";

 

      // tcp client for pop3

      TcpClient tcp = new TcpClient();

 

      // connect to host to port 110 (pop3)

      tcp.Connect(host, 110);

      NetworkStream netStream = tcp.GetStream();

      StreamReader reader = new StreamReader(tcp.GetStream());

 

      // allocate bytes for buffered read by TCP stream

      string inBuffer = "";

      // sent bytes to mail server

      byte[] outBuffer;

      // read data into the buffer

      inBuffer = reader.ReadLine();

 

      // output data read from server (usually name of mail server with welcome message)

      Console.WriteLine(inBuffer);

 

      // authorize to the server (USER userName)

      outBuffer = Encoding.ASCII.GetBytes("USER " + user + "\r\n");

      netStream.Write(outBuffer, 0, outBuffer.Length);

 

      // response from server (OK)

      inBuffer = reader.ReadLine();

 

      // send password (PASS password)

      outBuffer = Encoding.ASCII.GetBytes("PASS " + password + "\r\n");

      netStream.Write(outBuffer, 0, outBuffer.Length);

 

      // response from server (OK - login)

      inBuffer = reader.ReadLine();

 

      Console.WriteLine("---------------------- Authenticated to server ----------------------");

 

      outBuffer = Encoding.ASCII.GetBytes("STAT" + "\r\n");

      netStream.Write(outBuffer, 0, outBuffer.Length);

 

      inBuffer = reader.ReadLine();

      Console.WriteLine(inBuffer);

 

      // retrieve first message from server (RETR messageNumber)

      outBuffer = Encoding.ASCII.GetBytes("RETR 1" + "\r\n");

      netStream.Write(outBuffer, 0, outBuffer.Length);

 

      inBuffer = reader.ReadLine();

      Console.WriteLine(inBuffer);

      while (!inBuffer.Equals("."))

      {

            inBuffer = reader.ReadLine();

            Console.WriteLine(inBuffer);

      }

 

      outBuffer = Encoding.ASCII.GetBytes("QUIT" + "\r\n");

      netStream.Write(outBuffer, 0, outBuffer.Length);

 

      // close tcp connection

      tcp.Close();

}

9. File operations

9.1. General IO operations

9.1.1. Get executing application’s path with reflection

Code:

      static void Main(string[] args)

      {

            string path;

            path = System.IO.Path.GetDirectoryName(

      System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);

            Console.WriteLine(path);

}

9.1.2. Get executing application’s path

Code:

static void Main(string[] args)

{

      // this shows application's path

Console.WriteLine(System.Windows.Forms.Application.StartupPath);

}

9.1.3. Classes working with file and directory information

9.1.4. Change file & folder attributes

Code:

using System;

using System.IO;

 

class ChangeAttrib

{

      static void Main(string[] args)

      {

            // arg[0] represent path to files and folder where attributes will be changed

            ChangeAttributes(args[0]);

      }

 

      public static void ChangeAttributes(string path)

      {

            DirectoryInfo dirInfo = new DirectoryInfo(path);

 

            // set directory attribute to appropriate

            dirInfo.Attributes = FileAttributes.Normal;

            foreach (FileSystemInfo file in dirInfo.GetFileSystemInfos())

            {

                  // here set appropriate attribute for file

                  file.Attributes = FileAttributes.Normal;

            }

 

            foreach (DirectoryInfo dir in dirInfo.GetDirectories())

            {

                  // do recursive calls to change attributes in subdirectories

                  ChangeAttributes(dir.FullName);

            }

      }

}

9.1.5. Recursive list of directories/subdirectories & files

Code:

using System;

using System.IO;

 

class Sample

{

      static void Main(string[] args)

      {

            DirectoryInfo dirInfo = new DirectoryInfo("c:\\Sample_path");

            RecursiveList(dirInfo);

      }

 

      private static void RecursiveList(DirectoryInfo dirInfo)

      {

            // first list all subdirectories

            DirectoryInfo[] subDirs = dirInfo.GetDirectories();

            foreach(DirectoryInfo subDir in subDirs)

            {

                  RecursiveList(subDir);

            }

 

            // list all files in current directory (as RecursiveList method is called)

            FileInfo[] dirFiles = dirInfo.GetFiles();

            foreach(FileInfo fileInfo in dirFiles)

            {

                  Console.WriteLine(fileInfo.FullName);

            }

      }

}

9.2. Reading and writing from/to files

Reading and writing to a file is done using a generic concept called a stream. The idea behind the stream lays a long time ago, when data are thought as a transfer from one point to another like a flow of data. In .NET environment you can find many classes representing this concept working with files or with memory data (see diagram bellow).

BufferedStream – this class is used to read and write to another stream. It is used for performance reasons, when caching of file data is used by underlying operating system.

MemoryStream -

The most important classes, regarding file operations, are

9.2.1. BufferedStream

Using of streams to do file IO is straightforward but slow with low performance (as was mentioned above). For that reason BufferedStream class exists and is more efficient. It can be used by any stream class. For file operations it’s possible to use FileStream, where buffering operations are already included.

9.2.2. Read from file using BufferedStream

Namespaces:

using System;

using System.Text;

using System.IO;

 

Code:

static void Main(string[] args)

{

      string path = "c:\\sample\\sample.xml";

      Stream instream = File.OpenRead(path);

      // create buffer for open stream

      BufferedStream bufin = new BufferedStream(instream);

      byte[] bytes = new byte[128];

      // read first 128 bytes of file

      bufin.Read(bytes, 0, 128);

      Console.WriteLine("Allocated bytes: "+Encoding.ASCII.GetString(bytes));

}

9.2.3. Read text from file

Namespaces:

using System;

using System.IO;

 

Code:

static void Main(string[] args)

{

      string fileName = "temp.txt";

      FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);

      StreamReader reader = new StreamReader(stream);

 

      while (reader.Peek() > -1) Console.WriteLine(reader.ReadLine());

      reader.Close();

}    

9.2.4. Write text to file

Namespaces:

using System;

using System.IO;

 

Code:

static void Main(string[] args)

{

      string fileName = "temp.txt";

      FileStream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);

      StreamWriter writer = new StreamWriter(stream);

 

      writer.WriteLine("This is my first line in a file");

      writer.Close();

}

9.2.5. Create file and write to it

This sample uses method CreateText() which creates a new file and returns StreamWriter object that writes to a file using UTF-8 encoding.

Namespaces:

using System;

using System.IO;

 

Code:

static void Main(string[] args)

{

      string fileName = "temp.txt";

      StreamWriter writer = File.CreateText(fileName);

 

      writer.WriteLine("This is my newly created file.");

      writer.Close();

}

9.2.6. Append text to file

Namespaces:

using System;

using System.IO;

 

Code:

static void Main(string[] args)

{

      try

      {

            string fileName = "temp.txt";

            // this appends text to existing file, if file doesn't exist it is created

            StreamWriter writer = File.AppendText(fileName);

            writer.WriteLine("This is the appended text.");

            writer.Close();

      }

      catch

      {

            Console.WriteLine("Error");

      }

}

9.2.7. Read from binary file

Namespaces:

using System;

using System.IO;

 

Code:

static void Main(string[] args)

{

      try

      {

            string fileName = "temp.txt";

            int letter = 0;

            FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);

            BinaryReader reader = new BinaryReader(stream);

 

            while (letter != -1)

            {

                  letter = reader.Read();

                  // chars are converted according to current Encoding settings

                  if (letter != -1) Console.Write((char)letter);

            }

            reader.Close();

            stream.Close();

      }

      catch

      {

            Console.WriteLine("Error");

      }

}

9.2.8. Write to binary file

Namespaces:

using System;

using System.IO;

 

Code:

static void Main(string[] args)

{

      try

      {

            string fileName = "temp.txt";

            // data to be stored

            int[] data = {0, 1, 2, 3, 4, 5};

            FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Write);

            BinaryWriter writer = new BinaryWriter(stream);

 

            for(int i=0; i<data.Length; i++)

            {

                  // numbers are stored in UTF-8 format (4 bytes), try to read it

                  writer.Write(data[i]);

            }

 

            writer.Close();

            stream.Close();

      }

      catch

      {

            Console.WriteLine("Error");

}

9.2.9. Watch file system for changes

.NET Framework provides class System.IO.FileSystemWatcher which listens to the file system changes. This sample presents how to use it.

Code:

using System;

using System.IO;

class WatcherSample

{

      static void Main(string[] args)

      {

            // watch for changes in application's directory and on all files

            FileSystemWatcher watcher = new FileSystemWatcher(System.Windows.Forms.Application.StartupPath, "*.*");

 

            // watch for file name and size changes

            watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;

            watcher.Changed += new FileSystemEventHandler(OnChange);

            watcher.Created += new FileSystemEventHandler(OnChange);

            watcher.Deleted += new FileSystemEventHandler(OnChange);

            watcher.Renamed += new RenamedEventHandler(OnChange);

            watcher.EnableRaisingEvents = true;

 

            // wait for key pressed to terminate the application

            Console.ReadLine();

      }

      private static void OnChange(object sender, FileSystemEventArgs e)

      {

            Console.WriteLine("File: {0} - change type: {1}", e.FullPath, e.ChangeType);

      }

      private static void OnChange(object sender, RenamedEventArgs e)

      {

            Console.WriteLine("File: {0} renamed to {1}", e.OldName, e.Name);

      }

}

10.         Text Manipulation & Internationalization

10.1. String operations

10.1.1. Append string

When appending is running for repeated number of times, StringBuilder class has very good performance results when compared with String class. It is recommended to use it in such cases.

Namespaces:

using System;

using System.Text;

 

Code:

      public static void Main(string[] args)

      {

            string sampleStr = "Some string to work with";

            StringBuilder sb = new StringBuilder();

            // append sample string, Append method is overloaded by many different layouts, see MSDN documentation

            sb.Append(sampleStr.ToCharArray());

            Console.WriteLine(sb.ToString());

            // this will append 'A' character to string in StringBuilder, this is a very appropriate use for StringBuilder

            sb.Append((char)0x41, 10);

            Console.WriteLine(sb.ToString());

      }

10.1.2. Inserting/Removing string

Namespaces:

using System;

using System.Text;

 

Code:

      public static void Main(string[] args)

      {

            string textToInsert = "---Inserted text---";

            // initiate StringBuilder instance with sample string

            StringBuilder sb = new StringBuilder("Here goes the insertion: text continues...");

            // insert sample string beginning on 24th possition in StringBuilder

            sb.Insert(24, textToInsert.ToCharArray());

            Console.WriteLine(sb.ToString());

            // remove inserted text from StringBuilder

            sb.Remove(24, textToInsert.Length);

            Console.WriteLine(sb.ToString());

      }

10.1.3. Replace string

Namespaces:

using System;

using System.Text;

 

Code:

      public static void Main(string[] args)

      {

            string textToReplace = "---Replaced text---";

            // initiate StringBuilder instance with sample string

            StringBuilder sb = new StringBuilder("REPLACING! Text will be replaced by other string...");

            // very simple replacing

            sb.Replace("REPLACING!", textToReplace);

            Console.WriteLine(sb.ToString());

      }

10.1.4. Reverse string

.NET Framework still has not been providing built-in reversing functionality for strings. But this is usually done by chars as presents this sample.

Code:

      public static void Main(string[] args)

      {

            string sampleStr = "Sample string to be reverted.";

            char[] sampleChars = sampleStr.ToCharArray();

            Array.Reverse(sampleChars);

            Console.WriteLine("Original string: " + sampleStr);

            Console.WriteLine("Reverted string: " + new string(sampleChars));

      }

10.1.5. Reverse string using recursion

Recursive methods are sometimes used to reverse a string. But this consumes too much of system resources and that is why it is very slow when compared with Array.Reverse method provided by .NET Framework.

Code:

      class ReversingString

      {

            public static void Main(string[] args)

            {

                  int start = Environment.TickCount;

 

                  string sampleStr = "Sample string to be reverted.";

                  char[] sampleChars = sampleStr.ToCharArray();

                  for (int i = 0; i < 30000; i++)

                  {

                        // standard method to reverse a string through Array class

                        Array.Reverse(sampleChars);

                  }

 

                  int end = Environment.TickCount;

                  Console.WriteLine("Array reverse:" + (end - start));

 

                  start = Environment.TickCount;

                  for (int i = 0; i < 30000; i++)

                  {

                        // this recursion providing reversing of string value

                        reverse(sampleStr);

                  }

                  end = Environment.TickCount;

                  Console.WriteLine("Array reverse:" + (end - start));

            }

 

            public static string reverse(string revStr)

            {

                  if (revStr.Length == 1) return revStr;

                  else return reverse(revStr.Substring(1)) + revStr.Substring(0,1);

      }

10.2. Formatting numbers

10.2.1. Table with number formatting options

Parameter

Description

Link to a sample

‘c’/’C’

Currency

Formatting of numeric values to currency

‘d’/’D’

Decimal

General decimal format.

‘e’/’E’

Exponential

Formatting of floating point values to a scientific notation (exponential)

‘f’/’F’

Fixed point

Formatting of floating point values to specific number of decimals (fixed-point)

‘n’/’N’

Number

Formatting of numeric value to local culture specific number

‘r’/’R’

Roundtrip, ensures that numbers converted to strings will have the same value when they are converted back to numbers

Formatting of floating point value to roundtrip (can be converted back to number)

‘x’/’X’

Hexadecimal

Formatting of an integer value to a hexadecimal number

10.2.2. Formatting of numeric values to currency

Numbers can be converted to currency, which depends on local culture settings.

Code:

      public static void Main(string[] args)

      {

            // floating point value

            double num = 150.4683;

            Console.WriteLine(num.ToString("c"));

            Console.WriteLine(num.ToString("C"));

            // integer value

            Console.WriteLine(170.ToString("c"));

            Console.WriteLine(170.ToString("C"));

      }

10.2.3. Formatting of numeric values to currency with NumberFormatInfo

Namespaces:

using System;

using System.Globalization;

 

Code:

      public static void Main(string[] args)

      {

            // floating point

            double num = 150679.4683;

 

            // NumberFormatInfo will use 2 groups with one member

            int[] group = { 1, 1 };

            NumberFormatInfo numFormat = new NumberFormatInfo();

 

            numFormat.CurrencyDecimalDigits = 1;

 

            // default value for separator is '.'

            numFormat.CurrencyDecimalSeparator = ",";

 

            // set currency symbol to appropriate one

            numFormat.CurrencySymbol = "US Dollars: ";

 

            // group specifies each group that is separated

            numFormat.CurrencyGroupSizes = group;

 

            // groups are devided by '.' sign

            numFormat.CurrencyGroupSeparator = ".";

            // floating point conversion

            Console.WriteLine(num.ToString("C", numFormat));

            // integer value conversion

            Console.WriteLine(170.ToString("C", numFormat));

      }

10.2.4. Formatting of floating point values to a scientific notation (exponential)

Code:

      public static void Main(string[] args)

      {

            double num = Math.PI;

            Console.WriteLine(num.ToString("E"));

      }

10.2.5. Formatting of floating point values to specific number of decimals (fixed-point)

This sample presents the option how to format floating point values to more readable form, with less number of decimals. Numbers are not truncated, but rounded.

Code:

      public static void Main(string[] args)

      {

            double num = Math.PI;

            // specify number of decimals, in this case 5 numbers

            Console.WriteLine(num.ToString("F5"));

      }

10.2.6. Formatting of numeric value to local culture specific number

Code:

      public static void Main(string[] args)

      {

            int num = 255;

            double num2 = 255.67;

            // lower-case format

            Console.WriteLine(num.ToString("n"));

            // upper-case format

            Console.WriteLine(num2.ToString("N"));

      }

10.2.7. Formatting of floating point value to roundtrip (can be converted back to number)

Code:

      public static void Main(string[] args)

      {

            double num = 255.456;

            // lower-case format

            Console.WriteLine(num.ToString("r"));

            // upper-case format

            Console.WriteLine(num.ToString("R"));

      }

10.2.8. Formatting of an integer value to a hexadecimal number

Code:

      public static void Main(string[] args)

      {

            int num = 255;

            // hexadecimal value in lower-case format

            Console.WriteLine(num.ToString("x"));

            // hexadecimal value in upper-case format

            Console.WriteLine(num.ToString("X"));

      }

10.2.9. Formatting floating point values to a percentage

Floating point values can be formatted to percentage in two options. This is the first one where number of decimals is not specified.

Code:

      public static void Main(string[] args)

      {

            double num = 0.4683;

            Console.WriteLine(num.ToString("p"));

      }

10.2.10. Formatting floating point values to a percentage with limited number of decimals

Number of decimals can be specified directly to the parameter.

Code:

      public static void Main(string[] args)

      {

            double num = 0.4683;

            // number of decimals is one

            Console.WriteLine(num.ToString("p1"));

            // number of decimals is five

            Console.WriteLine(num.ToString("p5"));

      }

10.2.11. Formatting of floating point values to a percentage with NumberFormatInfo

Namespaces:

using System;

using System.Globalization;

 

Code:

      public static void Main(string[] args)

      {

            double num = 0.4683;

            NumberFormatInfo numFormat = new NumberFormatInfo();

            // number of decimals in percentage

            numFormat.PercentDecimalDigits = 3;

            // decimal separator, default is ","

            numFormat.PercentDecimalSeparator = ".";

            // there are three option for pattern used for percetage representation

            numFormat.PercentPositivePattern = 2;

            // Default symbol '%' can be changed to anything more appropriate

            numFormat.PercentSymbol = "Percentage: ";

            Console.WriteLine(num.ToString("p", numFormat));

      }

10.3. Formatting date and time

10.3.1. Table with date&time formatting options

Parameter

Pattern

Link to a sample

‘f’

dddd, MMMM dd, yyyy, hh:mm

tting DateTime to the short date&time pattern (dddd, MMMM dd, yyyy, hh:mm)

‘F’

Dddd, MMMM dd, yyyy hh:mm:ss

Formatting DateTime to the full date&time pattern (dddd, MMMM dd, yyyy hh:mm:ss)

‘d’

M/d/yyyy

Formating DateTime to the short date numerical pattern (M/d/yyyy)

‘D’

dddd, MMMM dd, yyyy

Formatting DateTime to the full date numerical pattern (dddd, MMMM dd, yyyy)

‘g’

M/d/yyyy hh:mm

Formatting DateTime to the short date&time numerical pattern (M/d/yyyy hh:mm)

‘G’

M/d/yyyy hh:mm:ss

Formatting DateTime to the full date&time numerical pattern (M/d/yyyy hh:mm:ss)

‘m’/’M’

MMMM dd

Formatting DateTime to the month name pattern (MMMM dd)

‘y’/’Y’

MMMM, yyyy

Formatting DateTime to the short date pattern (MMMM, yyyy)

‘T’

hh:mm:ss

Formatting DateTime to the long time pattern (hh:mm:ss)

‘t’

hh:mm

Formatting DateTime to the short time pattern (hh:mm)

‘r’/’R’

ddd, dd MMM yyyy HH':'mm':'ss 'GMT'

Formatting DateTime to the RFC1123 pattern (ddd, dd MMM yyyy HH':'mm':'ss 'GMT')

‘s’

yyyy'-'MM'-'dd HH':'mm':'ss'Z'

Formatting DateTime to sortable pattern

‘u’

yyyy'-'MM'-'dd HH':'mm':'ss'Z'

Formatting DateTime to universal sortable pattern (yyyy'-'MM'-'dd HH':'mm':'ss'Z')

‘U’

 

Formatting DateTime to full date&time using universal time

10.3.2. Formatting DateTime to the short date&time pattern (dddd, MMMM dd, yyyy, hh:mm)

DateTime class instances can be formatted to specific local culture string representing readable form of date.

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

            // formats datetime to readable form in language depending on local culture settings

            Console.WriteLine(dt.ToString("f"));

      }

10.3.3. Formatting DateTime to the full date&time pattern (dddd, MMMM dd, yyyy hh:mm:ss)

DateTime class instances can be formatted to specific local culture string representing readable form of date.

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

 

            // formats datetime to readable full-length form in language depending on local culture settings

            Console.WriteLine(dt.ToString("F"));

      }

10.3.4. Formating DateTime to the short date numerical pattern (M/d/yyyy)

Separator depends on local culture settings.

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

            Console.WriteLine(dt.ToString("d"));

      }

10.3.5. Formatting DateTime to the full date numerical pattern (dddd, MMMM dd, yyyy)

Separator and date format depends on local culture settings.

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

            Console.WriteLine(dt.ToString("D"));

      }

10.3.6. Formatting DateTime to the short date&time numerical pattern (M/d/yyyy hh:mm)

Separator depends on local culture settings.

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

            Console.WriteLine(dt.ToString("g"));

      }

10.3.7. Formatting DateTime to the full date&time numerical pattern (M/d/yyyy hh:mm:ss)

Separator depends on local culture settings.

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

            Console.WriteLine(dt.ToString("G"));

      }

10.3.8. Formatting DateTime to the month name pattern (MMMM dd)

Format and layout depends on local culture settings.

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

            // upper or lower case can be used with the same result

            Console.WriteLine(dt.ToString("m"));

            Console.WriteLine(dt.ToString("M"));

      }

10.3.9. Formatting DateTime to the short date pattern (MMMM, yyyy)

Format and layout depends on local culture settings.

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

            // upper or lower case can be used with the same result

            Console.WriteLine(dt.ToString("y"));

            Console.WriteLine(dt.ToString("Y"));

      }

10.3.10. Formatting DateTime to the long time pattern (hh:mm:ss)

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

            Console.WriteLine(dt.ToString("T"));

      }

10.3.11. Formatting DateTime to the short time pattern (hh:mm)

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

            Console.WriteLine(dt.ToString("t"));

      }

10.3.12. Formatting DateTime to the RFC1123 pattern (ddd, dd MMM yyyy HH':'mm':'ss 'GMT')

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

            // upper or lower case can be used with the same result

            Console.WriteLine(dt.ToString("r"));

            Console.WriteLine(dt.ToString("R"));

      }

10.3.13. Formatting DateTime to sortable pattern

This format is based on ISO 8601 and uses local time.

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

            Console.WriteLine(dt.ToString("s"));

      }

10.3.14. Formatting DateTime to universal sortable pattern (yyyy'-'MM'-'dd HH':'mm':'ss'Z')

The pattern is the same regardless of culture of format provider.

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

            Console.WriteLine(dt.ToString("u"));

      }

10.3.15. Formatting DateTime to full date&time using universal time

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

            Console.WriteLine(dt.ToString("U"));

      }

10.3.16. Formatting DateTime to custom format using DateTimeFormatInfo

Code:

      public static void Main(string[] args)

      {

            DateTime dt = DateTime.Now;

            DateTimeFormatInfo dtfi = new CultureInfo("en-GB", false).DateTimeFormat;

                 

            // english and german day names

            string[] dayNames = { "Monday/Montag", "Tuesday/Dienstag", "Wednesday/Mittwoch", "Thursday/Donnerstag", "Friday/Freitag", "Saturday/Samstag", "Sunday/Sonntag" };

            // english and german month names

            string[] monthNames = {"January/Januar", "February/Februar", "March/März", "April/April", "May/Mai", "June/Juni", "July/Juli", "August/August", "September/September", "October/Oktober", "November/November", "December/Dezember", ""};

 

            dtfi.DayNames = dayNames;

            dtfi.MonthNames = monthNames;

            // creating a pattern to present custom day and month names

            dtfi.FullDateTimePattern = "----- dd. MMMM (dddd) yyyy HH:mm:ss -----";

 

            Console.WriteLine(dt.ToString("F", dtfi));

      }

10.4. Custom number formatting

Formatting character

Name

Description

0

Zero placeholder

Digit of formatting number is copied to the position where ‘0’ character appears. When number of ‘0’ is different from range of formatting number, then number is rounded.

#

Digit placeholder

This is similar to ‘0’ character, but when 0 is significant digit, it is not never displayed.

.

Decimal point

First occurrence of ‘.’ represent decimal separator location. Any other ‘.’ are ignored.

,

Thousand separator and number scaling

 

%

Percentage placeholder

Number being formatted is multiplied by 100 before formatting.

e0/E0

e+0/e+0

e-0/E-0

Scientific notation

 

\

Escape character

Backslash is used as an escape sequence like ‘\n’ or similar ones. When ‘\\’ is used then ‘\’ character is displayed in final string.

'ABC '

"ABC"

Literal string

Characters enclosed in quotes are just copied into the final string. They don’t affect formatting.

;

Section separator

The character ‘;’ is used to separate sections: positive, negative, zero. See sample

Other

All other characters

All other characters are copied to the final string.

Complete version of this table and description of formatting parameters can be found in MSDN documentation.

10.4.1. Formatting of number to specific number of decimals

This sample is similar to usage of ‘f’ parameter (see Formatting of floating point values to specific number of decimals (fixed-point)). But this is not useful as separate formatting option but it is intended to be used in complex custom formats (it is presented in the following samples).

Code:

      public static void Main(string[] args)

      {

            double num = 0.8853;

            // final number will be rounded to 0.9

            string number = num.ToString("0.0");

            // final number will be rounded to .89

            string number2 = num.ToString("#.##");

            Console.WriteLine(number);

            Console.WriteLine(number2);

      }

10.4.2. Formatting of number with adding zeros

For many reasons developers needs to add zeros to a formatted number (like when formatted reports are created or in similar cases).

Code:

      public static void Main(string[] args)

      {

            double num = 0.8853;

            // final number will be formatted to output 00000.89, zeros are added according to format and decimals are rounded

            string number = num.ToString("00000.00");

            Console.WriteLine(number);

      }

10.4.3. Formatting of number to custom positive, negative and zero sections

Code:

      public static void Main(string[] args)

      {

            double numPos = 150.8853;

            double numNeg = -150.8853;

            // this will create custom formatting specific to US culture

            // positive value

            Console.WriteLine(numPos.ToString("USD #,##0.00;USD -#,##0.00;Zero value"));

            // negative value

            Console.WriteLine(numNeg.ToString("USD #,##0.00;USD -#,##0.00;Zero value"));

            // setting zero value and formatting

            numPos = 0;

            Console.WriteLine(numPos.ToString("USD #,##0.00;USD -#,##0.00;Zero value"));

      }

10.4.4. Formatting of number using custom CultureInfo and custom format

Namespaces:

using System;

using System.Globalization;

 

Code:

      public static void Main(string[] args)

      {

            double num = 110.8853;

            // this will use belgium formatting, 20 spaces will be added (this will include formatted number), finally N2 specifies parameter to format number and number of decimals

            Console.WriteLine(String.Format(new CultureInfo("nl-BE"), "{0,20:N2}", num));

            // 20 spaces padded to a number

            Console.Write(String.Format(new CultureInfo("nl-BE"), "{0,-20:N2}", num));

            Console.WriteLine("<- here is the end of spaces padded to a formatted number");

      }

10.5. Formatting strings

Strings can be formatted using String.Format() method. This method is overloaded to provide different behavior and here are some of these functionalities.

10.5.1. Simple string formatting with number parameter

Namespaces:

using System;

 

Code:

static void Main(string[] args)

{

      int[] param = {1111, 2222};

      // one parameter

      string result = String.Format("This is a parameter: {0}", param[0]);

      Console.WriteLine(result);

 

      // two parameters

      result = String.Format("This is a parameter 1: {0} and this a parameter 2: {1}", param[0], param[1]);

      Console.WriteLine(result);

 

      // two parameters, second version presenting type info

      result = String.Format("Parameter type is: {0} and first field contains: {1}", param, param[1]);

      Console.WriteLine(result);

}

10.6. Conversions

Convertion operations can be achieved by very important class System.Convert which provides methods converting base data types to another base data types.

10.6.1. Class Convert (many convertion methods)

10.6.2. Convert string to integer

Code:

      public static void Main(string[] args)

      {

            string convertStr = "150";

            // conversion of string to number

            int num = int.Parse(convertStr);

            num += 150;

            Console.WriteLine(num.ToString());

      }

10.6.3. Convert string to double

Code:

      public static void Main(string[] args)

      {

            // string must be in coorect format depending on CultureInfo, now is using the US defaults

            string convertStr = "0,5";

            // conversion of string to number

            double num = double.Parse(convertStr);

            num += 0.5;

            Console.WriteLine(num.ToString());

      }

10.6.4. Convert string to double using CultureInfo

Namespaces:

using System;

using System.Globalization;

 

Code:

      public static void Main(string[] args)

      {

            int num = 0;

            // culture representing US locales

            CultureInfo cultureUS = new CultureInfo("en-US");

            // culture representing german locales

            CultureInfo cultureBE = new CultureInfo("nl-BE");

            string convertStr = "150.500";

            try

            {

                  // conversion of string to number using US culture

                  num = int.Parse(convertStr, NumberStyles.AllowThousands, cultureUS);

            }

            catch (Exception e)

            {

                  Console.WriteLine("This is a wrong format for US culture:" + e.Message);

            }

            // conversion of string to number using dutch-belgium culture

            num = int.Parse(convertStr, NumberStyles.AllowThousands, cultureBE);

            num += 149500;

            Console.WriteLine(num.ToString());

}

10.6.5. Convert string to date

Namespaces:

using System;

using System.Globalization;

 

Code:

      public static void Main(string[] args)

      {

            string convertStr = "01/31/1973";

            // this is running fine when default culture is "en-US", otherwise it will rise an exception

            DateTime date = DateTime.Parse(convertStr);

            Console.WriteLine(date.ToString());

      }

10.6.6. Use regular expression to find and replace string inside of string

Namespaces:

using System;

using System.Text;

using System.Text.RegularExpressions;

 

Code:

static void Main(string[] args)

{

     // string to use to search inside of it

     string source = "sdfkwj3j23StRIngAaB34StrING3jijoaabSTRING";

     // string to find

     string stringToFind = "string";

     // string to use as a replacement

     string stringToReplace = "AAAA";

 

     Regex regex = new Regex(Regex.Escape(stringToFind), RegexOptions.IgnoreCase);

     string result = regex.Replace(source, stringToReplace);

 

     Console.WriteLine(result);

     Console.ReadLine();

}

10.6.7. Converting string to DateTime using CultureInfo

Namespaces:

using System;

using System.Globalization;

 

Code:

      public static void Main(string[] args)

      {

            string convertStr = "31.1.1973";

            // cultureinfo for Czech republic, it supports this date formatting

            CultureInfo cultureCZ = new CultureInfo("cs-CZ");

            CultureInfo cultureUS = new CultureInfo("en-US");

            DateTime date;

            try

            {

                  date = DateTime.Parse(convertStr, cultureUS);

                  Console.WriteLine(date.ToString());

            }

            catch (Exception e)

            {

                  Console.WriteLine("This is a wrong format for US culture:" + e.Message);

            }

            // converting on czech date format

            date = DateTime.Parse(convertStr, cultureCZ);

            Console.WriteLine(date.ToString());

      }

10.6.8. Convert time_t to DateTime

Code:

static void Main(string[] args)

{

      uint time_t = 1027530000;

      long win32FileTime = 10000000*(long)time_t + 116444736000000000;

      DateTime dt = DateTime.FromFileTimeUtc(win32FileTime);

      Console.WriteLine(dt.ToString());

}

10.6.9. Convert time_t to DateTime (shorter code)

Code:

static void Main(string[] args)

{

      uint time_t = 1027530000;

      DateTime dt = new DateTime(1970, 1, 1).AddSeconds(time_t);

      Console.WriteLine(dt.ToString());

}

10.6.10. Convert base64 encoded number to float

Namespaces:

using System;

using System.Text;

 

Code:

static void Main()

{

      // sample float values

      float [] realNums = {9.32675f, 102.48839f, -32.9457f};

      // allocate array with 4 bytes/item, float holds 4 bytes

      byte[] array1 = new byte[realNums.Length * 4];

      // copy float into allocated bytes in array

      Buffer.BlockCopy(realNums, 0, array1, 0, array1.Length);

      string base64 = Convert.ToBase64String(array1, 0, array1.Length);

 

      // convert base64 string to byte array - it will be the same length as array1

      byte[] array2 = Convert.FromBase64String(base64);

      // allocate float (must be 4-times shorter because each element in array is 4 byte element representing 1 float value

      float[] realNums2 = new float[array2.Length / 4];

 

      // move over the array and convert it to float, unsafe code must be used

      int byteIndex = 0;

      for(int floatIndex = 0; floatIndex < realNums2.Length; floatIndex ++)

      {

            unsafe

            {

                  // get pointer to starting address of element in array

                  fixed(byte* ptr = &array2[byteIndex])

                  {

                        // cast it to float

                        realNums2[floatIndex] = *((float*)ptr);

                  }

                  // add 4 to array index

                  byteIndex += sizeof(float);

            }

      }

      // step over and output elements in float array

      foreach(float value in realNums2)

      {

            Console.WriteLine(value);

      }

}

10.6.11. Convert file1/encoding1 into file2/encoding2

Namespaces:

using System;

using System.IO;

using System.Text;

 

Code:

class ConvertFileEncodingSample

{

 

      public static void Main(string[] args)

      {

            if (args.Length == 2)

            {

                  // path and file to be converted

                  string sPath = args[0];

                  //destination path and filename for finally converted file

                  string dPath = args[1];

                  // original encoding of first file - take ASCII encoding for this sample

                  Encoding sEncoding = Encoding.ASCII;

                  // final encoding of destination file - convert to UTF8

                  Encoding dEncoding = Encoding.Unicode;

 

                  ConvertFileEncoding(sPath, dPath, sEncoding, dEncoding);

            } else Console.WriteLine("Provide filenames: appName.exe sourceFile destFile");

      }

 

      public static void ConvertFileEncoding(string sPath, string dPath, Encoding sEncoding, Encoding dEncoding)

      {

            if (sEncoding == dEncoding)

            {

                  return;

            }

 

            // temporary file for conversion

            String tempFile = null;

 

            try

            {

                  tempFile = Path.GetTempFileName();

 

                  using (StreamReader sr = new StreamReader(sPath, sEncoding, false))

                  {

                        using (StreamWriter sw = new StreamWriter(tempFile, false, dEncoding))

                        {

                              int charsRead;

                              // allocate buffer for reading

                              char[] buffer = new char[128 * 1024];

                              // fill buffer with data from file

                              while ((charsRead = sr.ReadBlock(buffer, 0, buffer.Length)) > 0)

                              {

                                    // write data encoding with destination encoding

                                    sw.Write(buffer, 0, charsRead);

                              }

                        }

                  }

 

                  File.Move(tempFile, dPath);

            }

            finally

            {

                  // delete temporary file

                  File.Delete (tempFile);

            }

      }

}

10.7. Internationalization

.NET Framework provides various encodings. The description of them follows in the next chapters.

10.7.1. American Standard Code for Information Interchange (ASCII)

 

 

 

00

01

02

03

04

05

06

07

08

09

0A

0B

0C

0D

0E

0F

00

NUL

0x00

STX

0x01

SOT

0x02

ETX

0x03

EOT

0x04

ENQ

0x05

ACK

0x06

BEL

0x07

BS

0x08

HT

0x09

LF

0x0A

VT

0x0B

FF

0x0C

CR

0x0D

SO

0x0E

SI

0x0F

10

DLE

0x10

DC1

0x11

DC2

0x12

DC3

0x13

DC4

0x14

NAK

0x15

SYN

0x16

ETB

0x17

CAN

0x18

EM

0x19

SUB

0x1A

ESC

0x1B

FS

0x1C

GS

0x1D

RS

0x1E

US

0x1F

20

SP

0x20

!

0x21

"

0x22

#

0x23

$

0x24

%

0x25

&

0x26

'

0x27

(

0x28

)

0x29

*

0x2A

+

0x2B

,

0x2C

-

0x2D

.

0x2E

/

0x2F

30

0

0x30

1

0x31

2

0x32

3

0x33

4

0x34

5

0x35

6

0x36

7

0x37

8

0x38

9

0x39

:

0x3A

;

0x3B

<

0x3C

=

0x3D

>

0x3E

?

0x3F

40

@

0x40

A

0x41

B

0x42

C

0x43

D

0x44

E

0x45

F

0x46

G

0x47

H

0x48

I

0x49

J

0x4A

K

0x4B

L

0x4C

M

0x4D

N

0x4E

O

0x4F

50

P

0x50

Q

0x51

R

0x52

S

0x53

T

0x54

U

0x55

V

0x56

W

0x57

X

0x58

Y

0x59

Z

0x5A

[

0x5B

\

0x5C

]

0x5D

^

0x5E

_

0x5F

60

`

0x60

a

0x61

b

0x62

c

0x63

d

0x64

e

0x65

f

0x66

g

0x67

h

0x68

i

0x69

j

0x6A

k

0x6B

l

0x6C

m

0x6D

n

0x6E

o

0x6F

70

p

0x70

q

0x71

r

0x72

s

0x73

t

0x74

u

0x75

v

0x76

w

0x77

x

0x78

y

0x79

z

0x7A

{

0x7B

|

0x7C

}

0xD

~

0x7E

DEL

0x7F

10.7.2. ISO 10646 & Universal Character Set

The international standard ISO 10646 defines the Universal Character Set (UCS), which is representing nearly all languages over the world, and also special characters and symbols.

ISO 10646 works with 31-bit character sets, but except UCS (with large character base), is defined 16-bit subset of UCS called Basic Multilingual Plane (BMP) or Plane 0.

10.7.3. Unicode

In late 1980s have been started two independent projects working on character sets definition. The first one was ISO 10646 (see chapter 10.7.2) and the second one, Unicode Project, was organized by a consortium of software companies developing international software. Fortunately in 1991 founders and members of those projects realized that it would be better to work together on this effort and to create one single code table.

10.7.4. Class CultureInfo

Class CultureInfo is the most important one when speaking about internationalization, because this class provides culture-specific data, such as currency, numbers, language, country or calendar specifics.

 

Very important is knowledge about standards implemented in this class like definition of culture names. They are derived from RFC 1766 standard where is defined format as language[-country/region] (language is lowercase two-letter code derived from ISO-639-1 standard and country/region is uppercase two-letter code derived from ISO 3166 standard. This convention is used on CultureInfo class and there can be seen 3 types of different cultures:

Invariant culture is very specific and it should be used just by services or applications that don’t require culture specific data or don’t work with security services working with strings that are culture specific.

Invariant culture is represented by CultureInfo.InvariantCulture property when invariant culture is unique culture associated with English language, but not with any country/region. But what is the meaning? As it was written above, some applications don’t need to work with culture specifics, like system services. The advantage is working with data that are not specific to any culture and any user from any country can work with them without problems (or can easily convert them to culture specific format). InvariantCulture is intended to be used for data that are not directly presented to the user and are stored in culture independent format. As it was written, with this approach data can be easily access from any culture and converted to any culture.

Neutral culture is specified by only two-digit lowercase code like “de” for German or “en” for English and this culture is associated with language like German or English, but it isn’t associated with country/region where can be many other culture specifics. Typically English is shared (in general) by United States and United Kingdom, but there are many differences in other culture specifics like currency etc.

When programmers start to work with class CultureInfo, this is typical error, when this type of culture is used and set to Thred.CurrentThread.CurrentCulture property and exception is fired. Just try following sample:

 

public static void Main(string[] args)

{

   Thread.CurrentThread.CurrentCulture = new CultureInfo("en");

}

 

When this code is run then it will caused the exception with this message: “Unhandled Exception: System.NotSupportedException: Culture 'en' is a neutral culture. It can not be used in formatting and parsing and therefore can not be setas the thread's current culture.”. This error can be easily solved by using specific culture (see next section).

Specific culture is a culture that provides complete information about language and other culture specifics. In previous section was presented sample on “en” culture, where just English language was specified (it was just neutral culture). But this sample presents correct usage of CultureInfo class:

 

public static void Main(string[] args)

{

   // setting of specific culture for United Kingdom

   Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");

}

 

This sample will be running without any error or exception.

11.          Collections

11.1.1. ArrayList

Namespaces:

using System;

using System.Collections;

 

Code:

static void Main(string[] args)

{

      // create arraylist instance

      ArrayList myList = new ArrayList();

 

      myList.Add("First");

      myList.Add("Second");

      myList.Add("Third");

      myList.Add("Fourth");

      myList.Add("Fifth");

 

      // sort array items

      myList.Sort();

      // remove first two items

      myList.RemoveRange(0, 2);

 

      // output final 3 item after sorting

      foreach (Object item in myList)

      {

            Console.WriteLine(item);

      }

}

11.1.2. BitArray

Namespaces:

using System;

using System.Collections;

 

Code:

static void Main(string[] args)

{

      // initialize bit array with boolean values

      bool[] values1 = {true, false, true, false, true, false, false};

      BitArray array1 = new BitArray(values1);

 

      foreach (Object item in array1)

      {

            // output value will the same as declared in array

            Console.WriteLine(item);

      }

 

      Console.WriteLine("---------------Bits from Int---------------");

 

      // 110000.....0

      // first two bits are 1, others 0, first two out value will be true and others false

      int[] values2 = {3};

      array1 = new BitArray(values2);

 

      foreach (Object item in array1)

      {

            Console.Write(item + " ");

      }

}

11.1.3. HashTable

Namespaces:

using System;

using System.Collections;

 

Code:

static void Main(string[] args)

{

      Hashtable myHash = new Hashtable();

      // add key-pair values

      myHash.Add("First", 1);

      myHash.Add("Second", 2);

      myHash.Add("Third", 3);

 

      // move through hashtable data in cycle

      foreach (DictionaryEntry item in myHash)

      {

            Console.WriteLine(item.Key);

            Console.WriteLine(item.Value);

      }

 

      // get first item

      Console.WriteLine("Get first item: " + myHash["First"]);

      // this will not work, name of item is not identical

      Console.WriteLine("Get second item: " + myHash["SECOND"]);

}

11.1.4. Queue

Namespaces:

using System;

using System.Collections;

 

Code:

static void Main(string[] args)

{

      // queue works as FIFO

      Queue myQueue = new Queue();

 

      // put some values into queue

      myQueue.Enqueue(1);

      myQueue.Enqueue(2);

      myQueue.Enqueue(3);

 

      // get values from queue (first in - first out)

      while (myQueue.Count > 0)

      {

            Console.WriteLine(myQueue.Dequeue());

      }

}

11.1.5. SortedList

Namespaces:

using System;

using System.Collections;

 

Code:

static void Main(string[] args)

{

      // values in sorted list are sorted according to key

      SortedList myList = new SortedList();

 

      myList.Add(12, "December");

      myList.Add(9, "September");

      myList.Add(10, "October");

      myList.Add(4, "April");

      myList.Add(5, "May");

      myList.Add(6, "June");

      myList.Add(2, "February");

      myList.Add(8, "August");

      myList.Add(7, "July");

      myList.Add(3, "March");

      myList.Add(11, "November");

      myList.Add(1, "January");

 

      // output values that are sorted according to key

      foreach (DictionaryEntry item in myList)

      {

            Console.WriteLine(item.Value);

      }

}

11.1.6. Stack

Namespaces:

using System;

using System.Collections;

 

Code:

static void Main(string[] args)

{

      // stack works as FILO

      Stack myStack = new Stack();

 

      myStack.Push(1);

      myStack.Push(2);

      myStack.Push(3);

 

      // get values from stack (last in - last out)

      while (myStack.Count > 0)

      {

            Console.WriteLine(myStack.Pop());

      }

}

12.         Time Operations

12.1.1. Time measuring (TickCount and Ticks property)

Code:

      public static void Main(string[] args)

      {

            // TickCount is less accurate property

            int start = Environment.TickCount;

            // Ticks are measuring in 100 nanoseconds intervals

            long startLong = DateTime.Now.Ticks;

 

            // this is a sample cycle to generate some workload

            for (int i = 0; i < 3000000; i++)

            {

                  // here do some operations to measure

            }

 

            int end = Environment.TickCount;

            long endLong = DateTime.Now.Ticks;

            Console.WriteLine("TickCount property:" + (end - start));

            Console.WriteLine("Ticks property:" + (endLong - startLong));

      }

12.1.2. Accurate time measuring

This sample can be used as a class to make very accurate measurement. Similar code was published by Microsoft: http://support.microsoft.com/default.aspx?scid=kb;en-us;Q306979

Code:

using System;

using System.Runtime.InteropServices;

 

namespace SampleApp

{

      public class CTimeMeasurement

      {

            private long m_Start;

            private long m_Finish;

            private long m_Frequency;

            // [DllImport("Coredll.dll")] – use it on PocketPC

            [DllImport("kernel32.dll")]

            extern private static short QueryPerformanceCounter(ref long x);

            // [DllImport("Coredll.dll")] – use it on PocketPC

            [DllImport("kernel32.dll")]

            extern private static short QueryPerformanceFrequency(ref long x);

            public CTimeMeasurement()

            {

                  Reset();

            }

            public void Start()

            {

                  QueryPerformanceFrequency(ref m_Frequency);

                  QueryPerformanceCounter(ref m_Start);

            }

            public void Stop()

            {

                  QueryPerformanceCounter(ref m_Finish);

            }

            public void Reset()

            {

                  m_Start = 0;

                  m_Finish = 0;

                  m_Frequency = Int32.MaxValue;

            }

            public double Duration()

            {

                  return (m_Finish - m_Start) * 1.0 / m_Frequency;

            }

 

            public static void Main(string[] args)

            {

                  CTimeMeasurement measure = new CTimeMeasurement();

                  measure.Start();

                  Console.WriteLine("Some measured operations.");

                  measure.Stop();

                  Console.WriteLine("Duration of mearured operations:" + measure.Duration());

            }

      }

}

13.         Windows Management Instrumentation (WMI)

WMI is a very strong feature of Windows systems. But it’s not Microsoft specific because it is based on general OS independent standard defined by Desktop Management Task Force (DMTF - http://www.dmtf.org). WMI unifies two other standards as Common Management Information Protocol (CMIP) and Simple Network Management Protocol (SNMP).

DMTF defines many other standards related to management like Web Based Enterprise Management (WBEM) instead of other companies (BMC Software, Cisco Systems Inc., Compaq, Intel and Microsoft) which passed this specification to DMTF.

13.1. CIM Schema

CIM specification consists of two parts: CIM schema and language for working with this schema and data.

CIM schema is organized as a structure of classes with their methods, properties and qualifiers and this structure defines how classes are organized and rules for them.

Schema is devided into logical and physical domains, logical domains are:

13.2. WMI Architecture

13.3. WMI tools

Microsoft provides WMI tools that can be used to test or manage WMI classes and environment. There are four tools available:

 

 

Those tools are written using HTML and ActiveX and they provide management environment for WMI for local (“\root\CIMV2”) or remote computers (“\\remotesrv\root\default”) and their usage is easy and intuitive.

13.3.1. WMI Object Browser

WMI Object Browser is primary tool for easy working with CIM classes (primary Win32 ones) organized in hierarchy view. Those classes represent current environment settings and user doesn’t use any WQL to select specific classes or values. Usage of Object Browser is not suitable for any WQL testing but can help to imagine organization of classes in CIM repository and their dependenties and parent classes.

 

13.3.2. WMI CIM Studio

WMI CIM Studio is great tool for advanced operations like creation of custom MOF files, quering CIM repository using WQL and changing properting and calling methods of classes (like in Object Browser).

CIM Studio is recommended to use when you’re tryting to use some WQL queries in .NET environment with ManagementQuery derived classes (like in samples listed in this chapter).

13.3.3. WMI Event Registration Tool

13.3.4. WMI Event Viewer

WMI Event Viewer allows to receive any events from WMI infrastructure.

 

13.4. WMI plug-in for Visual Studio .NET 2003

Microsoft provides a plugin that can be easily used in Visual Studio .NET and can be download from Microsoft website.

It is automatically installed into Server explorer as another extension (see figure below):

 

WMI Server Explorer extension is very easy to use. Follow those steps to start basic WMI monitoring:

 

13.5. List of WMI Classes

 

13.5.1. Working with WMI on remote machine

Namespaces:

using System;

using System.Management;

 

Code:

static void Main(string[] args)

{

      ConnectionOptions options = new ConnectionOptions();

      options.Username = @"domain\username";

      options.Password = "password";

      ManagementScope scope = new ManagementScope(@"\\machine_name\root\cimv2", options);

 

      scope.Connect();

 

      try

      {

            // do some work with WMI

      }

      catch (Exception e)     {}

}

13.5.2. Get computer info (domain, model etc.)

Namespaces:

using System;

using System.Management;

 

Code:

static void Main(string[] args)

{

      WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_ComputerSystem");

      ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

      foreach (ManagementObject mo in find.Get())

      {

            Console.WriteLine("Computer belongs to domain................" + mo["Domain"]);

            Console.WriteLine("Computer manufacturer....................." + mo["Manufacturer"]);

            Console.WriteLine("Model name given by manufacturer.........." + mo["Model"]);

      }

}

13.5.3. Get computer info (vendor, UUID, type)

Namespaces:

using System;

using System.Management;

 

Code:

static void Main(string[] args)

{

      WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_ComputerSystemProduct");

      ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

      foreach (ManagementObject mo in find.Get())

      {

      Console.WriteLine("Description..........................................." + mo["Description"]);

            Console.WriteLine("Identifying number (usually serial number)............" + mo["IdentifyingNumber"]);

            Console.WriteLine("Commonly used product name............................" + mo["Name"]);

            Console.WriteLine("Universally Unique Identifier of product.............." + mo["UUID"]);

            Console.WriteLine("Vendor of product....................................." + mo["Vendor"]);

      }

}

13.5.4. Get data about operating system

Namespaces:

using System;

using System.Management;

 

Code:

class WMIOperatingSystemSample

{

      static void Main(string[] args)

      {

            WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_OperatingSystem");

            ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

            foreach (ManagementObject mo in find.Get())

            {

                  Console.WriteLine("Boot device name........................." + mo["BootDevice"]);

                  Console.WriteLine("Build number............................." + mo["BuildNumber"]);

                  Console.WriteLine("Caption.................................." + mo["Caption"]);

                  Console.WriteLine("Code page used by OS....................." + mo["CodeSet"]);

                  Console.WriteLine("Country code............................." + mo["CountryCode"]);

                  Console.WriteLine("Latest service pack installed............" + mo["CSDVersion"]);

                  Console.WriteLine("Computer system name....................." + mo["CSName"]);

                  Console.WriteLine("Time zone (minute offset from GMT........" + mo["CurrentTimeZone"]);

                  Console.WriteLine("OS is debug build........................" + mo["Debug"]);

                  Console.WriteLine("OS is distributed across several nodes..." + mo["Distributed"]);

                  Console.WriteLine("Encryption level of transactions........." + mo["EncryptionLevel"] + " bits");

                  Console.WriteLine("Priority increase for foreground app....." + GetForeground(mo));

                  Console.WriteLine("Available physical memory................" + mo["FreePhysicalMemory"] + " kilobytes");

                  Console.WriteLine("Available virtual memory................." + mo["FreeVirtualMemory"] + " kilobytes");

                  Console.WriteLine("Free paging-space withou swapping........" + mo["FreeSpaceInPagingFiles"]);

                  Console.WriteLine("Installation date........................" + ManagementDateTimeConverter.ToDateTime(mo["InstallDate"].ToString()));

                  Console.WriteLine("What type of memory optimization........." + (Convert.ToInt16(mo["LargeSystemCache"]) == 0 ? "for applications" : "for system performance"));

                  Console.WriteLine("Time from last boot......................" + mo["LastBootUpTime"]);

                  Console.WriteLine("Local date and time......................" + ManagementDateTimeConverter.ToDateTime(mo["LocalDateTime"].ToString()));

                  Console.WriteLine("Language identifier (LANGID)............." + mo["Locale"]);

                  Console.WriteLine("Local date and time......................" + ManagementDateTimeConverter.ToDateTime(mo["LocalDateTime"].ToString()));

                  Console.WriteLine("Max# of processes supported by OS........" + mo["MaxNumberOfProcesses"]);

                  Console.WriteLine("Max memory available for process........." + mo["MaxProcessMemorySize"] + " kilobytes");

                  Console.WriteLine("Current number of processes.............." + mo["NumberOfProcesses"]);

                  Console.WriteLine("Currently stored user sessions..........." + mo["NumberOfUsers"]);

                  Console.WriteLine("OS language version......................" + mo["OSLanguage"]);

                  Console.WriteLine("OS product suite version................." + GetSuite(mo));

                  Console.WriteLine("OS type.................................." + GetOSType(mo));

                  // this is extension to OS addressing space, not available to Windows XP, Windows 2000, and Windows NT 4.0 SP4 and later

                  // Console.WriteLine("PAE enabled.............................." + mo["PAEEnabled "]);

                  Console.WriteLine("Number of Windows Plus!.................." + mo["PlusProductID"]);

                  Console.WriteLine("Version of Windows Plus!................." + mo["PlusVersionNumber"]);

                  Console.WriteLine("Type of installed OS....................." + GetProductType(mo));

                  Console.WriteLine("Registered user of OS...................." + mo["RegisteredUser"]);

                  Console.WriteLine("Serial number of product................." + mo["SerialNumber"]);

                  Console.WriteLine("Serial number of product................." + mo["SerialNumber"]);

                  Console.WriteLine("ServicePack major version................" + mo["ServicePackMajorVersion"]);

                  Console.WriteLine("ServicePack minor version................" + mo["ServicePackMinorVersion"]);

                  Console.WriteLine("Total number to store in paging files...." + mo["SizeStoredInPagingFiles"] + " kilobytes");

                  Console.WriteLine("Status..................................." + mo["Status"]);

                  Console.WriteLine("ServicePack minor version................" + mo["ServicePackMinorVersion"]);

                  Console.WriteLine("OS suite................................." + GetOSSuite(mo));

                  Console.WriteLine("Physical disk partition with OS.........." + mo["SystemDevice"]);

                  Console.WriteLine("System directory........................." + mo["SystemDirectory"]);

                  Console.WriteLine("Total virtual memory....................." + mo["TotalVirtualMemorySize"] + " kilobytes");

                  Console.WriteLine("Total physical memory...................." + mo["TotalVisibleMemorySize"] + " kilobytes");

                  Console.WriteLine("Version number of OS....................." + mo["Version"]);

                  Console.WriteLine("Windows directory........................" + mo["WindowsDirectory"]);

            }

      }

 

      private static string GetForeground(ManagementObject mo)

      {

            int i = Convert.ToInt16(mo["ForegroundApplicationBoost"]);

            switch (i)

            {

                  case 0:

                        return "None";

                  case 1:

                        return "Minimum";

                  case 2:

                        return "Maximum (defualt value)";

            }

            return "Boost not defined.";

      }

 

      private static string GetSuite(ManagementObject mo)

      {

            uint i = Convert.ToUInt32(mo["OSProductSuite"]);

            switch (i)

            {

                  case 1:

                        return "Small Business";

                  case 2:

                        return "Enterprise";

                  case 4:

                        return "BackOffice";

                  case 8:

                        return "Communication Server";

                  case 16:

                        return "Terminal Server";

                  case 32:

                        return "Small Business (Restricted)";

                  case 64:

                        return "Embedded NT";

                  case 128:

                        return "Data Center";

            }

            return "OS suite not defined.";

      }

 

      // this method covers just some of the Windows systems, not other ones from other vendors as Microsoft

      private static string GetOSType(ManagementObject mo)

      {

            uint i = Convert.ToUInt16(mo["OSType"]);

            switch (i)

            {

                  case 16:

                        return "WIN95";

                  case 17:

                        return "WIN98";

                  case 18:

                        return "WINNT";

                  case 19:

                        return "WINCE";

            }

            return "Other OS systems aren not covered.";

      }

 

      private static string GetProductType(ManagementObject mo)

      {

            uint i = Convert.ToUInt32(mo["ProductType"]);

            switch (i)

            {

                  case 1:

                        return "Work Station";

                  case 2:

                        return "Domain Controller";

                  case 3:

                        return "Server";

            }

            return "Product type not defined.";

      }

 

      private static string GetOSSuite(ManagementObject mo)

      {

            uint i = Convert.ToUInt32(mo["SuiteMask"]);

            // OS suite identification - final string

            string suite = "";

            if ((i & 1) == 1) suite += "Small Business";

            if ((i & 2) == 2)

            {

                  if (suite.Length > 0) suite += ", "; suite += "Enterprise";

            }

            if ((i & 4) == 4)

            {

                  if (suite.Length > 0) suite += ", "; suite += "Back Office";

            }

            if ((i & 8) == 8)

            {

                  if (suite.Length > 0) suite += ", "; suite += "Communications";

            }

            if ((i & 16) == 16)

            {

                  if (suite.Length > 0) suite += ", "; suite += "Terminal";

            }

            if ((i & 32) == 32)

            {

                  if (suite.Length > 0) suite += ", "; suite += "Small Business Restricted";

            }

            if ((i & 64) == 64)

            {

                  if (suite.Length > 0) suite += ", "; suite += "Embedded NT";

            }

            if ((i & 128) == 128)

            {

                  if (suite.Length > 0) suite += ", "; suite += "Data Center";

            }

            if ((i & 256) == 256)

            {

                  if (suite.Length > 0) suite += ", "; suite += "Single User";

            }

            if ((i & 512) == 512)

            {

                  if (suite.Length > 0) suite += ", "; suite += "Personal";

            }

            if ((i & 1024) == 1024)

            {

                  if (suite.Length > 0) suite += ", "; suite += "Blade";

            }

            return suite;

      }

}

13.5.5. Logoff, shutdown, reboot computer

Namespaces:

using System;

using System.Management;

 

Code:

static void Main(string[] args)

{

      object[] FLAG_LOGOFF = {0};

      object[] FLAG_SHUTDOWN = {1};

      object[] FLAG_REBOOT = {2};

      object[] FLAG_FORCELOGOFF = {4};

      object[] FLAG_FORCESHUTDOWN = {5};

      object[] FLAG_FORCEREBOOT = {6};

      object[] FLAG_POWEROFF = {8};

      object[] FLAG_FORCEPOWEROFF = {12};

 

      SelectQuery query = new SelectQuery("Win32_OperatingSystem");

      ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

 

      try

      {

            // convert parameter from command-line to choose appropriate shutdown mode

            int mode = Convert.ToInt32(args[0]);

 

            foreach (ManagementObject mo in find.Get())

            {

                  if (mode == (int)FLAG_LOGOFF[0])

                        mo.InvokeMethod("Win32Shutdown",  FLAG_LOGOFF);

                  else if (mode == (int)FLAG_SHUTDOWN[0])

                        mo.InvokeMethod("Win32Shutdown",  FLAG_SHUTDOWN);

                  else if (mode == (int)FLAG_REBOOT[0])

                        mo.InvokeMethod("Win32Shutdown",  FLAG_REBOOT);

                  else if (mode == (int)FLAG_FORCELOGOFF[0])

                        mo.InvokeMethod("Win32Shutdown",  FLAG_FORCELOGOFF);

                  else if (mode == (int)FLAG_FORCESHUTDOWN[0])

                        mo.InvokeMethod("Win32Shutdown",  FLAG_FORCESHUTDOWN);

                  else if (mode == (int)FLAG_FORCEREBOOT[0])

                        mo.InvokeMethod("Win32Shutdown",  FLAG_FORCEREBOOT);

                  else if (mode == (int)FLAG_POWEROFF[0])

                        mo.InvokeMethod("Win32Shutdown",  FLAG_POWEROFF);

                  else if (mode == (int)FLAG_FORCEPOWEROFF[0])

                        mo.InvokeMethod("Win32Shutdown",  FLAG_FORCEPOWEROFF);

                  else

                        Console.WriteLine("This is unknown shutdown mode.");

            }

      }

      catch (Exception e)

      {

            Console.WriteLine(e.Message);

      }

}

13.5.6. Get user’s desktop info

Namespaces:

using System;

using System.Management;

 

Code:

static void Main(string[] args)

{

      // get default desktop settings, where condition should be changed for appropriate user

      WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_Desktop WHERE Name = '.Default'");

      ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

      foreach (ManagementObject mo in find.Get())

      {

            // values can be changed in registry "HKEY_CURRENT_USER\Control Panel\Desktop"

            Console.WriteLine("Width of window borders..............................." + mo["BorderWidth"]);

            Console.WriteLine("ALT+TAB task switching allowed........................" + mo["CoolSwitch"]);

            // value is in ms

            Console.WriteLine("Lenght of time between cursor blincks................." + mo["CursorBlinkRate"]);

            Console.WriteLine("Show content of windows when are draged..............." + mo["DragFullWindows"]);

            // this indicates spacing of grid cells that windows are bound to

            Console.WriteLine("Grid settings for dragging windows...................." + mo["GridGranularity"]);

            Console.WriteLine("Grid settings for icon spacing........................" + mo["IconSpacing"]);

            Console.WriteLine("Font used for the names of icons......................" + mo["IconTitleFaceName"]);

            Console.WriteLine("Icon ront size........................................" + mo["IconTitleSize"]);

            Console.WriteLine("Wrapping of icon title................................" + mo["IconTitleWrap"]);

            Console.WriteLine("Name of the desktop profile..........................." + mo["Name"]);

            Console.WriteLine("Screen saver is active................................" + mo["ScreenSaverActive"]);

            Console.WriteLine("Name of the screen saver executable..................." + mo["ScreenSaverExecutable"]);

            Console.WriteLine("Is screen saver protected with password..............." + mo["ScreenSaverSecure"]);

            Console.WriteLine("Time to pass to activate screen saver................." + mo["ScreenSaverTimeout"]);

            Console.WriteLine("File name for desktop background......................" + mo["Wallpaper"]);

            Console.WriteLine("Wallpaper fills entire screen........................." + mo["WallpaperStretched"]);

            Console.WriteLine("Wallpaper is tiled...................................." + mo["WallpaperTiled"]);

      }

}

13.5.7. Determine computer type (workstation, server, controller etc.)

Namespaces:

using System;

using System.Management;

 

Code:

static void Main(string[] args)

{

      WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_ComputerSystem");

      ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

      foreach (ManagementObject mo in find.Get())

      {

            switch (Convert.ToInt32(mo["DomainRole"]))

            {

                  case 0:

                        Console.WriteLine("Standalone Workstation");

                        break;

                  case 1:

                        Console.WriteLine("Member Workstation");

                        break;

                  case 2:

                        Console.WriteLine("Standalone Server");

                        break;

                  case 3:

                        Console.WriteLine("Member Server");

                        break;

                  case 4:

                        Console.WriteLine("Backup Domain Controller");

                        break;

                  case 5:

                        Console.WriteLine("Primary Domain Controller");

                        break;

            }

      }

}

13.5.8. Determine physical computer features

Namespaces:

using System;

using System.Management;

 

Code:

class WMIEncloserSample

{

      static void Main(string[] args)

      {

            // get all processor unit on machine

            WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_SystemEnclosure");

            ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

            int i = 0;

            foreach (ManagementObject mo in find.Get())

            {

                  Console.WriteLine("------------------------- Chasis setting #" + i + " -------------------------");

                  Console.WriteLine("Chasis type..............................." + GetChasisType(mo));

                  Console.WriteLine("Description..............................." + mo["Description"]);

                  Console.WriteLine("Depth of physical package (in inches)....." + mo["Depth"]);

                  Console.WriteLine("Height of physical package (in inches)...." + mo["Height"]);

                  Console.WriteLine("Width of physical package (in inches)....." + mo["Width"]);

                  Console.WriteLine("Weight...................................." + mo["Weight"]);

                  Console.WriteLine("Service philosophy ......................." + GetServicePhilosophy(mo));

                  Console.WriteLine("Status...................................." + mo["Status"]);

                  Console.WriteLine("Property includes visible alarm..........." + mo["VisibleAlarm"]);

                  Console.WriteLine("Property includes visible alarm..........." + mo["VisibleAlarm"]);

                  Console.WriteLine("--------------------------------------------------");

                  i++;

            }

      }

 

      private static string GetChasisType(ManagementObject mo)

      {

            System.UInt16[] type = (System.UInt16[])mo["ChassisTypes"];

            String returnType = "";

            for (int i=0; i<type.Length; i++)

            {

                  if (i > 0) returnType += ", ";

                  switch (type[i])

                  {

                        case 1:

                              returnType += "Other";

                              break;

                        case 2:

                              returnType += "Unknown";

                              break;

                        case 3:

                              returnType += "Desktop";

                              break;

                        case 4:

                              returnType += "Low Profile Desktop";

                              break;

                        case 5:

                              returnType += "Pizza Box";

                              break;

                        case 6:

                              returnType += "Mini Tower";

                              break;

                        case 7:

                              returnType += "Tower";

                              break;

                        case 8:

                              returnType += "Portable";

                              break;

                        case 9:

                              returnType += "Laptop";

                              break;

                        case 10:

                              returnType += "Notebook";

                              break;

                        case 11:

                              returnType += "Hand Held";

                              break;

                        case 12:

                              returnType += "Docking Station";

                              break;

                        case 13:

                              returnType += "All in One";

                              break;

                        case 14:

                              returnType += "Sub Notebook";

                              break;

                        case 15:

                              returnType += "Space-Saving";

                              break;

                        case 16:

                              returnType += "Lunch Box";

                              break;

                        case 17:

                              returnType += "Main System Chassis";

                              break;

                        case 18:

                              returnType += "Expansion Chassis";

                              break;

                        case 19:

                              returnType += "SubChassis";

                              break;

                        case 20:

                              returnType += "Bus Expansion Chassis";

                              break;

                        case 21:

                              returnType += "Peripheral Chassis";

                              break;

                        case 22:

                              returnType += "Storage Chassis";

                              break;

                        case 23:

                              returnType += "Rack Mount Chassis";

                              break;

                        case 24:

                              returnType += "Sealed-Case PC";

                              break;

                  }

            }

            return returnType;

      }

 

      private static string GetServicePhilosophy(ManagementObject mo)

      {

            int i = Convert.ToInt16(mo["ServicePhilosophy"]);

            switch (i)

            {

                  case 0:

                        return "Unknown";

                  case 1:

                        return "Other";

                  case 2:

                        return "Service From Top";

                  case 3:

                        return "Service From Front";

                  case 4:

                        return "Service From Back";

                  case 5:

                        return "Service From Side";

                  case 6:

                        return "Sliding Trays";

                  case 7:

                        return "Removable Sides";

                  case 8:

                        return "Moveable";

            }

            return "Service philosophy not defined.";

      }

}

13.5.9. Rename computer name

Namespaces:

using System;

using System.Management;

 

Code:

static void Main(string[] args)

{

      WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_ComputerSystem");

      ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

      // set new computer name

      object[] methodArgs = {"SomeString"};

      foreach (ManagementObject mo in find.Get())

      {

            // invoke Rename method on computer object

            mo.InvokeMethod("Rename",  methodArgs);

      }

}

13.5.10. Get processor info

Namespaces:

using System;

using System.Management;

 

Code:

class WMIProcessorSample

{

      static void Main(string[] args)

      {

            // get all processor unit on machine

            WqlObjectQuery query = new WqlObjectQuery("Select * from Win32_Processor");

            ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

            int i = 0;

            foreach (ManagementObject mo in find.Get())

            {

                  Console.WriteLine("-------------- Processor #" + i + " --------------");

                  Console.WriteLine("Processor address width in bits.............." + mo["AddressWidth"]);

                  Console.WriteLine("Architecture of processor...................." + GetArchitecture(mo));

                  Console.WriteLine("Availability................................." + GetDeviceState(mo));

                  Console.WriteLine("Caption......................................" + mo["Caption"]);

                  Console.WriteLine("Processor address width in bits.............." + mo["AddressWidth"]);

                  Console.WriteLine("Device configuration........................." + GetConfigError(mo));

                  Console.WriteLine("Usage status of the processor................" + GetCpuStatus(mo));

                  Console.WriteLine("Current clock speed (in MHz)................." + mo["CurrentClockSpeed"]);

                  Console.WriteLine("Processor data width........................." + mo["DataWidth"]);

                  Console.WriteLine("Unique string identification................." + mo["DeviceID"]);

                  Console.WriteLine("External clock frequency....................." + mo["ExtClock"]);

                  Console.WriteLine("Processor data width........................." + mo["DataWidth"]);

                  Console.WriteLine("Processor family............................." + GetFamily(mo));

                  Console.WriteLine("L2 cache size................................" + mo["L2CacheSize"]);

                  Console.WriteLine("L2 cache speed..............................." + mo["L2CacheSpeed"]);

                  Console.WriteLine("Load percentage (average value for second)..." + mo["LoadPercentage"]);

                  Console.WriteLine("Manufacturer................................." + mo["Manufacturer"]);

                  Console.WriteLine("Maximum speed (in MHz)......................." + mo["MaxClockSpeed"]);

                  Console.WriteLine("Name........................................." + mo["Name"]);

                  Console.WriteLine("Support for power management................." + mo["PowerManagementSupported"]);

                  Console.WriteLine("Unique identificator describing processor...." + mo["ProcessorId"]);

                  Console.WriteLine("Processor type..............................." + GetProcessorType(mo));

                  Console.WriteLine("Role (CPU/math).............................." + mo["Role"]);

                  Console.WriteLine("Socket designation..........................." + mo["SocketDesignation"]);

                  Console.WriteLine("Status......................................." + mo["Status"]);

                  Console.WriteLine("Status information..........................." + GetStatusInfo(mo));

                  Console.WriteLine("Method how upgrade processor................." + GetUpgradeMethod(mo));

                  Console.WriteLine("Processor version............................" + mo["Version"]);

                  Console.WriteLine("Socket voltage..............................." + mo["VoltageCaps"]);

                  i++;

            }

      }

 

      private static string GetArchitecture(ManagementObject mo)

      {

            int i = Convert.ToInt32(mo["Architecture"]);

            switch (i)

            {

                  case 0:

                        return "x86";

                  case 1:

                        return "MIPS";

                  case 2:

                        return "Alpha";

                  case 3:

                        return "PowerPC";

                  case 4:

                        return "ia64";

            }

 

            return "Undefined";

      }

 

      private static string GetCpuStatus(ManagementObject mo)

      {

            int i = Convert.ToInt32(mo["CpuStatus"]);

            switch(i)

            {

                  case 0:

                        return "Unknown";

                  case 1:

                        return "CPU Enabled";

                  case 2:

                        return "CPU Disabled by User via BIOS Setup";

                  case 3:

                        return "CPU Disabled By BIOS (POST Error)";

                  case 4:

                        return "CPU is Idle";

                  case 5:

                        return "This value is reserved.";

                  case 6:

                        return "This value is reserved.";

                  case 7:

                        return "Other";

            }

            return "Undefined";

      }

 

      private static string GetFamily(ManagementObject mo)

      {

            int i = Convert.ToInt32(mo["Family"]);

            switch (i)

            {

                  case 1:

                        return "Other";

                  case 2:

                        return "Unknown";

                  case 3:

                        return "8086";

                  case 4:

                        return "80286";

                  case 5:

                        return "80386";

                  case 6:

                        return "80486";

                  case 7:

                        return "8087";

                  case 8:

                        return "80287";

                  case 9:

                        return "80387";

                  case 10:

                        return "80487";

                  case 11:

                        return "Pentium® brand";

                  case 12:

                        return "Pentium® Pro";

                  case 13:

                        return "Pentium® II";

                  case 14:

                        return "Pentium® processor with MMX technology";

                  case 15:

                        return "Celeron™";

                  case 16:

                        return "Pentium® II Xeon";

                  case 17:

                        return "Pentium® III";

                  case 18:

                        return "M1 Family";

                  case 19:

                        return "M2 Family";

                  case 24:

                        return "K5 Family";

                  case 25:

                        return "K6 Family";

                  case 26:

                        return "K6-2";

                  case 27:

                        return "K6-3";

                  case 28:

                        return "AMD Athlon™ Processor Family";

                  case 29:

                        return "AMD® Duron™ Processor";

                  case 30:

                        return "AMD2900 Family";

                  case 31:

                        return "K6-2+";

                  case 32:

                        return "Power PC Family";

                  case 33:

                        return "Power PC 601";

                  case 34:

                        return "Power PC 603";

                  case 35:

                        return "Power PC 603+";

                  case 36:

                        return "Power PC 604";

                  case 37:

                        return "Power PC 620";

                  case 38:

                        return "Power PC X704";

                  case 39:

                        return "Power PC 750";

                  case 48:

                        return "Alpha Family";

                  case 49:

                        return "Alpha 21064";

                  case 50:

                        return "Alpha 21066";

                  case 51:

                        return "Alpha 21164";

                  case 52:

                        return "Alpha 21164PC";

                  case 53:

                        return "Alpha 21164a";

                  case 54:

                        return "Alpha 21264";

                  case 55:

                        return "Alpha 21364";

                  case 64:

                        return "MIPS Family";

                  case 65:

                        return "MIPS R4000";

                  case 66:

                        return "MIPS R4200";

                  case 67:

                        return "MIPS R4400";

                  case 68:

                        return "MIPS R4600";

                  case 69:

                        return "MIPS R10000";

                  case 80:

                        return "SPARC Family";

                  case 81:

                        return "SuperSPARC";

                  case 82:

                        return "microSPARC II";

                  case 83:

                        return "microSPARC IIep";

                  case 84:

                        return "UltraSPARC";

                  case 85:

                        return "UltraSPARC II";

                  case 86:

                        return "UltraSPARC IIi";

                  case 87:

                        return "UltraSPARC III";

                  case 88:

                        return "UltraSPARC IIIi";

                  case 96:

                        return "68040";

                  case 97:

                        return "68xxx Family";

                  case 98:

                        return "68000";

                  case 99:

                        return "68010";

                  case 100:

                        return "68020";

                  case 101:

                        return "68030";

                  case 112:

                        return "Hobbit Family";

                  case 120:

                        return "Crusoe™ TM5000 Family";

                  case 121:

                        return "Crusoe™ TM3000 Family";

                  case 128:

                        return "Weitek";

                  case 130:

                        return "Itanium™ Processor";

                  case 144:

                        return "PA-RISC Family";

                  case 145:

                        return "PA-RISC 8500";

                  case 146:

                        return "PA-RISC 8000";

                  case 147:

                        return "PA-RISC 7300LC";

                  case 148:

                        return "PA-RISC 7200";

                  case 149:

                        return "PA-RISC 7100LC";

                  case 150:

                        return "PA-RISC 7100";

                  case 160:

                        return "V30 Family";

                  case 176:

                        return "Pentium® III Xeon™";

                  case 177:

                        return "Pentium® III Processor with Intel® SpeedStep™ Technology";

                  case 178:

                        return "Pentium® 4";

                  case 179:

                        return "Intel® Xeon™";

                  case 180:

                        return "AS400 Family";

                  case 181:

                        return "Intel® Xeon™ processor MP";

                  case 182:

                        return "AMD AthlonXP™ Family";

                  case 183:

                        return "AMD AthlonMP™ Family";

                  case 184:

                        return "Intel® Itanium® 2";

                  case 185:

                        return "AMD Opteron™ Family";

                  case 190:

                        return "K7";

                  case 200:

                        return "IBM390 Family";

                  case 201:

                        return "G4";

                  case 202:

                        return "G5";

                  case 250:

                        return "i860";

                  case 251:

                        return "i960";

                  case 260:

                        return "SH-3";

                  case 261:

                        return "SH-4";

                  case 280:

                        return "ARM";

                  case 281:

                        return "StrongARM";

                  case 300:

                        return "6x86";

                  case 301:

                        return "MediaGX";

                  case 302:

                        return "MII";

                  case 320:

                        return "WinChip";

                  case 350:

                        return "DSP";

                  case 500:

                        return "Video Processor";

            }

            return "Undefined processor family";

      }

 

      private static string GetProcessorType(ManagementObject mo)

      {

            int i = Convert.ToInt32(mo["ProcessorType"]);

            switch (i)

            {

                  case 1:

                        return "Other";

                  case 2:

                        return "Unknown";

                  case 3:

                        return "Central Processor";

                  case 4:

                        return "Math Processor";

                  case 5:

                        return "DSP Processor";

                  case 6:

                        return "Video Processor";

            }

            return "Undefined type";

      }

 

      private static string GetStatusInfo(ManagementObject mo)

      {

            int i = Convert.ToInt32(mo["StatusInfo"]);

            switch (i)

            {

                  case 1:

                        return "Other";

                  case 2:

                        return "Unknown";

                  case 3:

                        return "Enabled";

                  case 4:

                        return "Disabled";

                  case 5:

                        return "Not applicable";

            }

            return "StatusInfo not defined.";

      }

 

      private static string GetUpgradeMethod(ManagementObject mo)

      {

            int i = Convert.ToInt32(mo["UpgradeMethod"]);

            switch (i)

            {

                  case 1:

                        return "Other";

                  case 2:

                        return "Unknown";

                  case 3:

                        return "Daughter Board";

                  case 4:

                        return "ZIF Socket";

                  case 5:

                        return "Replacement/Piggy Back";

                  case 6:

                        return "None";

                  case 7:

                        return "LIF Socket";

                  case 8:

                        return "Slot 1";

                  case 9:

                        return "Slot 2";

                  case 10:

                        return "370 Pin Socket";

                  case 11:

                        return "Slot A";

                  case 12:

                        return "Slot M";

            }

            return "UpgradeMethod not defined.";

      }

 

      private static string GetSocketVoltage(ManagementObject mo)

      {

            int i = Convert.ToInt32(mo["VoltageCaps"]);

            switch (i)

            {

                  case 1:

                        return "5";

                  case 2:

                        return "3.3";

                  case 3:

                        return "2.9";

            }

            return "Socket voltage not defined";

      }

 

      private static string GetDeviceState(ManagementObject mo)

      {

            // here call method from sample 'Get CD-ROM/DVD information'

            return "";

      }

 

      private static string GetConfigError(ManagementObject mo)

      {

            // here call method from sample 'Get CD-ROM/DVD information'

            return "";

      }

}

13.5.11. Get memory info

Namespaces:

using System;

using System.Management;

 

Code:

static void Main(string[] args)

{

      WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_PerfFormattedData_PerfOS_Memory");

      ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

      foreach (ManagementObject mo in find.Get())

      {

            Console.WriteLine("Available bytes: " + mo["AvailableBytes"]);

            Console.WriteLine("Available KBs: " + mo["AvailableKBytes"]);

            Console.WriteLine("Available MBs: " + mo["AvailableMBytes"]);

            Console.WriteLine("Cache bytes: " + mo["CacheBytes"]);

            Console.WriteLine("Cache bytes peak: " + mo["CacheBytesPeak"]);

            Console.WriteLine("Cache bytes: " + mo["CacheBytes"]);

            Console.WriteLine("Commit limit: " + mo["CommitLimit"]);

            Console.WriteLine("Committed bytes: " + mo["CommittedBytes"]);

            Console.WriteLine("Free system page table entries: " + mo["FreeSystemPageTableEntries"]);

            Console.WriteLine("Pool paged bytes: " + mo["PoolPagedBytes"]);

            Console.WriteLine("System code total bytes: " + mo["SystemCodeTotalBytes"]);

            Console.WriteLine("System driver total bytes: " + mo["SystemDriverTotalBytes"]);

      }

}

13.5.12. Getting list of file shares on local machine

Namespaces:

using System;

using System.Management;

 

Code:

static void Main(string[] args)

{

      WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_Share");

      ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

      foreach (ManagementObject mo in find.Get())

      {

            Console.WriteLine("List of shares = " + mo["Name"]);

      }

}

13.5.13. Get logical disk info

Namespaces:

using System;

using System.Management;

 

Code I:

This sample uses CMI path instead of WQL, this is just demonstration on CMI addressing but WQL is more intuitive and will be used in majority of samples.

 

static void Main(string[] args)

{

      string cmiPath = @"\root\cimv2:Win32_LogicalDisk.DeviceID='C:'";

      // create managment object for required path in CMI

      ManagementObject mo = new ManagementObject(cmiPath);

 

      //output logical disk data

      Console.WriteLine("Description: " + mo["Description"]);

      Console.WriteLine("File system: " + mo["FileSystem"]);

      Console.WriteLine("Free disk space: " + mo["FreeSpace"]);

      Console.WriteLine("Size: " + mo["Size"]);

}

 

Code II:

static void Main(string[] args)

{

      WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_LogicalDisk WHERE DeviceID = 'C:'");

      ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

      foreach (ManagementObject mo in find.Get())

      {

            //output logical disk data

            Console.WriteLine("Description: " + mo["Description"]);

            Console.WriteLine("File system: " + mo["FileSystem"]);

            Console.WriteLine("Free disk space: " + mo["FreeSpace"]);

            Console.WriteLine("Size: " + mo["Size"]);

      }

}

13.5.14. Get environment variables

Namespaces:

using System;

using System.Management;

 

Code:

static void Main(string[] args)

{

      WqlObjectQuery query = new WqlObjectQuery("Select * from Win32_Environment");

      ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

      Console.WriteLine("Description - Name - User Name - Value");

      Console.WriteLine("----------------------------------------------------------");

      foreach (ManagementObject mo in find.Get())

      {

            Console.WriteLine(mo["Description"] + " - " + mo["Name"] + " - " + mo["UserName"] + " - " + mo["VariableValue"]);

      }

}

13.5.15. Get CD-ROM/DVD information

Namespaces:

using System;

using System.Management;

 

Code:

class CDROMInfo

{

      static void Main(string[] args)

      {

            WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_CDROMDrive");

            ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

            foreach (ManagementObject mo in find.Get())

            {

                  Console.WriteLine("The availability and status of the device............." + GetDeviceState(mo));

                  Console.WriteLine("Device configuration state............................" + GetConfigError(mo));

                  Console.WriteLine("Description..........................................." + mo["Description"]);

                  Console.WriteLine("Drive................................................." + mo["Drive"]);

                  Console.WriteLine("Maximum length of a filename.........................." + mo["MaximumComponentLength"]);

                  Console.WriteLine("Type of media........................................." + mo["MediaType"]);

                  Console.WriteLine("Size of the disk......................................" + mo["Size"]);

                  Console.WriteLine("Status of the disk...................................." + mo["Size"]);

                  Console.WriteLine("Current transfer rate................................." + mo["TransferRate"]);

            }

      }

 

      // this method returns state of device regarding its power state

      private static string GetDeviceState(ManagementObject mo)

      {

            int i = Convert.ToInt16(mo["Availability"]);

            switch (i)

            {

                  case 3:

                        return "Full Power";

                  case 4:

                        return "Warning";

                  case 5:

                        return "Test";

                  case 10:

                        return "Degraded";

                  case 13:

                        return "Power Save - Unknown";

                  case 14:

                        return "Power Save - Low Power Mode";

                  case 15:

                        return "Power Save - Standby";

                  case 17:

                        return "Power Save - Warning";

            }

            return "Unknown state";

      }

      // this method returns configuration state of device

      private static string GetConfigError(ManagementObject mo)

      {

            int i = Convert.ToInt16(mo["ConfigManagerErrorCode"]);

            switch (i)

            {

                  case 0:

                        return "This device is working properly.";

                  case 1:

                        return "This device is not configured correctly.";

                  case 2:

                        return "Windows cannot load the driver for this device.";

                  case 3:

                        return "The driver for this device might be corrupted, or your system may be running low on memory or other resources.";

                  case 4:

                        return "This device is not working properly. One of its drivers or your registry might be corrupted.";

                  case 5:

                        return "The driver for this device needs a resource that Windows cannot manage.";

                  case 6:

                        return "The boot configuration for this device conflicts with other devices.";

                  case 7:

                        return "Cannot filter.";

                  case 8:

                        return "The driver loader for the device is missing.";

                  case 9:

                        return "This device is not working properly because the controlling firmware is reporting the resources for the device incorrectly.";

                  case 10:

                        return "This device cannot start.";

                  case 11:

                        return "This device failed.";

                  case 12:

                        return "This device cannot find enough free resources that it can use.";

                  case 13:

                        return "Windows cannot verify this device's resources.";

                  case 14:

                        return "This device cannot work properly until you restart your computer.";

                  case 15:

                        return "This device is not working properly because there is probably a re-enumeration problem.";

                  case 16:

                        return "Windows cannot identify all the resources this device uses.";

                  case 17:

                        return "This device is asking for an unknown resource type.";

                  case 18:

                        return "Reinstall the drivers for this device.";

                  case 19:

                        return "Your registry might be corrupted.";

                  case 20:

                        return "Failure using the VxD loader.";

                  case 21:

                        return "System failure: Try changing the driver for this device. If that does not work, see your hardware documentation. Windows is removing this device.";

                  case 22:

                        return "This device is disabled.";

                  case 23:

                        return "System failure: Try changing the driver for this device. If that doesn't work, see your hardware documentation.";

                  case 24:

                        return "This device is not present, is not working properly, or does not have all its drivers installed.";

                  case 25:

                        return "Windows is still setting up this device.";

                  case 26:

                        return "Windows is still setting up this device.";

                  case 27:

                        return "This device does not have valid log configuration.";

                  case 28:

                        return "The drivers for this device are not installed.";

                  case 29:

                        return "This device is disabled because the firmware of the device did not give it the required resources.";

                  case 30:

                        return "This device is using an Interrupt Request (IRQ) resource that another device is using.";

                  case 31:

                        return "This device is not working properly because Windows cannot load the drivers required for this device.";

            }

            return "Unknown state.";

      }

}

13.5.16. Get boot configuration

Namespaces:

using System;

using System.Management;

 

Code:

static void Main(string[] args)

{

      WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_BootConfiguration");

      ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

      foreach (ManagementObject mo in find.Get())

      {

            Console.WriteLine("Boot directory with files required for booting........" + mo["BootDirectory"]);

      Console.WriteLine("Description..........................................." + mo["Description"]);

            Console.WriteLine("Directory with temporary files for booting............" + mo["ScratchDirectory"]);

            Console.WriteLine("Directory with temporary files........................" + mo["TempDirectory"]);

      }

}

13.5.17.  Find a service by its name

Namespaces:

using System;

using System.Management;

 

Code:

static void Main(string[] args)

{

     string serviceName = "EventLog";

     WqlObjectQuery query = new WqlObjectQuery("select * from Win32_Service where name = '" + serviceName + "'");

     ManagementObjectSearcher find = new ManagementObjectSearcher(query);

 

     Console.WriteLine("--------------------------------------------------");

     foreach (ManagementObject mo in find.Get())

     {

           // output service

           Console.WriteLine("Service name: " + mo["Name"] + " --- Caption: " + mo["Caption"] + "---  Description: " + mo["Description"]);

           Console.WriteLine("--------------------------------------------------");

     }

}

13.5.18. Get list of running/stopped services

Namespaces:

using System;

using System.Management;

 

Code:

static void Main(string[] args)

{

      // change the WSL to 'stopped' to get non-running services

      WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_Service WHERE state='running'");

      ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

 

      Console.WriteLine("--------------------------------------------------");

      foreach (ManagementObject mo in find.Get())

      {

            // output running services

            Console.WriteLine("Service name: " + mo["DisplayName"] + " --- Start mode: " + mo["StartMode"] + "---  Description: " + mo["Description"]);

            Console.WriteLine("--------------------------------------------------");

      }

}

13.5.19. Getting partition info

Namespaces:

using System;

using System.Management;

 

Code:

class WMIPartitionSample

{

 

      static void Main(string[] args)

      {

            WqlObjectQuery query = new WqlObjectQuery("Select * from Win32_DiskPartition");

            ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

            foreach (ManagementObject mo in find.Get())

            {

                  Console.WriteLine("Block size............................................" + mo["BlockSize"] + " Bytes");

                  Console.WriteLine("Partition is labeled as bootable......................" + mo["Bootable"]);

                  Console.WriteLine("Boot partition active................................." + mo["BootPartition"]);

                  Console.WriteLine("Caption..............................................." + mo["Caption"]);

                  Console.WriteLine("Device configuration state............................" + GetConfigError(mo));

                  Console.WriteLine("Description..........................................." + mo["Description"]);

                  Console.WriteLine("Unique identification of partition...................." + mo["DeviceID"]);

                  Console.WriteLine("Index number of the disk with that partition.........." + mo["DiskIndex"]);

                  Console.WriteLine("Detailed description of error in LastErrorCode........" + mo["ErrorDescription"]);

                  Console.WriteLine("Type of error detection and correction................" + mo["ErrorMethodology"]);

                  Console.WriteLine("Hidden sectors in partition..........................." + mo["HiddenSectors"]);

                  Console.WriteLine("Index number of the partition........................." + mo["Index"]);

                  Console.WriteLine("Last error by device.................................." + mo["LastErrorCode"]);

                  Console.WriteLine("Total number of consecutive blocks...................." + mo["NumberOfBlocks"]);

                  Console.WriteLine("Partition labeled as primary.........................." + mo["PrimaryPartition"]);

                  Console.WriteLine("Free description of media purpose....................." + mo["Purpose"]);

                  Console.WriteLine("Total size of partition..............................." + mo["Size"] + " bytes");

                  Console.WriteLine("Starting offset of the partition......................" + mo["StartingOffset"]);

                  Console.WriteLine("Status................................................" + mo["Status"]);

                  Console.WriteLine("Type of the partition................................." + mo["Type"]);

            }

      }

      // this method returns configuration state of device

      private static string GetConfigError(ManagementObject mo)

      {

            // code here is removed, see sample and copy the same method from “Get CD-ROM/DVD information

      }

}

13.5.20. Get list of user’s account from local machine/domain

Namespaces:

using System;

using System.Management;

 

Code:

class WMIAccountSample

{

      class WMIGroupsSample

      {

 

            static void Main(string[] args)

            {

                  // select all local groups, not groups from domain

// change this query to change WHERE statement and remove LocalAccount and use Domain = 'domain_name'

                  WqlObjectQuery query = new WqlObjectQuery("SELECT * FROM Win32_UserAccount WHERE LocalAccount = 'true'");

                  ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

                  Console.WriteLine("---------------------------------------------------------------------------");

                  foreach (ManagementObject mo in find.Get())

                  {

                        Console.WriteLine("Caption........................................" + mo["Caption"]);

                        Console.WriteLine("Description...................................." + mo["Description"]);

                        Console.WriteLine("Domain where account belongs..................." + mo["Domain"]);

                        Console.WriteLine("Account is defined on local machine............" + mo["LocalAccount"]);

                        Console.WriteLine("Name of the account............................" + mo["Name"]);

                        Console.WriteLine("Password can be changed........................" + mo["PasswordChangeable"]);

                        Console.WriteLine("Password expires..............................." + mo["PasswordExpires"]);

                        Console.WriteLine("Password is required for this account.........." + mo["PasswordRequired"]);

                        Console.WriteLine("Security identifier (SID)......................" + mo["SID"]);

                        Console.WriteLine("Type of security identifier...................." + GetSidType(Convert.ToInt32(mo["SIDType"])));

                        Console.WriteLine("Status........................................." + mo["Status"]);

                        Console.WriteLine("---------------------------------------------------------------------------");

                  }

            }

 

            public static string GetSidType(int type)

            {

                  switch (type)

                  {

                        case 1:

                              return "SidTypeUser";

                        case 2:

                              return "SidTypeGroup";

                        case 3:

                              return "SidTypeDomain";

                        case 4:

                              return "SidTypeAlias";

                        case 5:

                              return "SidTypeWellKnownGroup";

                        case 6:

                              return "SidTypeDeletedAccount";

                        case 7:

                              return "SidTypeInvalid";

                        case 8:

                              return "SidTypeUnknown";

                        case 9:

                              return "SidTypeComputer";

                  }

                  return "";

            }

      }

}

13.5.21. Get list of user groups from local machine/domain

Namespaces:

using System;

using System.Management;

 

Code:

class WMIGroupsSample

{

 

      static void Main(string[] args)

      {

// select all local groups, not groups from domain

// change this query to change WHERE statement and remove LocalAccount and use Domain = 'domain_name'

WqlObjectQuery query = new WqlObjectQuery("Select * from Win32_Group where LocalAccount = 'true'");

            ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

            Console.WriteLine("---------------------------------------------------------------------------");

            foreach (ManagementObject mo in find.Get())

            {

                  Console.WriteLine("Caption........................................" + mo["Caption"]);

                  Console.WriteLine("Description...................................." + mo["Description"]);

                  Console.WriteLine("Domain where group belongs....................." + mo["Domain"]);

                  Console.WriteLine("Account is defined on local machine............" + mo["LocalAccount"]);

                  Console.WriteLine("Name of the group.............................." + mo["Name"]);

                  Console.WriteLine("Security identifier (SID)......................" + mo["SID"]);

                  Console.WriteLine("Type of security identifier...................." + GetSidType(Convert.ToInt32(mo["SIDType"])));

                  Console.WriteLine("Status........................................." + mo["Status"]);

                  Console.WriteLine("---------------------------------------------------------------------------");

            }

      }

 

      public static string GetSidType(int type)

      {

            // code here is the same as in previous sample

      }

}

13.5.22. Get list of installed codec files

Namespaces:

using System;

using System.Management;

 

Code:

static void Main(string[] args)

{

      WqlObjectQuery query = new WqlObjectQuery("Select * from Win32_CodecFile");

      ManagementObjectSearcher find =     new ManagementObjectSearcher(query);

      Console.WriteLine("---------------------------------------------------------------------------");

      foreach (ManagementObject mo in find.Get())

      {

            Console.WriteLine("File should be archived..............................." + mo["Archive"]);

            Console.WriteLine("Caption (name of codec)..............................." + mo["Caption"]);

            Console.WriteLine("Compressed............................................" + mo["Compressed"]);

            Console.WriteLine("Compression method...................................." + mo["CompressionMethod"]);

            Console.WriteLine("DOS compatible file name.............................." + mo["EightDotThreeFileName"]);

            Console.WriteLine("Encrypted............................................." + mo["Encrypted"]);

            Console.WriteLine("Encryption method....................................." + mo["EncryptionMethod"]);

            Console.WriteLine("Extension............................................." + mo["Extension"]);

            Console.WriteLine("File name............................................." + mo["FileName"]);

            Console.WriteLine("File size............................................." + mo["FileSize"]);

            Console.WriteLine("File type............................................." + mo["FileType"]);

            Console.WriteLine("Group (type of codec)................................." + mo["Group"]);

            Console.WriteLine("File is hidden........................................" + mo["Hidden"]);

            Console.WriteLine("Installation date....................................." + ManagementDateTimeConverter.ToDateTime(mo["InstallDate"].ToString()));

            Console.WriteLine("Currently open instances of this file................." + mo["InUseCount"]);

            Console.WriteLine("Time when file was last accessed......................" + ManagementDateTimeConverter.ToDateTime(mo["LastAccessed"].ToString()));

            Console.WriteLine("Time when file was last modified......................" + ManagementDateTimeConverter.ToDateTime(mo["LastModified"].ToString()));

            Console.WriteLine("Manufacturer.........................................." + mo["Manufacturer"]);

            Console.WriteLine("Path.................................................." + mo["Path"]);

            Console.WriteLine("File can be read......................................" + mo["Readable"]);

            Console.WriteLine("Status................................................" + mo["Status"]);

            Console.WriteLine("Version..............................................." + mo["Version"]);

            Console.WriteLine("File can be written..................................." + mo["Writeable"]);

            Console.WriteLine("---------------------------------------------------------------------------");

      }

}

13.6.  Watching for event

13.6.1. Watching for newly started processes

Namespaces:

using System;

using System.Management;

 

Code:

class WMIWatcher

{

     static void Main()

     {

           // watch for newly started processes

           ManagementEventWatcher processStart = new ManagementEventWatcher(

                new EventQuery("select * from Win32_ProcessStartTrace"));

           processStart.EventArrived += new EventArrivedEventHandler(ProcessStartHandler);

       

           processStart.Start();

 

           Console.Read();   

           processStart.Dispose();

     }

 

     static void ProcessStartHandler(object sender, EventArrivedEventArgs e)

     {

           PropertyDataCollection props = e.NewEvent.Properties;

           Console.WriteLine("Process ID: {0};\tProcess Name: {1}",

                props["ProcessID"].Value, props["ProcessName"].Value);

     }

}


14.         XML

14.1. What is SGML?

Standard Generalized Markup Language, SGML, is an international standard (ISO 8879) first published in 1986.  SGML defines a format for embedding descriptive markup within a document.  SGML also specifies a standard method for describing the structure of a document.

SGML requires a hierarchical structure for documents.  Each element in the document has a user-defined label to provide descriptive markup to define the logical structure.  

SGML supports a wide variety of document structures.  Developers may design a different document structure for each category of information being handled:  user manuals, technical manuals, parts catalogs, job descriptions, design specifications, technical reports, etc.

Documents are independent of any vendors' hardware or software and can be exchanged among differing systems.  

14.2. What is XML?

Extensible Markup Language, XML 1.0, was created as a subset of SGML (Standard Generalized Markup Language, ISO 8879:1986), is intended for use on the World Wide Web.  XML keeps SGML's basic features of being readable, extensible, vendor independent, and capable of validation.  In addition, XML is designed to be easier to implement and understand than SGML.

XML specification defines the syntax of XML, the XML version of an SGML DTD, and the concept of a well-formed document.

XML is a markup language for documents containing structured information. Structured information contains both content (words, pictures, etc.) and an indication of how the content relates to other information in the document.

 

Here is provided the short list of basic features of XML:

 

14.3. What is XHTML?

XHTML 1.0 is a reformulation of HTML 4 as an XML 1.0 application, and three DTDs (Strict, Transitional, and Frameset) which correspond to the ones defined by HTML 4.  Element and associated attribute semantics are defined in the W3C Recommendation for HTML 4.  The XHTML recommendation provides the basis for future extensibility of markup. 

XHTML 1.1 introduces a modular approach to the markup language and drops the Transitional and Frameset DTDs.

XHTML 2.0 breaks from the traditional markup language development as it is not planned to be backward compatible with HTML4.x or XHTML 1.x.; although, many of its characteristics will be similar.

14.4. Forward-only reading and writing XML

.NET environment provides many classes to work with XML documents and to parse them. The basic one is a XmlReader/XmlWriter classes providing many functionality to derived ones.

You see the possible usage on the following figure.

14.5. XmlTextReader

The XmlTextReader is the fastest version of XmlReader class and this class is designed to enable manipulation with strings (from an URL, a stream or from a memory).

14.5.1. XML file “Sample.xml” used in following samples

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE samples [

 

      <!ELEMENT samples ((writer)*, (client)*)>

      <!ELEMENT writer (name, address+, email+, book+)>

            <!ELEMENT name (#PCDATA)>

            <!ELEMENT address (#PCDATA)>

            <!ELEMENT email (#PCDATA)>

            <!ELEMENT book (#PCDATA)>

                  <!ATTLIST book price CDATA #REQUIRED>

            <!ELEMENT client (#PCDATA)>

            <!ATTLIST client name CDATA #REQUIRED

                  address CDATA #IMPLIED

                  email CDATA #IMPLIED>

 

]>

 

<samples>

      <writer>

            <name>Jan Seda</name>

            <address>Prague, Czech republic</address>

            <email>jan.seda@skilldrive.com</email>

            <book price="49.9">.NET in Samples</book>

      </writer>

 

      <writer>

            <name>Jane Gurnet</name>

            <address>New Your, USA</address>

            <email>jane@hotmail.com</email>

            <book price="29">Technical Support in Microsoft</book>

      </writer>

 

      <writer>

            <name>John Grey</name>

            <address />

            <email></email>

            <book price="39.9">Programmer</book>

      </writer>

 

      <client name="Jan Seda" address="Prague, Czech republic" email="jan.seda@skilldrive.com" />

      <client name="John Grey" address="" email="" />

</samples>

14.5.2. What is a XML schema?

A schema is a well-formed XML document whose function is similar to that of a DTD.  It defines a set of elements and attributes that may be included in an XML document and the related rules for their use.

An extremely important feature of schemas is the datatyping capability they provide.  You are not restricted to just defining character data, as with a DTD.  The data can now be identified as string, integer, Boolean, decimal, and other forms.  Additionally, you can use the base datatypes to define derived datatypes.

14.5.3. XSD file “Sample.xsd” used in following samples

Schema file can be easily created using Visual Studio .NET environemt and provided XML file. Programmers using .NET SDK can use following schema file:

 

<?xml version="1.0"?>

<xs:schema id="samples" targetNamespace="http://tempuri.org/Sample.xsd" xmlns:mstns="http://tempuri.org/Sample.xsd" xmlns="http://tempuri.org/XMLFile23.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified">

  <xs:element name="samples" msdata:IsDataSet="true" msdata:Locale="cs-CZ" msdata:EnforceConstraints="False">

    <xs:complexType>

      <xs:choice maxOccurs="unbounded">

        <xs:element name="writer">

          <xs:complexType>

            <xs:sequence>

              <xs:element name="name" type="xs:string" minOccurs="0" />

              <xs:element name="address" type="xs:string" minOccurs="0" />

              <xs:element name="email" type="xs:string" minOccurs="0" />

              <xs:element name="book" nillable="true" minOccurs="0" maxOccurs="unbounded">

                <xs:complexType>

                  <xs:simpleContent msdata:ColumnName="book_Text" msdata:Ordinal="1">

                    <xs:extension base="xs:string">

                      <xs:attribute name="price" form="unqualified" type="xs:string" />

                    </xs:extension>

                  </xs:simpleContent>

                </xs:complexType>

              </xs:element>

            </xs:sequence>

          </xs:complexType>

        </xs:element>

        <xs:element name="client">

          <xs:complexType>

            <xs:attribute name="name" form="unqualified" type="xs:string" />

            <xs:attribute name="address" form="unqualified" type="xs:string" />

            <xs:attribute name="email" form="unqualified" type="xs:string" />

          </xs:complexType>

        </xs:element>

      </xs:choice>

    </xs:complexType>

  </xs:element>

</xs:schema>

14.5.4. Load and read XML from URL

Namespaces:

using System;

using System.Xml;

 

Code:

static void Main(string[] args)

{

      XmlTextReader xmlreader = new XmlTextReader("http://localhost/sample.xml");

      while (xmlreader.Read())

      {

Console.WriteLine("{0,-10}{1,-10}{2,-10}", xmlreader.NodeType.ToString(),

            xmlreader.Name,xmlreader.Value);

      }

      xmlreader.Close();

}

14.5.5. Load and read XML from file

Namespaces:

using System;

using System.IO;

using System.Xml;

 

Code:

static void Main(string[] args)

{

      FileStream fs = new FileStream("Sample.xml", FileMode.Open);

      XmlTextReader xmlreader = new XmlTextReader(fs);

      while (xmlreader.Read())

      {

            Console.WriteLine("{0,-10}{1,-10}{2,-10}", xmlreader.NodeType.ToString(),

                  xmlreader.Name,xmlreader.Value);

      }

      xmlreader.Close();

      fs.Close();

}

14.5.6. Load and read XML from memory-stored data

Namespaces:

using System;

using System.Xml;

using System.IO;

using System.Text;

 

Code:

static void Main(string[] args)

      {

            string xmlData = "<?xml version='1.0' encoding='utf-8' ?><a><b>Some data</b></a>";

            UTF8Encoding utf8 = new UTF8Encoding();

            byte[] byteData = utf8.GetBytes(xmlData);

            MemoryStream ms = new MemoryStream(byteData);

            XmlTextReader xmlreader = new XmlTextReader(ms);

            while (xmlreader.Read())

            {

                  Console.WriteLine("{0,-20}{1,-10}{2,-10}", xmlreader.NodeType.ToString(),

                              xmlreader.Name,xmlreader.Value);

            }

            xmlreader.Close();

            ms.Close();

}

14.5.7. Handle whitespaces in XML

Whitespaces in XML: carriage return, line feed characters, space, tab.

 

Namespaces:

using System;

using System.Xml;

using System.IO;

 

Code:

static void Main(string[] args)

{

      FileStream fs = new FileStream("Sample.xml", FileMode.Open);

      XmlTextReader xmlreader = new XmlTextReader(fs);

 

      // see the difference when whitespaces are handled

      // xmlreader.WhitespaceHandling = WhitespaceHandling.All;

      xmlreader.WhitespaceHandling = WhitespaceHandling.None;

      while (xmlreader.Read())

      {

            Console.WriteLine("{0,-10}{1,-10}{2,-10}", xmlreader.NodeType.ToString(),

                  xmlreader.Name,xmlreader.Value);

      }

 

      xmlreader.Close();

      fs.Close();

}

14.5.8. Read specific attribute in XML

Namespaces:

using System;

using System.Xml;

using System.IO;

 

Code:

static void Main(string[] args)

{

      FileStream fs = new FileStream("Sample.xml", FileMode.Open);

      XmlTextReader xmlreader = new XmlTextReader(fs);

      while (xmlreader.Read())

      {

            // get name attributes for client elements in xml file

            if (xmlreader.Name.Equals("client"))

                  Console.WriteLine(xmlreader.GetAttribute("name"));

      }

      xmlreader.Close();

      fs.Close();

}

14.5.9. Step over attributes in XML

Namespaces:

using System;

using System.Xml;

using System.IO;

 

Code:

static void Main(string[] args)

{

      FileStream fs = new FileStream("Sample.xml", FileMode.Open);

      XmlTextReader xmlreader = new XmlTextReader(fs);

      while (xmlreader.Read())

      {

            // find elements with attributes and step over them

            if (xmlreader.NodeType==XmlNodeType.Element && xmlreader.HasAttributes)

            {

                  while(xmlreader.MoveToNextAttribute())

                  {

                        Console.WriteLine(xmlreader.Name+": "+xmlreader.Value);

                  }

            }          

      }

      xmlreader.Close();

      fs.Close();

}

14.5.10. Write string data to XML file

Namespaces:

using System;

using System.Xml;

using System.IO;

 

Code:

static void Main(string[] args)

{

      XmlTextWriter xmlwriter = new XmlTextWriter("Sample2.xml", null);

      // output starting element

      xmlwriter.WriteStartElement("start");

      // output child element to <start>

      xmlwriter.WriteStartElement("some_element");

      // output string data

      xmlwriter.WriteString("here goes some string data");

      xmlwriter.WriteEndElement();

      xmlwriter.WriteEndElement();

      xmlwriter.Close();

}

14.5.11. Write characters to XML file

Namespaces:

using System;

using System.Xml;

using System.IO;

 

Code:

static void Main(string[] args)

{

      char[] chars = new char[3] {'A', 'B', 'C'};

      XmlTextWriter xmlwriter = new XmlTextWriter("Sample2.xml", null);

      // starting element

      xmlwriter.WriteStartElement("start");

      // child element

      xmlwriter.WriteStartElement("some_element");

      // output characters

      xmlwriter.WriteChars(chars, 0, 3);

      xmlwriter.WriteEndElement();

      xmlwriter.WriteEndElement();

      xmlwriter.Close();

}

14.5.12. Write comments to XML file

Namespaces:

using System;

using System.Xml;

using System.IO;

 

Code:

static void Main(string[] args)

{

      XmlTextWriter xmlwriter = new XmlTextWriter("Sample2.xml", null);

      xmlwriter.WriteStartElement("start");

      xmlwriter.WriteComment("Here goes some comments in xml file");

      xmlwriter.WriteEndElement();

      xmlwriter.Close();

}

14.5.13. Write processing instructions to XML file

Processing instructions are used when XML document is processed by some application and they can provide additional information, simply application specific.

Procession instructions have following syntax:

 

<?instruction_name instruction_data?>

 

Namespaces do not apply to processing instruction and that is why collitions can happen.

Namespaces:

using System;

using System.Xml;

using System.IO;

 

Code:

static void Main(string[] args)

{

      XmlTextWriter xmlwriter = new XmlTextWriter("Sample2.xml", null);

      xmlwriter.WriteStartElement("start");

      xmlwriter.WriteProcessingInstruction("instruction_name", "instruction_data 1 2 3");

      xmlwriter.WriteEndElement();

      xmlwriter.Close();

}

14.5.14. Write attributes to XML file

Namespaces:

using System;

using System.Xml;

using System.IO;

 

Code:

static void Main(string[] args)

{

      XmlTextWriter xmlwriter = new XmlTextWriter("Sample2.xml", null);

      // starting element

      xmlwriter.WriteStartElement("start");

      // first attrbite to <start> element

      xmlwriter.WriteStartAttribute("first_att", null);

      // outputing attribute value

      xmlwriter.WriteString("attribute value");

      xmlwriter.WriteEndAttribute();

      xmlwriter.WriteAttributeString("second_att", "second_value");

      xmlwriter.WriteEndElement();

      xmlwriter.Close();

}

14.5.15. What is it a XML namespace?

The purpose of XML namespaces is to distinguish between duplicate element and attribute names from multiple DTDs and/or Schemas used in a given XML document instance.

XML namespace represents a collection of names.  The namespace is identified by a unique location - a URI.  Any associated name in an XML namespace can be uniquely identified by a two-part name: the namespace prefix and the local part, the element, attribute, data type, etc. joined by a colon.  This two-part naming system is the purpose of XML namespaces.

XML namespaces are declared with an xmlns attribute, which can associate a prefix with the namespace.  Once declared, an element is referenced by its namespace prefix and the namespace element or attribute name.

14.5.16. Write namespace to XML file

Namespaces:

using System;

using System.Xml;

using System.IO;

 

Code:

static void Main(string[] args)

{

      XmlTextWriter xmlwriter = new XmlTextWriter("Sample2.xml", null);

      // starting element

      xmlwriter.WriteStartElement("start");

      // writing namespace to element

      xmlwriter.WriteStartElement("some_element", "http://localhost/some_element");

      xmlwriter.WriteEndElement();

      xmlwriter.WriteEndElement();

      xmlwriter.Close();

}

14.5.17. Write namespace with prefix to XML file

Namespaces:

using System;

using System.Xml;

using System.IO;

 

Code:

static void Main(string[] args)

{

      XmlTextWriter xmlwriter = new XmlTextWriter("Sample2.xml", null);

      // starting element

      xmlwriter.WriteStartElement("start");

      // prefixing elements

      xmlwriter.WriteStartElement("prefix", "some_element", "http://localhost/some_element");

      xmlwriter.WriteElementString("element_Name", "http://localhost/some_element", "Here goes element value");

      xmlwriter.WriteEndElement();

      xmlwriter.WriteEndElement();

      xmlwriter.Close();

}

14.5.18. Set format options when writing to XML file

Namespaces:

using System;

using System.Xml;

using System.IO;

 

Code:

static void Main(string[] args)

{

      XmlTextWriter xmlwriter = new XmlTextWriter("Sample2.xml", null);

      // format xml document

      xmlwriter.Formatting = Formatting.Indented;

      // use tab as indenting character

      xmlwriter.IndentChar = '\t';

      // just one tab character

      xmlwriter.Indentation = 1;

      // starting element

      xmlwriter.WriteStartElement("start");

      xmlwriter.WriteStartElement("some_element");

      xmlwriter.WriteString("here goes some string data");

      xmlwriter.WriteEndElement();

      xmlwriter.WriteEndElement();

      xmlwriter.Close();

}

14.5.19. Set a single quote as formatting option for XML file

xmlwriter.QuoteChar = '\'';

14.6. Document Object Model (DOM)

14.6.1. What is a XML document?

XML document must be well-formed as defined by the Worldwide Web Consortium (W3C) XML recommendation.  All document components must be correctly formed and properly nested.  If an XML document has an associated Document Type Definition (DTD), the document structure can be validated.

 

 

Markup of an XML document consists of declarations, elements, attributes, entity references, character references, comments, and processing instructions.  In a well-formed document, few constraints exist on how markup is named, how elements and attributes are employed, or how elements and attributes are related.  In a valid document, markup is constrained by the Document Type Definition (DTD).

XML document is content oriented and intent of markup is to label content as data; creating text-based, hierarchical database. XML markup has no inherent display characteristics (it’s just content oriented as mentioned above).  To display XML document on a web page, additional technologies must be used such as Cascading Style Sheets (CSS) or Extensible Style Language Transformations (XSLT).

14.6.2. Open XML document from URL

Namespaces:

using System;

using System.Xml;

 

Code:

static void Main(string[] args)

{

      XmlDocument xmldoc = new XmlDocument();

      xmldoc.Load("https://localhost/sample.xml");

}

14.6.3. Open XML document from file

Namespaces:

using System;

using System.IO;

using System.Xml;

 

Code:

static void Main(string[] args)

{

      XmlDocument xmldoc = new XmlDocument();

      // opening a file stream with xml source

      FileStream fs = new FileStream("Sample.xml", FileMode.Open);

      xmldoc.Load(fs);

      // output content of DOM to console

      Console.Write(xmldoc.InnerXml);

      fs.Close();

}

14.6.4. Open XML document with memory-stored data

Namespaces:

using System;

using System.Xml;

 

Code:

static void Main(string[] args)

{

      string xmlData = "<?xml version='1.0' encoding='utf-8' ?><a><b>Some data</b></a>";

      XmlDocument xmldoc = new XmlDocument();

      xmldoc.LoadXml(xmlData);

      // output content of DOM to console

      Console.Write(xmldoc.InnerXml);

}

14.6.5. Insert nodes into XML document

Namespaces:

using System;

using System.IO;

using System.Xml;

 

Code:

public static void Main()

{

     XmlDocument xmldoc = new XmlDocument();

     // opening a file stream with xml source

     FileStream fs = new FileStream("Sample.xml", FileMode.Open);

     xmldoc.Load(fs);

 

     // add new writer element

     XmlNode newWriter = xmldoc.CreateElement("writer");

     //insert child nodes for a writer

           XmlNode child = xmldoc.CreateElement("name");

           child.InnerText = "John Fox";

           newWriter.AppendChild(child);

           child = xmldoc.CreateElement("address");

           child.InnerText = "London, UK";

           newWriter.AppendChild(child);

           child = xmldoc.CreateElement("email");

           child.InnerText = "fox@hotmail.com";

           newWriter.AppendChild(child);

           child = xmldoc.CreateElement("book");

           // add attribute for book node

           XmlAttribute atrbPrice = xmldoc.CreateAttribute("price");

           atrbPrice.Value = "40.99";

           child.Attributes.Append(atrbPrice);

           child.InnerText = "Programming Visual Studio .NET 2003";

           newWriter.AppendChild(child);

 

     // add writer element to the xml document

     xmldoc.DocumentElement.AppendChild(newWriter);

 

     // move to first possition of in file stream

     fs.Position = 0;

     // save xml document

     xmldoc.Save(fs);

 

     fs.Close();

}

14.6.6. Finding nodes by their names

Namespaces:

using System;

using System.Xml;

 

Code:

public static void Main()

{

     XmlDocument xmldoc = new XmlDocument();

     xmldoc.Load("Sample.xml");

 

     // find writer nodes and retrieve them

     XmlNodeList writers = xmldoc.GetElementsByTagName("writer");

     foreach (XmlNode writer in writers)

     {

           // output name to console, use XPath to move to relative child nodes

          Console.WriteLine(writer.SelectSingleNode("child::name").InnerText);

     }

}

14.6.7. XPath classes in .NET 1.1

14.6.8. Quering XML using XPath

Namespaces:

using System;

using System.Xml.XPath;

 

Code:

static void Main(string[] args)

{

      // this is a special document proving fast accest to xml document

      XPathDocument xDoc = new XPathDocument("Sample.xml");

      XPathNavigator nav = xDoc.CreateNavigator();

 

      // find and iterate through the names in xml document

      XPathNodeIterator iter = nav.Select("/samples/writer/name");

      while (iter.MoveNext())

      {

            Console.WriteLine("This is value of node 'name': " + iter.Current.Value);

      }

}

14.6.9. Sum attribute values using XPath expression

Namespaces:

using System;

using System.Xml.XPath;

 

Code:

static void Main(string[] args)

{

      XPathDocument xDoc = new XPathDocument("sample.xml");

      XPathNavigator nav = xDoc.CreateNavigator();

 

      XPathExpression expr = nav.Compile("sum(//book/@price)");

      Console.WriteLine("Sum price of the books: " + nav.Evaluate(expr));

}

14.6.10. List of XPath axes

Axes can be used in XPath statements to refere to XML elements. Here is the list of XPath axes.

 

Keyword

Description

ancestor

Contains ancestors of context node. It referes to parent of context node and other parents and so on.

ancestor-or-self

Contains context node and other ancestors of this node.

attribute

Referes to attributes of context node.

child

Referes to child nodes of context node.

descendant

Referes to descendants of context node, It refereres to descendant of context node and then to descendard of descendant and so on.

descendant-of-self

Referes to desncendants of context node.

following

Contains all nodes in the same document that are after context node in document, excluding descendants, namespace nodes etc.

following-sibling

Contains all following siblings of context node.

namespace

Referes to namespace nodes of context node.

Parent

Referes to parent of context node.

preceding

Referes to all nodes that are before context node in document, excluding descendants, namespace nodes etc.

preceding-sibling

Contains all preceding siblings of context node.

Self

Referes just to context node.

 

14.6.11. What is DTD?

Before creating a valid XML document, the elements, attributes, notations, entities, etc., and their relationships must be defined.  A DTD can be referenced by or incorporated in the XML document. 

A DTD is not an XML document.  The DTD syntax  is different than XML.  The intent of DTDs is to ensure consistency in the creation of XML documents instances based on the given DTD.

14.6.12. Validate XML against XSD (Schema)

Namespaces:

using System;

using System.Xml;

using System.Xml.Schema;

 

Code:

class SchemaValidationClass

{

      static void Main(string[] args)

      {

 

            XmlSchema schema = XmlSchema.Read(new XmlTextReader("Sample.xsd"), null);

            // define handler for outputing compile errors

            ValidationEventHandler handler = new ValidationEventHandler(SchemaValidationClass.CompileErrors);

            XmlTextReader reader;

            XmlValidatingReader valid;

            try

            {

                  // open xml file to be validated

                  reader = new XmlTextReader("Sample.xml");

                  valid = new XmlValidatingReader(reader);

                  // add schema to validate xml file

                  valid.Schemas.Add(schema);

                  // set type of validation to schema

                  valid.ValidationType = ValidationType.Schema;

                  // read through the xml

                  while (valid.Read()) {}

                  Console.WriteLine("XML is ok!");

            }

            catch (Exception e)

{

      Console.WriteLine("XML is wrong!");

}

            finally

            {

                  reader = null;

                  valid = null;

                  schema = null;

            }

      }

 

      public static void CompileErrors(object sender, ValidationEventArgs args)

      {

            Console.WriteLine("Compile error: " + args.Message);

      }

}

14.6.13. Validate XML against DTD

Namespaces:

using System;

using System.Xml;

using System.Xml.Schema;

 

Code:

class DTDValidationClass

{

      static void Main(string[] args)

      {

            // define handler for outputing compile errors

            ValidationEventHandler handler = new ValidationEventHandler(DTDValidationClass.CompileErrors);

            XmlTextReader reader;

            XmlValidatingReader valid;

            try

            {

                  // open xml file to be validated

                  reader = new XmlTextReader("Sample.xml");

                  valid = new XmlValidatingReader(reader);

                  // set type of validation to DTD

                  valid.ValidationType = ValidationType.DTD;

                  // read through the xml

                  while (valid.Read()) {}

                  Console.WriteLine(reader.Name);

                  Console.WriteLine("XML is ok!");

            }

            catch (XmlException e)

            {

                  Console.WriteLine("XML is wrong! " + e.Message);

            }

            finally

            {

                  reader = null;

                  valid = null;

            }

      }

 

      public static void CompileErrors(object sender, ValidationEventArgs args)

      {

            Console.WriteLine("Compile error: " + args.Message);

      }

}

14.7. Extensible Stylesheet Language for Transformation (XSLT)

XSLT technology is simple but very powerful when appropriately used. Simply said XSLT enables transformation of an one XML document to another with different format like for example XHTML.

.NET Frameworks provides support for XSLT in XslTransform class.

14.8. XML Encryption

Details about xml encryption and specifications can be found on http://www.w3.org/TR/xmlenc-core/.

XML encryption is extension to existing security protocols like SSL/TLS and IPSec solving following issues:

Unlike SSL/TLS encrypted XML solves problem with encryption of only sensitive data, not whole communication and this can improve system performance.

SSL/TLS is not suitable for data storage, this scenario can be easily solved by encrypted xml.

15.         Computer environment

15.1.1. Local computer environment properties

Functionality

Namespace

Code

Get current application directory.

using System.Net;

Environment.CurrentDirectory;

Get NetBIOS local machine name.

using System.Net;

Environment.MachineName;

Get operating system name & version.

using System.Net;

Environment.OSVersion;

Get number of processors on local machine.

using System.Net;

Environment.ProcessorCount;

Get current stack trace information.

using System.Net;

Environment.StackTrace;

Get system directory path.

using System.Net;

Environment.SystemDirectory;

Get number of milliseconds since system start.

using System.Net;

Environment.TickCount;

Get domain name associated with current user.

using System.Net;

Environment.UserDomainName;

Get current user name.

using System.Net;

Environment.UserName;

Get CLR version number.

using System.Net;

Environment.Version;

Get amount of memory mapped to process context.

using System.Net;

Environment.WorkingSet;

 

15.1.2. Creating shortcut in special folders (Desktop, StartMenu, Startup)

This table lists all available special folders provided by WSH. They can be used in sample code and set into variable shortAdr.

 

Folder identifier

Description

AllUsersDesktop

Desktop shortcuts for all users.

AllUsersStartMenu

Startmenu schortcuts available to all users.

AllUsersPrograms

Programs shortcuts available to all users.

AllUsersStartup

Startup shortcuts set for all users.

Desktop

Desktop shortcuts of current user.

Favorites

Internet favorite shortcuts of current user.

Fonts

Folder with installed system fonts.

MyDocuments

“My Documents” folder of current user.

NetHood

Network shortcuts of current user.

PrintHood

Printer shortcuts of current user.

Programs

Programs shortcuts of current user.

Recent

Shortcuts to recetly opened documents by current user.

SendTo

Shortcuts displayed in “SendTo” menu (right-click on any file).

StartMenu

Startmenu shortcuts of current user.

Startup

Startup shortcuts of current user.

Templates

Application specific templates of current user.

 

Namespaces:

using System;

// link to this COM must be added in references - component "Windows script host object model"

using IWshRuntimeLibrary;

 

Code:

static void Main()

{

      // create shortcut on desktop

      object shortAdr = (object)"Desktop";

      // create shortcut in start menu of current user

      // object shortAdr = (object)"Startmenu";

      // create shortcut to My Documents folder of current user

      // object shortAdr = (object)"MyDocuments";

           

      WshShell shell = new WshShell();

      // address where to store link

      string link = ((string) shell.SpecialFolders.Item(ref shortAdr)) + @"\Calc.lnk";

      IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(link);

      // description of shortcut

      shortcut.Description = "Custom shortcut from .NET";

      // assing hotkeys for shortcut

      shortcut.Hotkey = "CTRL+SHIFT+A";

      // set full-path to application

      shortcut.TargetPath = Environment.GetFolderPath(Environment.SpecialFolder.System) + @"\Calc.exe";

      // create shortcut

      shortcut.Save();

}

15.1.3. Determine actual system power status

Namespaces:

using System;

using System.Runtime.InteropServices;

 

Code:

class Powerstatus

{

      [DllImport("Kernel32.dll", CharSet=CharSet.Auto)]

      public static extern bool GetSystemPowerStatus(ref SYSTEM_POWER_STATUS sps);

 

      // this structure contains data about power status

      public struct SYSTEM_POWER_STATUS

      {

            public System.Byte            ACLineStatus;

            public System.Byte            BatteryFlag;

            public System.Byte            BatteryLifePercent;

            public System.Byte            Reserved1;

            public System.Int32     BatteryLifeTime;

            public System.Int32     BatteryFullLifeTime;

      }

 

      static void Main(string[] args)

      {

            SYSTEM_POWER_STATUS sps = new SYSTEM_POWER_STATUS();

            // get power status from Windows

            if (GetSystemPowerStatus(ref sps))

            {

                  // output AC status

                  Console.Write("AC power status is ");

                  switch (sps.ACLineStatus)

                  {

                        case 0:

                              Console.Write("'offline'");break;

                        case 1:

                              Console.Write("'online'");break;

                        case 255:

                              Console.Write("'unknown status'");break;

                  }

 

                  // output battery status

                  Console.Write("\nBattery charge status is ");

                  switch (sps.BatteryFlag)

                  {

                        case 1:

                              Console.Write("'high'");break;

                        case 2:

                              Console.Write("'low'");break;

                        case 4:

                              Console.Write("'critical'");break;

                        case 8:

                              Console.Write("'charging'");break;

                        case 128:

                              Console.Write("'no system battery'");break;

                        case 255:

                              Console.Write("'unknown status'");break;

                  }

 

                  if (sps.BatteryFullLifeTime == -1) Console.WriteLine("\nBattery full lifetime is unknown.");

                  else Console.WriteLine("\nBattery full lifetime is " + sps.BatteryFullLifeTime + " seconds.");

 

                  if (sps.BatteryLifeTime == -1) Console.WriteLine("\nRemaining battery lifetime is unknown.");

                  else Console.WriteLine("\nRemaining rattery lifetime is " + sps.BatteryLifeTime + " seconds.");

 

                  if (sps.BatteryLifePercent == 255) Console.WriteLine("\nBattery percentage charge is unknown.");

                  else Console.WriteLine("\nBattery percentage charge is " + sps.BatteryLifePercent + "%.");

 

            } else Console.WriteLine("Power status could not be determined.");

      }

}

15.1.4. Enumerate installed printers on local machine

Namespaces:

using System;

using IWshRuntimeLibrary;

using System.Collections;

 

Code:

static void Main()

{

      WshNetwork net = new WshNetwork();

      foreach (IEnumerable enumer in net.EnumPrinterConnections())

      {

            Console.WriteLine(enumer.ToString());

      }

}

15.1.5. Set default printer on local machine

Namespaces:

using System;

using IWshRuntimeLibrary;

 

Code:

static void Main()

{

      // index specifies number of printer connection to use

      object index = (object)"1";

      WshNetwork net = new WshNetwork();

      IWshCollection enumer = net.EnumPrinterConnections();

      if (enumer.Count() > 0)

      {

            // set first printer connection as default

            net.SetDefaultPrinter((string)enumer.Item(ref index));

      }

}

15.1.6. Enumerate network drives

Namespaces:

using System;

using IWshRuntimeLibrary;

using System.Collections;

 

Code:

static void Main()

{

      WshNetwork net = new WshNetwork();

      foreach (IEnumerable enumer in net.EnumNetworkDrives())

      {

            Console.WriteLine(enumer.ToString());

      }

}

15.1.7. Integration with Windows (Help, Shotdown, Suspend, Control Panels)

Many Windows functions can be integrated into the .NET applications using Shell32 component. It must be added into the project references (find it under COM objects as “Microsoft Shell Controls And Automation”). Here is a list of provided methods with their sample usage.

 

Method name

Description

Sample

BrowseForFolder

Enables to open specific Windows folder dialog.

 

CascadeWindows

Re-arrange application Windows to cascade style.

x

ControlPanelItem

Starts Control Panel application according to parameter.

 

EjectPC

Ejects computer from docking station.

x

Explore

Opens a specific folder according to parameter.

 

FileRun

Opens “Run” dialog windows (same as Start -> Run).

x

FindComputer

Opens search dialog finding computer.

x

FindFiles

Opens search dialog finding files (same as Start -> Search -> For Files or Folders)

x

Help

Opens Windows help and support center.

x

MinimizeAll

Minimizes all windows on the desktop.

x

PrintHood

Printer shortcuts of current user.

 

Programs

Programs shortcuts of current user.

 

Recent

Shortcuts to recetly opened documents by current user.

 

SendTo

Shortcuts displayed in “SendTo” menu (right-click on any file).

 

StartMenu

Startmenu shortcuts of current user.

 

Startup

Startup shortcuts of current user.

 

Templates

Application specific templates of current user.

 

 

15.1.8. Open Control Panel items

This is simple sample on using Shell and some of its methods. Provided code will open many windows that represent Control Panel’s applications (same can be done using Run command or command prompt). It’s just demonstration on shell usage.

 

Namespaces:

using System;

// this is COM component that can be found under the name "Microsoft Shell Controls And Automation"

// this must be added to project references

using Shell32;

Code:

static void Main()

{

      Shell shell = new Shell();

      // accessibility options

      shell.ControlPanelItem("access.cpl");

      // add-remove programs

      shell.ControlPanelItem("appwiz.cpl");

      // bluetooth configuration

      shell.ControlPanelItem("btcpl.cpl");

      // desktop settings

      shell.ControlPanelItem("desk.cpl");

      // directX properties

      shell.ControlPanelItem("directx.cpl");

      // add hardware wizard

      shell.ControlPanelItem("hdwwiz.cpl");

      // internet properties

      shell.ControlPanelItem("inetcpl.cpl");

      // regional and language options

      shell.ControlPanelItem("intl.cpl");

      // Wireless link

      shell.ControlPanelItem("irprops.cpl");

      // Game controllers

      shell.ControlPanelItem("joy.cpl");

      // Mouse properties

      shell.ControlPanelItem("main.cpl");

      // Sounds and audio devices properties

      shell.ControlPanelItem("mmsys.cpl");

      // Network connections

      shell.ControlPanelItem("ncpa.cpl");

      // User accounts

      shell.ControlPanelItem("nusrmgr.cpl");

      // ODBC datasource administrator

      shell.ControlPanelItem("odbccp32.cpl");

      // Power options properties

      shell.ControlPanelItem("powercfg.cpl");

      // System properties

      shell.ControlPanelItem("sysdm.cpl");

      // Location information - telephone properties

      shell.ControlPanelItem("telephon.cpl");

      // Date and time properties

      shell.ControlPanelItem("timedate.cpl");

      // Automatic updates - WindowsUpdate settings

      shell.ControlPanelItem("wuaucpl.cpl");

}

15.1.9. Get folder items using Windows folder dialog

This sample list all items in Desktop folder, can be easily customized to other folder types (just see MSDN documentation).

 

Namespaces:

using System;

using Shell32;

 

Code:

static void Main()

{

      Shell shell = new Shell();

      // open dialog for desktop folder

      // use appropriate constant for folder type - ShellSpecialFolderConstants

      Folder folder = shell.BrowseForFolder(0, "Get some folder from user...", 0, ShellSpecialFolderConstants.ssfDESKTOP);

 

      if (folder != null)

      {

            foreach (FolderItem fi in folder.Items())

            {

                  Console.WriteLine(fi.Name);

            }

      }

}

16.         Other features

16.1.1. Get string resource from dll library

Code:

using System;

using System.Text;

using System.Runtime.InteropServices;

 

class StringResource

{

     private static int LOAD_LIBRARY_AS_DATAFILE = 2;

 

     [DllImport("kernel32.dll", SetLastError=true)]

     private static extern int LoadLibraryEx(string lpFileName, int hFile, int dwFlags);

 

     [DllImport("kernel32.dll", SetLastError=true)]

     private static extern string GetLastError();

 

     [DllImport("User32.dll")]

     private static extern int LoadString(int hModuleInstance,int lResourceID, StringBuilder strResource,int size);

 

     [DllImport("kernel32.dll", SetLastError=true)]

     private static extern void FreeLibrary(int hModuleInstance);

 

     public string GetResourceString(string executable, string resourceid)

     {

           int hFile = 0;

           hFile = LoadLibraryEx(executable, 0, LOAD_LIBRARY_AS_DATAFILE);

           if (hFile == 0)

           {

                throw new Exception("GetResourceString: Unable to load " + executable + " with error: " + GetLastError());

           }

 

           StringBuilder Resource = new StringBuilder(1023);

           int strLength = LoadString(hFile, Convert.ToInt32(resourceid), Resource, Resource.Capacity);

 

           if (strLength == 0)

           {

                FreeLibrary(hFile);

                throw new Exception ("GetResourceString: Unable to load string " + resourceid + " from " + executable);

           }

 

           FreeLibrary(hFile);

           return Resource.ToString();

     }

}

16.1.2. Handle events from other applications

This sample uses mapping of Win32 window’s messages to .NET events. That is why class Form is used to provide this wrapped native functionality. Code can be changed to hide WinForm and work just with called application like “notepad.exe” in this sample.

 

Namespaces:

using System;

using System.Windows.Forms;

using System.Diagnostics;

 

Code:

class ProcessSample : System.Windows.Forms.Form

{

      static void Main()

      {

            Application.Run(new ProcessSample());

      }

 

      public ProcessSample()

      {

            this.Load += new System.EventHandler(this.Form_Load);

            this.Visible = false;

      }

 

      private void Form_Load(object sender, System.EventArgs e)

      {

            this.Hide();

            // create a new process

            Process proc = new Process();

            // set process's application

            proc.StartInfo.FileName = "Notepad.exe";

            // proc.StartInfo.UseShellExecute = false;

            proc.EnableRaisingEvents = true;

            proc.SynchronizingObject = this;

            proc.Exited +=new EventHandler(closeHandler);

            proc.Start();

      }

 

      private void closeHandler(object sender, EventArgs e)

      {

            this.Close();

            Console.WriteLine("Application is finished.");

      }

}

16.1.3. Beep in application

Namespaces:

using System;

using System.Runtime.InteropServices;

 

Code:

class Beep

{

      [DllImport("kernel32.dll")]

      // frequency of the sound, 37 - 32767, duration in milliseconds

      static extern bool Beep (int freq, int duration);

 

      static void Main(string[] args)

      {

            Beep(1000, 100);

      }

}

16.1.4. Beep in application in Whidbey

Whidbey brings an option to beeping in application using .NET Framework classes and not interop. Here is the code.

Code:

Console.Beep();

Console.Beep(1000, 100);

16.1.5. Programming access to attributes

This sample presents how to access attributes defined in AssemblyInfo.cs file. But generally this technique can be used to access any attribute.

For instance programmer want to access attribute:

 

[assembly: AssemblyCompany("My enterprise company")]

 

This attribute holds a value with a name of company (in this case “My enterprise company”). To access this attribute from code, use the following code.

 

Code:

using System;

 

namespace SampleApp

{

      public class SampleClass

      {

            public SampleClass()

            {

                  System.Reflection.Assembly assembly = this.GetType().Assembly;

                  // here is defined type of attribute by System.Reflection.AssemblyCompanyAttribute parameter

                  System.Reflection.AssemblyCompanyAttribute attribute = (System.Reflection.AssemblyCompanyAttribute)System.Attribute.GetCustomAttribute(assembly, typeof(System.Reflection.AssemblyCompanyAttribute));

                  string company = attribute.Company;

                  Console.WriteLine(company.ToString());

            }

 

            public static void Main(string[] args)

            {

                  new SampleClass();

            }

      }

}

16.1.6. Get full-path & name of current process

Namespaces:

using System;

using System.Diagnostics;

 

Code:

static void Main(string[] args)

{

      Process curProcess = Process.GetCurrentProcess();

      String path = curProcess.MainModule.FileName;

      Console.WriteLine(path);

}

16.1.7. Programmatically create virtual website in IIS

Code:

using System;

using System.DirectoryServices;

 

class IIS

{

      static void Main()

      {

            // This is root directory on web site '1' on machine 'localhost'

            DirectoryEntry en = new DirectoryEntry( "IIS://localhost/w3svc/1/root" );

 

            // Create a virtual directory

            DirectoryEntry child = en.Children.Add( "myApplication", "IIsWebVirtualDir");

            child.Properties[ "Path" ].Value = @"C:\@Honza"; // Physical path to the folder

            child.CommitChanges(); // Save changes

 

            // Make the directory 'web application'

            child.Invoke(

                  "AppCreate2",        // Method name - 'create application'

                  new object[] { 2 }   // One argument - 'app type' - 0=inproc, 1=outproc, 2=pooled

                  );

      }

}

16.1.8. Get topmost window title using Win32 API

To get topmost window it is useful to use Win32 API, as it is demonstrated in code below. The sample assumes that programmer doesn’t know anything about the process that owns the topmost windows.

 

Code:

using System;

using System.Runtime.InteropServices;

using System.Text;

 

namespace SampleApp

{

      public class SampleClass

      {

            public static void Main(string[] args)

            {

                  StringBuilder title = new StringBuilder();

                  int strSize = 256;

                  // get handle to window with focus

                  int hwnd = GetForegroundWindow();

                  GetWindowText(hwnd, title, strSize);

                  Console.WriteLine(title);

            }

            [DllImport("user32.dll")]

            public static extern int GetWindowText(

                  int hWnd, // Handle to window

                  [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder lpString, // string buffer

                  int nMaxCount //buffer size

            );

 

            [DllImport("user32.dll")]

            public static extern int GetForegroundWindow();

      }

}

17.         ADO.NET

17.1. Architecture of ADO.NET

.NET brings a conceptually new data access API providing many enhanced functionalities and primary completely object approach to working with data and data API interfaces when previous ADO was completely redesigned leveraging more then ten years of experience with data services. Next ADO.NET is not based on COM model and uses new one based on OOP approach and newly designed classes.

 

ADO.NET gives access to source of data through data provider, which is specific to each data source and this provider encapsulates other classes related to working with data from that source.

By default in .NET environment can be found following data providers:

 

Data provider

Namespace

Description

.NET Framework Data Provider for SQL Server

System.Data.SqlClient

It’s high speed provider for SQL Server version 7.0 or later using optimized TDS communication. Still requires MDAC 2.6 or later.

.NET Framework Data Provider for OLE DB

System.Data.OleDb

OLE DB provider works via COM interop for existing version 2.5 or later. It’s not designed to be used with ODBC.

.NET Framework Data Provider for ODBC

System.Data.ODBC

(old: Microsoft.Data.ODBC)

ODBC provider is about 20% faster than OLE DB. In .NET Framework since version 1.1.

.NET Framework Data Provider for Oracle

System.Data.OracleClient

Oracle data providers supports Oracle client software version 8.1.7 or later. In .NET Framework since version 1.1.

.NET Compact Framework Data Provider for SQL Server CE

System.Data.SqlServerCe

Provides connection to SQL Server CE.

 

As it was presented in diagram above, data providers encapsulate many other classes. Their functionality will be shortly described in the next sections.

It’s obvious that connection classes hold information about connection to specific data source. Those classes implement interface IDbConnection when data source is a relational database.

Command classes are used to execute SQL statement or stored procedures and they implement IDbCommand interface (again when we talk about relational data source). A whole SQL command is stored in CommandText property (it can contain SQL statement, the name of stored procedure or finally the name of a table).

Command classes can have a collection of parameters and that is why there can be two types of classes: implementation of IDataParameterCollection and implementation of IDataParameter. Those two classes are holding parameter information and are used by command class.

 

ADO.NET provides two basic concepts related to working with data because data can be seen as online data or offline data. For those concepts ADO.NET gives two answers as it’s presented on the following figures.

17.1.1. Connecting to SQL Server, Oracle, MySQL and others

The best resource about connections to data source can be found on http://www.connectionstrings.com, here are just a few samples.

Namespaces:

using System;

// must be added reference to System.Data.OracleClient.dll

using System.Data.OracleClient;

using System.Data.SqlClient;

 

Code:

static void Main(string[] args)

{

      try

      {

            // ------------- SQL SERVER -------------

            // connection to SQL Server 7.x and 2000 using optimized driver

            SqlConnection sqlConn = new SqlConnection();

            // connect to SQL Server using integrated Windows security, its recommended for higher security

            sqlConn.ConnectionString = "Data Source=(local);Initial Catalog=Northwind;Integrated Security=SSPI;";

            sqlConn.Open();

            Console.WriteLine("Connected to SQL Server: " + sqlConn.State);

            sqlConn.Close();

 

            // ------------- Oracle -------------

            // Microsoft provides optimized Oracle driver, details on http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/manprooracperf.asp

            // Oracle provides custom .NET driver ODP.NET, http://www.oracle.com/technology/tech/windows/odpnet/index.html

            // third party optimized oracle driver, http://crlab.com/oranet/, http://www.crlab.com/oranet/oranet.msi

            OracleConnection oraConn = new OracleConnection();

            oraConn.ConnectionString = "Data Source=Oracle8i;User Id=user;Password=pass;Integrated Security=yes;";

            oraConn.Open();

            Console.WriteLine("Connected to Oracle: " + oraConn.State);

                  oraConn.Close();

 

            // ------------- MySQL -------------

            // Direct driver from Core Lab, http://crlab.com/mysqlnet/, http://www.crlab.com/mysqlnet/mysqlnet.msi

            // Direct driver from Seven Objects, http://www.sevenobjects.com/MySqlClient.aspx

 

            // ------------- Others -------------

            // see http://www.connectionstrings.com/

      }

      catch (Exception e)

      {

            Console.WriteLine(e.StackTrace);

      }

}

17.1.2. Watching connection state events and messages

Code:

using System;

using System.Data;

using System.Data.SqlClient;

 

class ConnectionStateWatch

{

     static void Main(string[] args)

     {

           SqlConnection sqlConn = new SqlConnection();

           sqlConn.ConnectionString = "Data Source=(local);Initial Catalog=Northwind;Integrated Security=SSPI;";

           // add method to delegate to handle state events

           sqlConn.StateChange += new StateChangeEventHandler(sqlConn_StateChange);

           // add method to delegate to handle messages from database

           sqlConn.InfoMessage += new SqlInfoMessageEventHandler(sqlConn_InfoMessage);

 

           try

           {

                sqlConn.Open();

           }

           catch (SqlException sqlE)

           {

           }

           finally

           {

                if (sqlConn.State == ConnectionState.Open) sqlConn.Close();

           }

 

           Console.ReadLine();

     }

 

     // this is the method added to delegate passing events to registered methods

     private static void sqlConn_StateChange(object sender, StateChangeEventArgs e)

     {

           Console.WriteLine("Original state: " + e.OriginalState.ToString());

           Console.WriteLine("Current state: " + e.CurrentState.ToString());

     }

 

     private static void sqlConn_InfoMessage(object sender, SqlInfoMessageEventArgs e)

     {

           Console.WriteLine("Message: " + e.Message.ToString());

     }

 

}

17.1.3. Executing SQL command and reading data in SqlDataReader

Namespaces:

using System;

using System.Data;

using System.Data.SqlClient;

 

Code:

static void Main(string[] args)

{

      SqlConnection conn = new SqlConnection("Integrated Security=yes;Initial Catalog=Northwind;Data Source=(local)");

      conn.Open();

      // select last names of just 5 top rows from table with employees

      SqlCommand command = new SqlCommand("select top 5 lastname from employees", conn);

      // this is default setting, SQL text is used to return data

      command.CommandType = CommandType.Text;

 

      // use datareader to work with data

      SqlDataReader reader = command.ExecuteReader();

      while (reader.Read())

      {

            // output last name

            Console.WriteLine(reader.GetString(0));

      }

 

      reader.Close();

      conn.Close();

}

17.1.4. Executing stored procedure and reading data in SqlDataReader

Namespaces:

using System;

using System.Data;

using System.Data.SqlClient;

 

Code:

static void Main(string[] args)

{

      SqlConnection conn = new SqlConnection("Integrated Security=yes;Initial Catalog=Northwind;Data Source=(local)");

      conn.Open();

      // command calls stored procedure 'Sales by Yeas' from Northwind database

      SqlCommand command = new SqlCommand("Ten Most Expensive Products", conn);

      command.CommandType = CommandType.StoredProcedure;

 

      // use datareader to work with data

      SqlDataReader reader = command.ExecuteReader();

      while (reader.Read())

      {

            Console.WriteLine(reader.GetString(0));

      }

 

      reader.Close();

      conn.Close();

}

17.1.5. Executing multiple SQL statements (batch)

Namespaces:

using System;

using System.Data;

using System.Data.SqlClient;

 

Code:

static void Main()

{

     // define multiple sql statements

     string batch = "SELECT TOP 3 ProductName FROM Products; SELECT TOP 3 CustomerID FROM Orders";

 

     SqlConnection conn = new SqlConnection("Integrated Security=yes;Initial Catalog=Northwind;Data Source=(local)");

     conn.Open();

 

     SqlCommand sqlComm = new SqlCommand(batch, conn);

     SqlDataAdapter adapter = new SqlDataAdapter(sqlComm);

     DataSet ds = new DataSet();

     // fill dataset with both resultset from both queries

     adapter.Fill(ds);

     foreach(DataTable dt in ds.Tables)

     {

           // output table name in dataset collection

           Console.WriteLine("Table name: {0}", dt.TableName);

           foreach(DataRow dr in dt.Rows)

           {

                // output first column name and its value

                Console.WriteLine("\tColumn {0} has value {1}", dt.Columns[0].ColumnName, dr[0]);

           }

     }

     conn.Close();

}

17.1.6. Executing stored procedure and reading data from multiple result sets in SqlDataReader

DataReader is able to work with multiple results set returned from database. This sample uses custom stored procedure ReaderSampleProc. Its code is available here:

 

ALTER PROCEDURE ReaderSampleProc AS

 

SET ROWCOUNT 5

 

SELECT Employees.LastName FROM Employees

SELECT Products.ProductName FROM Products

 

Namespaces:

using System;

using System.Data;

using System.Data.SqlClient;

 

Code:

static void Main(string[] args)

{

      SqlConnection conn = new SqlConnection("Integrated Security=yes;Initial Catalog=Northwind;Data Source=(local)");

      conn.Open();

      // command calls stored procedure 'Sales by Yeas' from Northwind database

      SqlCommand command = new SqlCommand("ReaderSampleProc", conn);

      command.CommandType = CommandType.StoredProcedure;

 

      // use datareader to work with data

      SqlDataReader reader = command.ExecuteReader();

      // output data from first result set

      Console.WriteLine("-------------- Employees --------------");

      while (reader.Read())

      {

            Console.WriteLine(reader.GetString(0));

      }

 

      // !!!! move to next result set !!!!

      reader.NextResult();

 

      // output data from second result set

      Console.WriteLine("-------------- Products --------------");

      while (reader.Read())

      {

            Console.WriteLine(reader.GetString(0));

      }

 

      reader.Close();

      conn.Close();

}

17.1.7. Executing stored procedure and getting data in DataSet

Logical schema of this sample is illustrated on the following figure:

Namespaces:

using System;

using System.Data;

using System.Data.SqlClient;

 

Code:

static void Main(string[] args)

{

// this is just another option how to work with resources

      using (SqlConnection conn = new SqlConnection("Integrated Security=yes;Initial Catalog=Northwind;Data Source=(local)"))

      {

            // command calls stored procedure 'Sales by Yeas' from Northwind database

            SqlCommand command = new SqlCommand("Sales by Year", conn);

            command.CommandType = CommandType.StoredProcedure;

 

            // setting parameters for stored procedure

            command.Parameters.Add("@Beginning_Date", SqlDbType.DateTime, 8).Value = "01/01/1997";

            command.Parameters.Add("@Ending_Date", SqlDbType.DateTime, 8).Value = "31/01/1997";

 

            // use dataadapter to read data

            SqlDataAdapter adapter = new SqlDataAdapter(command);

            DataSet dataset = new DataSet();

 

            adapter.Fill(dataset);

 

            // output xml data

            Console.WriteLine(dataset.GetXml());

      }

}

17.1.8. Updating database data with changes in DataSet

Namespaces:

using System;

using System.Data;

using System.Data.SqlClient;

 

Code:

static void Main(string[] args)

{

      // this is just another option how to work with resources

      using (SqlConnection conn = new SqlConnection("Integrated Security=yes;Initial Catalog=Northwind;Data Source=(local)"))

      {

            // command calls stored procedure 'Sales by Yeas' from Northwind database

            SqlCommand command = new SqlCommand("select top 5 * from employees order by employeeID", conn);

 

            // use dataadapter to read data

            SqlDataAdapter adapter = new SqlDataAdapter(command);

            DataSet dataset = new DataSet();

 

            adapter.Fill(dataset);

 

            // do some changes on dataset data - in this sample first row

            DataRow row = dataset.Tables[0].Rows[0];

            // update last name of employee

            row["LastName"] = "Changed name";

 

            // add new row to the table

            row = dataset.Tables[0].NewRow();

            // row["EmployeeID"] = "";

            row["LastName"] = "Seda";

            row["FirstName"] = "Jan";

            row["Title"] = "Software engineer";

            row["TitleOfCourtesy"] = "Mr.";

            dataset.Tables[0].Rows.Add(row);

 

            // update database using SqlCmdBuilder, which build SQL commands to update table

            SqlCommandBuilder builder = new SqlCommandBuilder(adapter);

 

            // it's obvious there are some changes, but this is correct code for real application

            if (dataset.HasChanges()) adapter.Update(dataset);

      }

}

17.1.9. Accessing Excel data using ADO.NET

Microsoft provides other data access provides as ODBC provider for .NET Framework. You can find those updates and providers on http://msdn.microsoft.com/netframework/downloads/updates/default.aspx

and download ODBC .NET data provider and install it.

 

Namespaces:

using System;

using System.Data;

using System.Data.Odbc;

 

Code:

static void Main()

{

     OdbcConnection conn = new OdbcConnection("Driver={Microsoft Excel Driver (*.xls)};DriverId=790;Dbq=sample.xls;");

     OdbcCommand sqlComm = new OdbcCommand("SELECT * FROM [Sheet1$]", conn);

 

     try

     {

           conn.Open();

           // first use datareader to move over the data in Excel file

           OdbcDataReader reader = sqlComm.ExecuteReader();

           while (reader.Read())

           {

                Console.WriteLine(reader.GetValue(0));

           }

     }

     catch (Exception e)

     {

           Console.WriteLine(e.StackTrace);

     }

     finally

     {

           conn.Close();

     }

}

17.1.10. List available SQL servers

This sample works with SQLDMO and that is why is not appropriate for general client where this COM component is not installed but can be used in backend applications.

 

Namespaces:

using System;

// COM component 'Microsoft SQLDMO Object Library' must be included in project references

using SQLDMO;

 

Code:

static void Main(string[] args)

{

      ApplicationClass appClass = new ApplicationClass();

      // this method provides list of available SQL servers

      NameList list = appClass.ListAvailableSQLServers();

      for(int i = 0; i < list.Count; i++)

      {

            // output SQL server list

            Console.WriteLine(list.Item(i + 1).ToString());

      }

}

18.         ADO.NET & System.Xml 2.0 (Whidbey)

18.1. Summary of new features in ADO.NET 2.0

ADO.NET 2.0 (or called by code name ADO.NET Whidbey) brings no model changes, so if it works now, it will work with ADO.NET 2.0.

18.1.1. Asynchronous Data Access

Asynchronous data access brings better performance to client and server applications by non blocking UI or server threads by waiting for source response. This feature provides performance improvement also when combined with MARS.

ADA works fine with SQL Server 7, 2000 and “Yucon”.

18.1.2. Batch Updates

Currently DataAdapter.Update does a round-trip per row, which is not efficient in case of batches.In ADO.NET 2.0 batching is supported by extending framework with a feature adapter.UpdateBatchSize. Also other enhancements are made with respect to batching, see samples.

This feature is available for SqlClient and OracleClient.

18.1.3. DataSet Performance

 

18.1.4. MARS (Multiple Active Results Sets)

MARS is a technology allowing a connection

 

18.2. Summary of new features in System.Xml


19.         Appendix A - Fast-track to C# language

This is just a very brief introduction to C# programming language giving basic understanding. All details can be found in C# language specification on http://download.microsoft.com/download/0/a/c/0acb3585-3f3f-4169-ad61-efc9f0176788/CSharp.zip.

19.1. Basic terms and definitions in .NET & C#

Application – refers to assembly that has an entry point (method Main()). When application is started, a new application domain is created and each application is separated in its own application domain.

Application domain – this is a new term in .NET and application domain brings better management and security when working with .NET applications. An application domain is something like a container with its own libraries and settings. No data or any areas of application domain can be shared between other domains.

Assembly – assemblies are libraries containing final output from compilation of application. Assemblies have similar format like “.dll” files with some differences (primary metadata section) and may contain executable code, type definitions and other resources.

Class library – this term refers to assembly that can be used by other assemblies. Class library is similar to “.dll” file loaded into the process’s address space (in case of typical Win32 application) when class library is loaded into the application domain (class library is not running in a separate application domain).

Namespace – namespaces provide logical organization system where classes are logically divided into separate “sections”. When compared with Java, namespaces unlike packages in Java are just logical and don’t require to be implemented in physically stored classes in each folders representing logical structure.

Unsafe code – unsafe code is a way how to run low-level operations like direct memory manipulations, pointers, etc. This is a way how to call direct APIs of operation system or optimize performance dependent algorithms (for example game development).

19.2. What is C#?

First of all, it’s important to say what C# is and which features aren’t included in this programming language. So here is the list of features which are significant for C# and are the most typical:

 

 

And here is the list of features that were disposed of C#:

 

19.3. Hello world

This is the basic skeleton of C# application presenting layout and structure:

 

using <namespace>

namespace <my own namespace> [{]

      public class <class name> {

            public static void Main()  {

            }

      }

[}]

 

It’s typical to start talking about programming language by “Hello world” application:

 

using System;

namespace Hello {

      public class HelloWorld {

            public static void Main()  {

                  Console.WriteLine("Hello world application.");

            }

      }

}

19.4. Assemblies

When you compile the “Hello world” sample assembly will be created. But what is assembly? This term is specific to .NET and it defines type of file that encapsulates all application content like compiled code, metadata, resources etc. It can be understood as a “.dll” library (assembly has similar physical layout) but assemblies can only be used in .NET environment and aren’t just a file-storage of application data (assemblies are related with rights, permitions, place of storage etc.) but are improved by many important features (see other chapters).

19.4.1. Locating of assemblies

It’s very important to understand how .NET runtime (Common Language Runtime – CLR) locates assemblies.

The process of locating and binding assemblies can be divided into two types of reference to assemblies:

Static references are created when application is being compiled and compiler makes static references to assemblies. Figure below illustrates sample references from the environment of Visual Studio .NET and those references are built as the static ones.

These references are stored in metadata when assembly is built.

These references are constructed on the fly by programmer using specific methods such as System.Reflection.Assembly.Load or AppDomain.Load.

 

19.4.2. Assembly layout

Assemblies encapsulate many types of data and that is why their layout must be defined properly. Here is the list of sections contained inside of assemblies and their short descriptions.

 

19.5. Identifiers

Identifiers are names used by programmers for methods, variables, types, classes, interfaces etc.

19.6. Types

There could be a long story about types and their representation in CLR. But this book is just a reference giving fast help and introduction to C# and that is why types are covered here just in a basic form.

Generally all data types in .NET are based on strong data typing. This definition says that all variables belong to specific type and all .NET languages must complain on this behavior.

19.6.1. Hierarchy of types

All types in C# are derived from System.Object and can access its methods even the primitive types like int, double etc. (this is done through the boxing/unboxing technique, see chapter 19.6.10).

19.6.2. Predefined types

Predefined types are the basic ones and are defined in all .NET environments (ISO compliant, details on ECMA site) and are not dependent on any other libraries, just the basic ones.

19.6.3. Integral types

C# type

.NET type

Size (bytes)

Signed

Range

sbyte

System.Sbyte

1

yes

-128 to 127

byte

System.Byte

2

no

0 to 255

char

System.Char

2 (Unicode)

-

U+0000 to U+ffff

short

System.Int16

2

yes

-32,768 to 32,767

ushort

System.UInt16

2

no

0 to 65,535

int

System.Int32

4

yes

-2,147,483,648 to 2,147,483,647

uint

System.UInt32

4

no

0 to 4,294,967,295

long

System.Int64

8

yes

-9,223,372,036,854,775,808 to -9,223,372,036,854,775,807

ulong

System.UInt64

8

no

0 to 18,446,744,073,709,551,615

 

Initialization of integral types can be defined in decimal or hexadecimal form:

 

// decimal initialization

int decimalVar = 12345;

// hexadecimal initialization

int hexadecimalVar = 0xA345;

 

Values can be filled in by the following suffixes:

 

U – uint and ulong types

 

uint four = 15U;

ulong eight = 15U;

 

L – long and ulong types

 

long signed_eight = 15L;

ulong eight = 15L;

 

UL – ulong type

 

ulong eight = 15UL;

 

Implicit conversions of integral types must be performed just between types where is not possible loss of data. Here is a list of possible implicit conversions:

 

// one byte identifiers

byte one = 1;

sbyte signed_one = -1;

 

// two bytes identifiers

short signed_two;

ushort two;

 

// four bytes indentifiers

int signed_four;

uint four;

 

// eight bytes identifiers

long signed_eight;

ulong eight;

 

// sample implicit conversions

signed_eight = signed_four = signed_two = signed_one;

eight = four = two = one;

 

The figure presenting implicit conversions can be found in chapter 19.6.9.

Special type of integral type is char type holding Unicode values representing literals or escape character (see table below).

 

Escape character

Description

Value

\0

Null

0x0000

\a

Bell

0x0007

\b

Backspace

0x0008

\t

Horizontal tab

0x0009

\n

New line

0x000A

\v

Vertical tab

0x000B

\f

Form feed

0x000C

\r

Carriage return

0x000D

\"

Double quote

0x0022

\'

Single quote

0x0027

\\

Backslash

0x005C

 

Char type can be converted to any

19.6.4. Floating-point types

C# type

.NET type

Size (bytes)

Signed

Aprox. Range

Precision

float

System.Single

4

yes

±1.5x1045 to ±4.5x1038

7 digits

double

System.Double

8

yes

±5.0x10324 to ±1.7x10308

15-16 digits

 

Implicit conversions of floating-point types must value keep data[JS1]  as integral types. That is why conversion from float to double is allowed but not vice versa.

Also int, uint and long can be converted to float and from long to double.

19.6.5. Decimal type

C# type

.NET type

Size (bytes)

Signed

Aprox. Range

Precision

decimal

System.Decimal

12

yes

±1.0x1028 to ±7.9x1028

28-29 significant num.

 

Decimal type has better precision but unlike floating-point type smaller range.

When decimal values are assigned, then they require suffix “m” or “M”:

 

decimal twelve = 465632.3676m;

19.6.6. Bool type

C# type

.NET type

Size (bytes)

Signed

Value

bool

System.Boolean

1-2

no

true, false

 

In C# Boolean type can be assigned just to true or false values, which are represented by one bit (1 or 0) but because of processor’s addressing space at least one byte is occupied. When Boolean type is used in arrays, it occupies 2 bytes of memory space.

Boolean type can’t be converted to any numeric type or vice versa.

19.6.7. Object type

C# type

.NET type

Size (bytes)

Value

object

System.Object

0/8

 

 

The object type is a base class for all types in .NET Framework. This type is going across other types like value and reference types, when in case of value types object has no overhead (special 8 bytes used to work with object, to address them, synchronize them and monitor by garbage collector).

19.6.8. String type

C# type

.NET type

Size (bytes)

Value

string

System.String

>=20

Unicode

 

String object is immutable object which means that instance of string can’t be changed without allocation of new string object. This is a reason why string operations are the most frequent reason for performance problems.

19.6.9. Implicit conversions of numeric values

On the diagram below are shown possible implicit conversions of built-in types in .NET. The[JS2] 

19‑1

19.6.10. Boxing and unboxing

Boxing and unboxing is typical part of type system and a nice and very good feature that was copied by other languages like Java (from version 1.5). It provides an easy way how to tread reference types as value types and vice versa. This is a very strong feature because it simplifies development and OOP layout of classes holding data.

Boxing is an implicit conversion of value type to the reference type. When value type is boxed, then memory is allocated on the heap for the reference type, which is boxing a value type.

Unboxing is implicit conversion method how to convert from reference type back to value type.

See the sample bellow:

 

static void Main(string[] args)

{

      int i = 123;

      // boxing, this will box the value type '123'

      object o = i;

// unboxing

      int j = (int)o;

 

      Console.WriteLine(o);

 

      if (o is int)

      {

            Console.WriteLine("Object is treated as int value.");

      }

}

19.7. Variables & parameters

19.7.1. Types of variables & parameters