Cryptography with .Net

(Hide table-of-contents)

Overview

This page contains examples of the most common crypting tasks. It is probably the easiest way to find out about how to use Cryptography with .Net.

For a complete reference see Wikipedia.

Math

The mathematics involved in cryptography are not difficult to grasp. It is the implementation which is more difficult, specially with languages that don't offer extended precision arithmetic.

.Net offers a series of classes to handle cryptography. They reside in System.Security.Cryptography which is in System.Core.dll

APL Code

Here is a namespace containing a few utilities and a HASH function:

Hash funtion

:Namespace CryptoTools

   ∇ r←DNetCrypto ⍝ This is the location of the cryptography libraries
     r←'System.Security.Cryptography,\Program Files' ⍝ change for 64b OS
     r,←'\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll'

   ∇ value←hash string;str;⎕USING
    ⍝ Return hash value of the string given as arg
     :If isChar str←,string
         str←⎕UCS str ⍝ turn characters into numbers for the hash fn
         {}⎕DR str       ⍝ kludge for V12 to ensure str is small int
     :EndIf
     ⎕USING←DNetCrypto                              
     value←(⎕NEW SHA256Managed).ComputeHash⊂str ⍝ also SHA1/384/512, CRC32 &  MD5CryptoServiceProvider

   if←/⍨ ⋄ UTF8←'UTF-8'∘⎕ucs ⋄ isChar←{∨/0 2=10|⎕DR 1/⍵}
   UCSN←{~isChar ⍵:⍵ ⋄ v{⍺}⎕dr v←UTF8 ⍵} ⍝ ⎕DR forces demotion (for V12)

:EndNamespace

The hash function (here SHA256, but it could be another) can be applied to any string:

      CryptoTools.hash  ⊃,/⎕src CryptoTools
165 157 223 218 183 249 78 22…

Symmetric encryption

The following class will handle symmetric cryptography:

:Class  CodeSymmetric

    :include CryptoTools
    
    M2MS←{8÷⍨⍵[2]+0,s×⍳(-/2↑⍵)÷1⌈s←¯1↑⍵} ⍝ valid Key sizes [min-max]
    ∇ boa provider;choices;msg;n
      :Access public
      :Implements constructor
⍝ Data Encryption Standard (DES) supports a 64 bit key only
⍝ Rivest Cipher 2 provider supports keys from 40 to 128* bits
⍝ Rijndael (also known as AES) provider supports keys 128/192/256*
⍝ TripleDES provider (also known as 3DES) supports keys of 128/192*
      choices←'DES' 'RC2' 'Rijndael' 'TripleDES'
      msg←'Invalid provider; choose one of',⍕choices
      msg ⎕SIGNAL 99 if(⍴choices)<n←choices⍳⊂provider
      ⎕USING←DNetCrypto
      choices←DESCryptoServiceProvider RC2CryptoServiceProvider
      _algo←⎕NEW n⊃choices,RijndaelManaged TripleDESCryptoServiceProvider
     
      n←⊃_algo.LegalKeySizes.(MaxSize MinSize SkipSize)
      _vks←M2MS n   ⍝ all valid Key sizes
     
      IV←'1Az=-@qT' ⍝ Initialisation Vector
     
      ⎕DF provider  ⍝ conveniently identify instance

    ∇ r←RandomKey ⍝ This generates a random Key
      :Access public
      _algo.GenerateKey
      r←_algo.Key

    :property Key ⍝ The key used to encrypt/decrypt data
    :access public
        ∇ r←get
          r←_key
        ∇ set val;val;msg;t
          msg←'⍴Key must be ',((1<⍴t)/'one of '),⍕t←_vks
          msg ⎕SIGNAL 11 if  ~(⍴val←val.NewValue)∊t
          _algo.Key←_key←UCSN val
    :endproperty

   ⍝ Using the default Cipher Block Chaining (CBC) mode, all data blocks
   ⍝ are processed using the value derived from the previous block; the
   ⍝ first data block has no previous data block to use, so it needs an
   ⍝ Initialisation Vector (IV) to feed the first block

    :property IV
    :access public
        ∇ r←get
          r←_iv
        ∇ set Value;val;validBS
        ⍝ We must ensure the value fits requirements:
          validBS←M2MS⊃_algo.LegalBlockSizes.(MaxSize MinSize SkipSize)
          :If ~validBS∊⍨⍴val←Value.NewValue ⍝ if not a valid block size
              val←validBS[1++/validBS<⍴val]⍴val ⍝ pick the next one up
          :EndIf
          _algo.IV←_iv←UCSN val
    :endproperty

