´╗┐using System;
using System.Text;
using System.IO;
using CryptoSysPKI;

namespace MpegOfCat
{

    /* 
     * This demonstrates the feature in CryptoSys PKI v12 to add an arbitrary X.509 Extension to a version 3 X.509 certificate.
     * As an extreme example, we add a video of a cat. This was done originally by Dave Smith of New Zealand back in 1998.
     * We've used a cut-down video of Dave playing with his cat. It's an mp4 file instead of an mpeg file, to make it smaller, 
     * but you'll get the idea.
     * How useful is this? Well, not at all. The original example by Dave Smith was to demonstrate that you could legally put any 
     * old rubbish in an X.509 certificate, even a video of you playing with your cat. 
     * You can, of course, use this feature to add proper Extensions to an X.509 certificate not otherwise supported with the 
     * standard options in CryptoSys PKI. You need a good understanding of ASN.1 DER encoding to do this.
     * 
     * The video is added as an Extension of type "subjectAltName" (2 5 29 17) with a GeneralName using CHOICE [0] AnotherName,
     * which takes any value with tag [0]. In this case, the type-id is mpeg-1 and the value is the contents 
     * of the mpeg file as an implicit OCTET STRING with tag [0].
     * In this example, note the mp4 signature "ftypmp42" (hex: 66 74 79 70 6D 70 34 32) at offset 4.
     * If you extract and save the 215388 bytes starting 00 00 00 18 66 74 79 70 6D 70 34 32 ..., you have the mp4 file.
     */

    /*
     * extnValue (to be encapsulated in an OCTET STRING)
    000000  34978: SEQUENCE {
    000005  34973: . [0] {
    00000A      B: . . OBJECT IDENTIFIER mpeg-1 (1 3 6 1 4 1 3029 42 11172 1)
    000017  34961: . . [0] {
    00001C  3495C: . . . OCTET STRING
                 : . . . . 00 00 00 18 66 74 79 70 6D 70 34 32 00 00 00 00
                 : . . . . 69 73 6F 6D 6D 70 34 32 00 00 03 E5 6D 6F 6F 76
                 : . . . . 00 00 00 6C 6D 76 68 64 00 00 00 00 D6 E1 D3 C6
                 : . . . . D6 E1 D3 C6 00 01 5F 90 00 03 4B C0 00 01 00 00
                 : . . . . 01 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00
                 : . . . . 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00
                 : . . . . 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00
                 : . . . . 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
                 : . . . . . . . . [ Another 215260 bytes skipped ]
                 : . . . }
                 : . . }
                 : . }
     */
/* Using Asn1.TextDump()
30 83 03 49 78  --SEQUENCE/215416=0x34978
   a0 83 03 49 73  --[0]/215411=0x34973
      06 0b  --OBJECTIDENTIFIER/11=0xB
         2b 06 01 04 01 97 55 2a d7 24 01 
         --mpeg-1 (1.3.6.1.4.1.3029.42.11172.1)
      a0 83 03 49 61  --[0]/215393=0x34961
         04 83 03 49 5c  --OCTETSTRING/215388=0x3495C
            00 00 00 18 66 74 79 70 6d 70 34 32 00 00 00 00 
            69 73 6f 6d 6d 70 34 32 00 00 03 e5 6d 6f 6f 76  
-----[cut]-----
            83 d8 c5 96 b4 16 29 ee 01 6c db f2 94 96 f5 4b 
            6d 4f f7 79 87 13 f0 00 00 00 01 0a 
--(215421 bytes)
 */

    class MpegOfCat
    {
        static void Main(string[] args)
        {
            string vidfile = "x509cat.mp4";
            string certfile = "cat.cer";
            // Compose DER-encoded ASN.1 value in hex. In effect, by hand.
            // We'll cheat here and assume the ASN.1 length bytes are always of the form 83 xx xx xx,
            // so we are expecting a video file of size 65536 to 16777215 bytes (0x010000 - 0xFFFFFF)

            // Our template in hex (white space will be removed later)
            string template =
@"30 83 www
   a0 83 xxx
      06 0b
         2b 06 01 04 01 97 55 2a d7 24 01       
      a0 83 yyy
         04 83 zzz
";
            // where "zzz" = length of video file in hex (3 bytes)
            // "yyy" = zzz + 5
            // "xxx" = zzz + 23
            // "www" = zzz + 28

            Console.WriteLine("PKI Version = {0}", General.Version());

            // Read in video file as array of bytes
            byte[] vidarr = File.ReadAllBytes(vidfile);
            int vidlen = vidarr.Length;
            Console.WriteLine("FILE: {0}: {1} bytes", vidfile, vidlen);

            // Compose lengths as 3-byte big-endian values hex encoded
            string zzz = NumToHex3(vidlen);
            Console.WriteLine("zzz={0}", zzz);
            string yyy = NumToHex3(vidlen + 5);
            Console.WriteLine("yyy={0}", yyy);
            string xxx = NumToHex3(vidlen + 23);
            Console.WriteLine("xxx={0}", xxx);
            string www = NumToHex3(vidlen + 28);
            Console.WriteLine("www={0}", www);

            // Substitute hex-encoded value in template and strip spaces
            string asn1hex = template.Replace("www", www).Replace("xxx", xxx).Replace("yyy", yyy).Replace("zzz", zzz);
            Console.WriteLine("ASN.1:\n{0}", asn1hex);
            asn1hex = asn1hex.Replace(" ", "").Replace("\r", "").Replace("\n", "");
            Console.WriteLine(asn1hex);

            // Append video bytes in hex
            asn1hex = asn1hex + Cnv.ToHex(vidarr);

            // Compose Extension string of form <dotted-oid>=#<hexstring> (don't forget the hash symbol '#')
            string extns = "2.5.29.17=#" + asn1hex;

            // Create X509 certificate, signed by Carl, using Alice's public key
            int r = X509.MakeCert(certfile, "CarlRSASelf.cer", "AlicePubRSA.pub", "CarlPrivRSASign.p8e", 0x1001, 4, "CN=Alice;OU=Feline",
                extns, X509.KeyUsageOptions.DigitalSignature, "password", 0);
            if (0 == r) {
                Console.WriteLine("Created certificate '{0}'", certfile);
            } else {
                Console.WriteLine("ERROR: {0}: {1}", General.ErrorLookup(r), General.LastError());
            }

        }

        /// <summary>
        /// Compose hex encoding of integer n represented by exactly 3 bytes.
        /// </summary>
        /// <param name="n">Integer n to be encoded.</param>
        /// <returns>Hex encoding of exactly 6 hex digits, n mod 0xFFFFFF.</returns>
        static string NumToHex3(int n)
        {
            uint z = (uint)n;
            byte[] zz = Cnv.NumToBytes(z, Cnv.EndianNess.BigEndian);
            string zzz = Cnv.ToHex(zz).Substring(2);
            return zzz;
        }
    }
}