/// <summary>
        /// Main method specifying where program execution is to begin
        /// </summary>
        /// <param name="args">Command line arguments passed to the program</param>
        static void Main(string[] args)
        {
            try
            {
                // Parse command line arguments
                string pkcs11Library   = null;
                int    listTokens      = 0;
                int    listObjects     = 0;
                int    sign            = 0;
                string tokenSerial     = null;
                string tokenLabel      = null;
                string pin             = null;
                string keyLabel        = null;
                string keyId           = null;
                string dataFile        = null;
                string signatureFile   = null;
                string hashAlg         = null;
                string signatureScheme = null;
                string outputFormat    = null;
                string certsDir        = null;

                if (args.Length == 0)
                {
                    ExitWithHelp(null);
                }

                int i = 0;
                while (i < args.Length)
                {
                    switch (args[i])
                    {
                    case _argPkcs11Library:
                        pkcs11Library = args[++i];
                        break;

                    case _argListTokens:
                        listTokens = 1;
                        break;

                    case _argListObjects:
                        listObjects = 1;
                        break;

                    case _argSign:
                        sign = 1;
                        break;

                    case _argTokenSerial:
                        tokenSerial = args[++i];
                        break;

                    case _argTokenLabel:
                        tokenLabel = args[++i];
                        break;

                    case _argPin:
                        pin = args[++i];
                        break;

                    case _argKeyLabel:
                        keyLabel = args[++i];
                        break;

                    case _argKeyId:
                        keyId = args[++i];
                        break;

                    case _argDataFile:
                        dataFile = args[++i];
                        break;

                    case _argSignatureFile:
                        signatureFile = args[++i];
                        break;

                    case _argHashAlg:
                        hashAlg = args[++i];
                        break;

                    case _argSignatureScheme:
                        signatureScheme = args[++i];
                        break;

                    case _argOutputFormat:
                        outputFormat = args[++i];
                        break;

                    case _argCertsDir:
                        certsDir = args[++i];
                        break;

                    default:
                        ExitWithHelp("Invalid argument: " + args[i]);
                        break;
                    }

                    i++;
                }

                // Validate operation modes
                if (listTokens + listObjects + sign != 1)
                {
                    ExitWithHelp(string.Format("Argument \"{0}\", \"{1}\" or \"{2}\" has to be specified", _argListTokens, _argListObjects, _argSign));
                }

                // Handle "--list-tokens" operation mode
                if (listTokens == 1)
                {
                    // Validate command line arguments
                    if (string.IsNullOrEmpty(pkcs11Library))
                    {
                        ExitWithHelp("Required argument: " + _argPkcs11Library);
                    }
                    if (!string.IsNullOrEmpty(tokenSerial))
                    {
                        ExitWithHelp("Unexpected argument: " + _argTokenSerial);
                    }
                    if (!string.IsNullOrEmpty(tokenLabel))
                    {
                        ExitWithHelp("Unexpected argument: " + _argTokenLabel);
                    }
                    if (!string.IsNullOrEmpty(pin))
                    {
                        ExitWithHelp("Unexpected argument: " + _argPin);
                    }
                    if (!string.IsNullOrEmpty(keyLabel))
                    {
                        ExitWithHelp("Unexpected argument: " + _argKeyLabel);
                    }
                    if (!string.IsNullOrEmpty(keyId))
                    {
                        ExitWithHelp("Unexpected argument: " + _argKeyId);
                    }
                    if (!string.IsNullOrEmpty(dataFile))
                    {
                        ExitWithHelp("Unexpected argument: " + _argDataFile);
                    }
                    if (!string.IsNullOrEmpty(signatureFile))
                    {
                        ExitWithHelp("Unexpected argument: " + _argSignatureFile);
                    }
                    if (!string.IsNullOrEmpty(hashAlg))
                    {
                        ExitWithHelp("Unexpected argument: " + _argHashAlg);
                    }
                    if (!string.IsNullOrEmpty(signatureScheme))
                    {
                        ExitWithHelp("Unexpected argument: " + _argSignatureScheme);
                    }
                    if (!string.IsNullOrEmpty(outputFormat))
                    {
                        ExitWithHelp("Unexpected argument: " + _argOutputFormat);
                    }
                    if (!string.IsNullOrEmpty(certsDir))
                    {
                        ExitWithHelp("Unexpected argument: " + _argCertsDir);
                    }

                    // Perform requested operation
                    using (Pkcs11Explorer pkcs11Explorer = new Pkcs11Explorer(pkcs11Library))
                    {
                        Console.WriteLine("Listing available tokens");

                        int          j      = 1;
                        List <Token> tokens = pkcs11Explorer.GetTokens();
                        foreach (Token token in tokens)
                        {
                            Console.WriteLine();
                            Console.WriteLine("Token no." + j);
                            Console.WriteLine("  Manufacturer:       " + token.ManufacturerId);
                            Console.WriteLine("  Model:              " + token.Model);
                            Console.WriteLine("  Serial number:      " + token.SerialNumber);
                            Console.WriteLine("  Label:              " + token.Label);
                            j++;
                        }
                    }
                }

                // Handle "--list-objects" operation mode
                if (listObjects == 1)
                {
                    // Validate command line arguments
                    if (string.IsNullOrEmpty(pkcs11Library))
                    {
                        ExitWithHelp("Required argument: " + _argPkcs11Library);
                    }
                    if (string.IsNullOrEmpty(tokenSerial) && string.IsNullOrEmpty(tokenLabel))
                    {
                        ExitWithHelp("Required argument: " + _argTokenSerial + " and/or " + _argTokenLabel);
                    }
                    if (string.IsNullOrEmpty(pin))
                    {
                        ExitWithHelp("Required argument: " + _argPin);
                    }
                    if (!string.IsNullOrEmpty(keyLabel))
                    {
                        ExitWithHelp("Unexpected argument: " + _argKeyLabel);
                    }
                    if (!string.IsNullOrEmpty(keyId))
                    {
                        ExitWithHelp("Unexpected argument: " + _argKeyId);
                    }
                    if (!string.IsNullOrEmpty(dataFile))
                    {
                        ExitWithHelp("Unexpected argument: " + _argDataFile);
                    }
                    if (!string.IsNullOrEmpty(signatureFile))
                    {
                        ExitWithHelp("Unexpected argument: " + _argSignatureFile);
                    }
                    if (!string.IsNullOrEmpty(hashAlg))
                    {
                        ExitWithHelp("Unexpected argument: " + _argHashAlg);
                    }
                    if (!string.IsNullOrEmpty(signatureScheme))
                    {
                        ExitWithHelp("Unexpected argument: " + _argSignatureScheme);
                    }
                    if (!string.IsNullOrEmpty(outputFormat))
                    {
                        ExitWithHelp("Unexpected argument: " + _argOutputFormat);
                    }
                    if (!string.IsNullOrEmpty(certsDir))
                    {
                        ExitWithHelp("Unexpected argument: " + _argCertsDir);
                    }

                    // Perform requested operation
                    using (Pkcs11Explorer pkcs11Explorer = new Pkcs11Explorer(pkcs11Library))
                    {
                        Console.WriteLine(string.Format("Listing objects available on token with serial \"{0}\" and label \"{1}\"", tokenSerial, tokenLabel));

                        // Find requested token
                        Token foundToken = null;

                        List <Token> tokens = pkcs11Explorer.GetTokens();
                        foreach (Token token in tokens)
                        {
                            if (!string.IsNullOrEmpty(tokenLabel))
                            {
                                if (0 != String.Compare(tokenLabel, token.Label, StringComparison.InvariantCultureIgnoreCase))
                                {
                                    continue;
                                }
                            }

                            if (!string.IsNullOrEmpty(tokenSerial))
                            {
                                if (0 != String.Compare(tokenSerial, token.SerialNumber, StringComparison.InvariantCultureIgnoreCase))
                                {
                                    continue;
                                }
                            }

                            foundToken = token;
                            break;
                        }

                        if (foundToken == null)
                        {
                            throw new TokenNotFoundException(string.Format("Token with serial \"{0}\" and label \"{1}\" was not found", tokenSerial, tokenLabel));
                        }

                        // Get private keys and certificates stored in requested token
                        List <PrivateKey>  privateKeys  = null;
                        List <Certificate> certificates = null;
                        pkcs11Explorer.GetTokenObjects(foundToken, true, pin, out privateKeys, out certificates);

                        // Print private keys
                        int j = 1;
                        foreach (PrivateKey privateKey in privateKeys)
                        {
                            Console.WriteLine();
                            Console.WriteLine("Private key no." + j);
                            Console.WriteLine("  ID (CKA_ID):        " + privateKey.Id);
                            Console.WriteLine("  Label (CKA_LABEL):  " + privateKey.Label);

                            // Print public part of RSA key
                            if ((privateKey.PublicKey != null) && (privateKey.PublicKey is RsaKeyParameters))
                            {
                                RsaKeyParameters rsa = privateKey.PublicKey as RsaKeyParameters;
                                Console.WriteLine("  RSA exponent:       " + ConvertUtils.BytesToHexString(rsa.Exponent.ToByteArrayUnsigned()));
                                Console.WriteLine("  RSA public modulus: " + ConvertUtils.BytesToHexString(rsa.Modulus.ToByteArrayUnsigned()));
                            }

                            j++;
                        }

                        // Print certificates
                        int k = 1;
                        foreach (Certificate certificate in certificates)
                        {
                            X509Certificate2 x509Cert = CertUtils.ToDotNetObject(certificate.Data);

                            Console.WriteLine();
                            Console.WriteLine("Certificate no." + k);
                            Console.WriteLine("  ID (CKA_ID):        " + certificate.Id);
                            Console.WriteLine("  Label (CKA_LABEL):  " + certificate.Label);
                            Console.WriteLine("  Serial number:      " + x509Cert.SerialNumber);
                            Console.WriteLine("  Subject DN:         " + x509Cert.Subject);
                            Console.WriteLine("  Issuer DN:          " + x509Cert.Issuer);
                            Console.WriteLine("  Not before:         " + x509Cert.NotBefore);
                            Console.WriteLine("  Not after:          " + x509Cert.NotAfter);

                            // Print certified public RSA key
                            if ((certificate.PublicKey != null) && (certificate.PublicKey is RsaKeyParameters))
                            {
                                RsaKeyParameters rsa = certificate.PublicKey as RsaKeyParameters;
                                Console.WriteLine("  RSA exponent:       " + ConvertUtils.BytesToHexString(rsa.Exponent.ToByteArrayUnsigned()));
                                Console.WriteLine("  RSA public modulus: " + ConvertUtils.BytesToHexString(rsa.Modulus.ToByteArrayUnsigned()));
                            }

                            k++;
                        }
                    }
                }

                // Handle "--sign" operation mode
                if (sign == 1)
                {
                    // Use SHA256 as default hashing algorithm
                    HashAlgorithm   hashAlgorithm = HashAlgorithm.SHA256;
                    SignatureScheme sigScheme     = SignatureScheme.RSASSA_PKCS1_v1_5;
                    OutputFormat    outFormat     = OutputFormat.CMS;

                    // Validate command line arguments (_argHashAlg and _argCertsDir are optional)
                    if (string.IsNullOrEmpty(pkcs11Library))
                    {
                        ExitWithHelp("Required argument: " + _argPkcs11Library);
                    }
                    if (string.IsNullOrEmpty(tokenSerial) && string.IsNullOrEmpty(tokenLabel))
                    {
                        ExitWithHelp("Required argument: " + _argTokenSerial + " and/or " + _argTokenLabel);
                    }
                    if (string.IsNullOrEmpty(pin))
                    {
                        ExitWithHelp("Required argument: " + _argPin);
                    }
                    if (string.IsNullOrEmpty(keyLabel) && string.IsNullOrEmpty(keyId))
                    {
                        ExitWithHelp("Required argument: " + _argKeyLabel + " and/or " + _argKeyId);
                    }
                    if (string.IsNullOrEmpty(dataFile))
                    {
                        ExitWithHelp("Required argument: " + _argDataFile);
                    }
                    if (string.IsNullOrEmpty(signatureFile))
                    {
                        ExitWithHelp("Required argument: " + _argSignatureFile);
                    }
                    if (!string.IsNullOrEmpty(hashAlg))
                    {
                        hashAlgorithm = (HashAlgorithm)Enum.Parse(typeof(HashAlgorithm), hashAlg);
                    }
                    if (!string.IsNullOrEmpty(signatureScheme))
                    {
                        sigScheme = (SignatureScheme)Enum.Parse(typeof(SignatureScheme), signatureScheme);
                    }
                    if (!string.IsNullOrEmpty(outputFormat))
                    {
                        outFormat = (OutputFormat)Enum.Parse(typeof(OutputFormat), outputFormat);
                    }

                    // Perform requested operation
                    using (Pkcs7SignatureGenerator pkcs7SignatureGenerator = new Pkcs7SignatureGenerator(pkcs11Library, tokenSerial, tokenLabel, pin, keyLabel, keyId, hashAlgorithm, sigScheme))
                    {
                        Console.WriteLine(string.Format("Signing file \"{0}\" using private key with ID \"{1}\" and label \"{2}\" stored on token with serial \"{3}\" and label \"{4}\"", dataFile, keyId, keyLabel, tokenSerial, tokenLabel));

                        // Read signing certificate from the token
                        byte[] signingCertificate = pkcs7SignatureGenerator.GetSigningCertificate();

                        // Read all certificates stored on the token
                        List <byte[]> otherCertificates = pkcs7SignatureGenerator.GetAllCertificates();

                        // Read additional certificates from directory
                        if (!string.IsNullOrEmpty(certsDir))
                        {
                            foreach (string file in Directory.GetFiles(certsDir))
                            {
                                otherCertificates.Add(File.ReadAllBytes(file));
                            }
                        }

                        // Build certification path for the signing certificate
                        ICollection <Org.BouncyCastle.X509.X509Certificate> certPath = CertUtils.BuildCertPath(signingCertificate, otherCertificates, true);

                        // Perform signing and signature encoding
                        if (outFormat == OutputFormat.CMS)
                        {
                            // Read data that should be signed
                            byte[] dataFileContent = File.ReadAllBytes(dataFile);

                            // Generate detached PKCS#7 signature
                            byte[] signature = pkcs7SignatureGenerator.GenerateSignature(dataFileContent, true, CertUtils.ToBouncyCastleObject(signingCertificate), certPath);

                            // Save signature to the file
                            File.WriteAllBytes(signatureFile, signature);
                        }
                        else if (outFormat == OutputFormat.SMIME)
                        {
                            MemoryStream dataStream      = null;
                            MemoryStream signatureStream = null;

                            try
                            {
                                // Construct MIME part for data
                                TextPart dataPart = new TextPart("plain");
                                dataPart.Text = File.ReadAllText(dataFile, Encoding.UTF8);

                                // Read data that should be signed
                                dataStream = new MemoryStream();
                                dataPart.WriteTo(dataStream);

                                // Generate detached PKCS#7 signature
                                byte[] signature = pkcs7SignatureGenerator.GenerateSignature(dataStream.ToArray(), true, CertUtils.ToBouncyCastleObject(signingCertificate), certPath);
                                signatureStream = new MemoryStream(signature);

                                // Construct MIME part for signature
                                MimePart signaturePart = new MimePart("application", "x-pkcs7-signature");
                                signaturePart.Content                 = new MimeContent(signatureStream, ContentEncoding.Binary);
                                signaturePart.ContentDisposition      = new ContentDisposition(ContentDisposition.Attachment);
                                signaturePart.ContentTransferEncoding = ContentEncoding.Base64;
                                signaturePart.FileName                = Path.GetFileName("smime.p7s");

                                // Construct multipart MIME message
                                var multipart = new Multipart("signed");
                                multipart.ContentType.Parameters["micalg"]   = HashAlgorithmUtils.GetHashMicalgName(hashAlgorithm);
                                multipart.ContentType.Parameters["protocol"] = "application/x-pkcs7-signature";
                                multipart.Add(dataPart);
                                multipart.Add(signaturePart);

                                // Save signature to the file
                                multipart.WriteTo(signatureFile);
                            }
                            finally
                            {
                                if (dataStream != null)
                                {
                                    dataStream.Dispose();
                                    dataStream = null;
                                }

                                if (signatureStream != null)
                                {
                                    signatureStream.Dispose();
                                    signatureStream = null;
                                }
                            }
                        }
                        else
                        {
                            throw new NotSupportedException(string.Format("Output format \"{0}\" is not supported", outputFormat));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(@"Operation error: " + ex.GetType() + " - " + ex.Message);
                Console.WriteLine(ex.StackTrace);
                Environment.Exit(_exitError);
            }

            Environment.Exit(_exitSuccess);
        }
        /// <summary>
        /// Main method specifying where program execution is to begin
        /// </summary>
        /// <param name="args">Command line arguments passed to the program</param>
        static void Main(string[] args)
        {
            try
            {
                // Parse command line arguments
                string pkcs11Library = null;
                int listTokens = 0;
                int listObjects = 0;
                int sign = 0;
                string tokenSerial = null;
                string tokenLabel = null;
                string pin = null;
                string keyLabel = null;
                string keyId = null;
                string dataFile = null;
                string signatureFile = null;
                string hashAlg = null;
                string certsDir = null;

                if (args.Length == 0)
                    ExitWithHelp(null);

                int i = 0;
                while (i < args.Length)
                {
                    switch (args[i])
                    {
                        case _argPkcs11Library:
                            pkcs11Library = args[++i];
                            break;
                        case _argListTokens:
                            listTokens = 1;
                            break;
                        case _argListObjects:
                            listObjects = 1;
                            break;
                        case _argSign:
                            sign = 1;
                            break;
                        case _argTokenSerial:
                            tokenSerial = args[++i];
                            break;
                        case _argTokenLabel:
                            tokenLabel = args[++i];
                            break;
                        case _argPin:
                            pin = args[++i];
                            break;
                        case _argKeyLabel:
                            keyLabel = args[++i];
                            break;
                        case _argKeyId:
                            keyId = args[++i];
                            break;
                        case _argDataFile:
                            dataFile = args[++i];
                            break;
                        case _argSignatureFile:
                            signatureFile = args[++i];
                            break;
                        case _argHashAlg:
                            hashAlg = args[++i];
                            break;
                        case _argCertsDir:
                            certsDir = args[++i];
                            break;
                        default:
                            ExitWithHelp("Invalid argument: " + args[i]);
                            break;
                    }

                    i++;
                }

                // Validate operation modes
                if (listTokens + listObjects + sign != 1)
                    ExitWithHelp(string.Format("Argument \"{0}\", \"{1}\" or \"{2}\" has to be specified", _argListTokens, _argListObjects, _argSign));

                // Handle "--list-tokens" operation mode
                if (listTokens == 1)
                {
                    // Validate command line arguments
                    if (string.IsNullOrEmpty(pkcs11Library))
                        ExitWithHelp("Required argument: " + _argPkcs11Library);
                    if (!string.IsNullOrEmpty(tokenSerial))
                        ExitWithHelp("Unexpected argument: " + _argTokenSerial);
                    if (!string.IsNullOrEmpty(tokenLabel))
                        ExitWithHelp("Unexpected argument: " + _argTokenLabel);
                    if (!string.IsNullOrEmpty(pin))
                        ExitWithHelp("Unexpected argument: " + _argPin);
                    if (!string.IsNullOrEmpty(keyLabel))
                        ExitWithHelp("Unexpected argument: " + _argKeyLabel);
                    if (!string.IsNullOrEmpty(keyId))
                        ExitWithHelp("Unexpected argument: " + _argKeyId);
                    if (!string.IsNullOrEmpty(dataFile))
                        ExitWithHelp("Unexpected argument: " + _argDataFile);
                    if (!string.IsNullOrEmpty(signatureFile))
                        ExitWithHelp("Unexpected argument: " + _argSignatureFile);
                    if (!string.IsNullOrEmpty(hashAlg))
                        ExitWithHelp("Unexpected argument: " + _argHashAlg);
                    if (!string.IsNullOrEmpty(certsDir))
                        ExitWithHelp("Unexpected argument: " + _argCertsDir);

                    // Perform requested operation
                    using (Pkcs11Explorer pkcs11Explorer = new Pkcs11Explorer(pkcs11Library))
                    {
                        Console.WriteLine("Listing available tokens");

                        int j = 1;
                        List<Token> tokens = pkcs11Explorer.GetTokens();
                        foreach (Token token in tokens)
                        {
                            Console.WriteLine();
                            Console.WriteLine("Token no." + j);
                            Console.WriteLine("  Manufacturer:       " + token.ManufacturerId);
                            Console.WriteLine("  Model:              " + token.Model);
                            Console.WriteLine("  Serial number:      " + token.SerialNumber);
                            Console.WriteLine("  Label:              " + token.Label);
                            j++;
                        }
                    }
                }

                // Handle "--list-objects" operation mode
                if (listObjects == 1)
                {
                    // Validate command line arguments
                    if (string.IsNullOrEmpty(pkcs11Library))
                        ExitWithHelp("Required argument: " + _argPkcs11Library);
                    if (string.IsNullOrEmpty(tokenSerial) && string.IsNullOrEmpty(tokenLabel))
                        ExitWithHelp("Required argument: " + _argTokenSerial + " and/or " + _argTokenLabel);
                    if (string.IsNullOrEmpty(pin))
                        ExitWithHelp("Required argument: " + _argPin);
                    if (!string.IsNullOrEmpty(keyLabel))
                        ExitWithHelp("Unexpected argument: " + _argKeyLabel);
                    if (!string.IsNullOrEmpty(keyId))
                        ExitWithHelp("Unexpected argument: " + _argKeyId);
                    if (!string.IsNullOrEmpty(dataFile))
                        ExitWithHelp("Unexpected argument: " + _argDataFile);
                    if (!string.IsNullOrEmpty(signatureFile))
                        ExitWithHelp("Unexpected argument: " + _argSignatureFile);
                    if (!string.IsNullOrEmpty(hashAlg))
                        ExitWithHelp("Unexpected argument: " + _argHashAlg);
                    if (!string.IsNullOrEmpty(certsDir))
                        ExitWithHelp("Unexpected argument: " + _argCertsDir);

                    // Perform requested operation
                    using (Pkcs11Explorer pkcs11Explorer = new Pkcs11Explorer(pkcs11Library))
                    {
                        Console.WriteLine(string.Format("Listing objects available on token with serial \"{0}\" and label \"{1}\"", tokenSerial, tokenLabel));

                        // Find requested token
                        Token foundToken = null;

                        List<Token> tokens = pkcs11Explorer.GetTokens();
                        foreach (Token token in tokens)
                        {
                            if (!string.IsNullOrEmpty(tokenLabel))
                                if (0 != String.Compare(tokenLabel, token.Label, StringComparison.InvariantCultureIgnoreCase))
                                    continue;

                            if (!string.IsNullOrEmpty(tokenSerial))
                                if (0 != String.Compare(tokenSerial, token.SerialNumber, StringComparison.InvariantCultureIgnoreCase))
                                    continue;

                            foundToken = token;
                            break;
                        }

                        if (foundToken == null)
                            throw new TokenNotFoundException(string.Format("Token with serial \"{0}\" and label \"{1}\" was not found", tokenSerial, tokenLabel));

                        // Get private keys and certificates stored in requested token
                        List<PrivateKey> privateKeys = null;
                        List<Certificate> certificates = null;
                        pkcs11Explorer.GetTokenObjects(foundToken, true, pin, out privateKeys, out certificates);

                        // Print private keys
                        int j = 1;
                        foreach (PrivateKey privateKey in privateKeys)
                        {
                            Console.WriteLine();
                            Console.WriteLine("Private key no." + j);
                            Console.WriteLine("  ID (CKA_ID):        " + privateKey.Id);
                            Console.WriteLine("  Label (CKA_LABEL):  " + privateKey.Label);

                            // Print public part of RSA key
                            if ((privateKey.PublicKey != null) && (privateKey.PublicKey is RsaKeyParameters))
                            {
                                RsaKeyParameters rsa = privateKey.PublicKey as RsaKeyParameters;
                                Console.WriteLine("  RSA exponent:       " + ConvertUtils.BytesToHexString(rsa.Exponent.ToByteArrayUnsigned()));
                                Console.WriteLine("  RSA public modulus: " + ConvertUtils.BytesToHexString(rsa.Modulus.ToByteArrayUnsigned()));
                            }

                            j++;
                        }

                        // Print certificates
                        int k = 1;
                        foreach (Certificate certificate in certificates)
                        {
                            X509Certificate2 x509Cert = CertUtils.ToDotNetObject(certificate.Data);

                            Console.WriteLine();
                            Console.WriteLine("Certificate no." + k);
                            Console.WriteLine("  ID (CKA_ID):        " + certificate.Id);
                            Console.WriteLine("  Label (CKA_LABEL):  " + certificate.Label);
                            Console.WriteLine("  Serial number:      " + x509Cert.SerialNumber);
                            Console.WriteLine("  Subject DN:         " + x509Cert.Subject);
                            Console.WriteLine("  Issuer DN:          " + x509Cert.Issuer);
                            Console.WriteLine("  Not before:         " + x509Cert.NotBefore);
                            Console.WriteLine("  Not after:          " + x509Cert.NotAfter);

                            // Print certified public RSA key
                            if ((certificate.PublicKey != null) && (certificate.PublicKey is RsaKeyParameters))
                            {
                                RsaKeyParameters rsa = certificate.PublicKey as RsaKeyParameters;
                                Console.WriteLine("  RSA exponent:       " + ConvertUtils.BytesToHexString(rsa.Exponent.ToByteArrayUnsigned()));
                                Console.WriteLine("  RSA public modulus: " + ConvertUtils.BytesToHexString(rsa.Modulus.ToByteArrayUnsigned()));
                            }

                            k++;
                        }
                    }
                }

                // Handle "--sign" operation mode
                if (sign == 1)
                {
                    // Use SHA256 as default hashing algorithm
                    HashAlgorithm hashAlgorithm = HashAlgorithm.SHA256;

                    // Validate command line arguments (_argHashAlg and _argCertsDir are optional)
                    if (string.IsNullOrEmpty(pkcs11Library))
                        ExitWithHelp("Required argument: " + _argPkcs11Library);
                    if (string.IsNullOrEmpty(tokenSerial) && string.IsNullOrEmpty(tokenLabel))
                        ExitWithHelp("Required argument: " + _argTokenSerial + " and/or " + _argTokenLabel);
                    if (string.IsNullOrEmpty(pin))
                        ExitWithHelp("Required argument: " + _argPin);
                    if (string.IsNullOrEmpty(keyLabel) && string.IsNullOrEmpty(keyId))
                        ExitWithHelp("Required argument: " + _argKeyLabel + " and/or " + _argKeyId);
                    if (string.IsNullOrEmpty(dataFile))
                        ExitWithHelp("Required argument: " + _argDataFile);
                    if (string.IsNullOrEmpty(signatureFile))
                        ExitWithHelp("Required argument: " + _argSignatureFile);
                    if (!string.IsNullOrEmpty(hashAlg))
                        hashAlgorithm = (HashAlgorithm)Enum.Parse(typeof(HashAlgorithm), hashAlg);

                    // Perform requested operation
                    using (Pkcs7SignatureGenerator pkcs7SignatureGenerator = new Pkcs7SignatureGenerator(pkcs11Library, tokenSerial, tokenLabel, pin, keyLabel, keyId, hashAlgorithm))
                    {
                        Console.WriteLine(string.Format("Signing file \"{0}\" using private key with ID \"{1}\" and label \"{2}\" stored on token with serial \"{3}\" and label \"{4}\"", dataFile, keyId, keyLabel, tokenSerial, tokenLabel));

                        // Read signing certificate from the token
                        byte[] signingCertificate = pkcs7SignatureGenerator.GetSigningCertificate();

                        // Read all certificates stored on the token
                        List<byte[]> otherCertificates = pkcs7SignatureGenerator.GetAllCertificates();

                        // Read additional certificates from directory
                        if (!string.IsNullOrEmpty(certsDir))
                            foreach (string file in Directory.GetFiles(certsDir))
                                otherCertificates.Add(File.ReadAllBytes(file));

                        // Build certification path for the signing certificate
                        ICollection<Org.BouncyCastle.X509.X509Certificate> certPath = CertUtils.BuildCertPath(signingCertificate, otherCertificates, true);

                        // Read data that should be signed
                        byte[] dataFileContent = File.ReadAllBytes(dataFile);

                        // Generate detached PKCS#7 signature
                        byte[] signature = pkcs7SignatureGenerator.GenerateSignature(dataFileContent, true, CertUtils.ToBouncyCastleObject(signingCertificate), certPath);

                        // Save signature to the file
                        File.WriteAllBytes(signatureFile, signature);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(@"Operation error: " + ex.GetType() + " - " + ex.Message);
                Console.WriteLine(ex.StackTrace);
                Environment.Exit(_exitError);
            }

            Environment.Exit(_exitSuccess);
        }