⍝ Encrypts the specified Data using preset key and preset IV

    ∇ r←EncryptString d;⎕USING;ms;cs;cr
      :Access public
      ⎕USING,⍨←⊂'System'
     
     ⍝ The key and IV have better been set
      ms←⎕NEW IO.MemoryStream
      cr←_algo.CreateEncryptor ⍬
      cs←⎕NEW CryptoStream(ms cr CryptoStreamMode.Write)
      cs.Write((UCSN d)0,⍴d)
      cs.Close
      ms.Close
      r←ms.ToArray

    ⍝ Decrypts the specified data using preset key and preset IV

    ∇ r←DecryptCipher encryptedData;b;ms;cs;len;⎕USING
      :Access public
      ⎕USING,⍨←'System' 'Dyalog'
      ms←⎕NEW IO.MemoryStream(encryptedData 0,⍴encryptedData)
     cs←⎕NEW CryptoStream(ms(_algo.CreateDecryptor ⍬)CryptoStreamMode.Read)
      b←⎕NEW ByRef,⊂⊂3/⍨⍴encryptedData
      len←cs.Read(b 0,⍴encryptedData)
      cs.Close
      r←⎕UCS len↑b.Value

:EndClass

Let's try it:

s1←⎕new CodeSymmetric  'TripleDES'
      s1.Key←16⍴'secret'
      +cs1←s1.EncryptString 'Let''s rendez-vous at midnight'
155 230 195 207 193 180 216 228 2 14 11…
      s1.DecryptCipher cs1
Let's rendez-vous at midnight

Any instance of the class will do to decrypt the cipher as long as the Initialisation Vector and the key are the same.

Obviously. that code could be modified to, say, accept the Initialisation Vector and/or the key at instantiation time, not to mention the handling of certificates, etc.

Asymmetric encryption

The following class will handle asymmetric cryptography:

:Class CodeAsymmetric
⍝ The only provider supported is the RSACryptoServiceProvider.

    :include CryptTools  
    ⎕using← DNetCrypto ⋄ ⎕io ⎕ml←1 ⋄ ⎕wx←3
    ∇ boa0 ⍝ The public Key is automatically generated
      :Implements constructor
      :Access public
      _rsa←⎕NEW RSACryptoServiceProvider
      GenerateNewKeys
    ∇ boa1 arg ⍝ This is where you make the public Key.
  ⍝ It could be made of INI files, XML, etc.
  ⍝ Here we only accept XML strings.
      :Implements constructor
      :Access public
      _rsa←⎕NEW RSACryptoServiceProvider
      _rsa.FromXmlString⊂arg
  ⍝ Generates a new public/private key pair as strings
    ∇ {(publicKeyXML privateKeyXML)}←GenerateNewKeys
  ⍝ Generate new keys for this instance
      :Access public
      publicKeyXML←_rsa.ToXmlString 0
      privateKeyXML←_rsa.ToXmlString 1
    ∇ r←Decrypt cipher
      :Access public
      r←UTF8 _rsa.Decrypt cipher 0
    ∇ r←Encrypt d
      :Access public
      r←_rsa.Encrypt((UCSN d)0)

:EndClass

Now, Adam creates an object with both public and private keys:

      adam←⎕new CodeAsymmetric 
      (pub  pvt)← adam.GenerateNewKeys        

Adam forwards the public key to Bea who uses it to send him messages:

      bea←⎕new   CodeAsymmetric    pub
      +msg←bea.Encrypt  'meet me at midnight’
89 12 181 136 53 …

Adam decrypts the message like this:

        adam.Decrypt  msg
meet me at midnight

That’s how easy it is.

Because asymmetric cryptography is calculation intensive it is best to limit the material to small strings.

Now, in real life messages tend to be longer and symmetric cryptography works best. What some people do is to encrypt the large text symmetrically with a key which is encrypted asymmetrically. They often also add a signature, for example the hash of the message and/or encrypted key to ensure everything is kosher. Something along the lines of

      beamsg←1000⍴'long message... '
      code←⎕new CodeSymmetric 'RC2'
      code.Key←cpw←'secret'
      ⍴cryptedmsg←code.EncryptString beamsg
1008
      ⍴cryptedkey←bea.Encrypt cpw
128
      ⍴H←CryptoTools.hash cryptedkey,cryptedmsg
32

She then sends Adam the encrypted message, the encrypted key and the hash. Adam first checks there’s been no tampering:

      H≡CryptoTools.hash cryptedkey,cryptedmsg
1

He then finds the key used to encrypt the message:

      adam.Decrypt cryptedkey
secret

Then he finally decodes the message:

      decode←⎕new CodeSymmetric 'RC2'
      decode.Key←'secret'
      decode.DecryptCipher cryptedmsg
long message... long message... long message... 

That is the basis of message exchange protocols like SSL which will bundle up all the stuff to transfer, adding their own packaging information.

There are all sorts of different links available. Wikipedia is a reliable source.
Vector also has been known to have articles on cryptography.


CategoryDotNet - CategoryDyalogExamplesDotNet