Example #1
0
        /// <summary>
        ///     Sign a file with a MinisignPrivateKey.
        /// </summary>
        /// <param name="fileToSign">The full path to the file.</param>
        /// <param name="minisignPrivateKey">A valid MinisignPrivateKey to sign.</param>
        /// <param name="untrustedComment">An optional untrusted comment.</param>
        /// <param name="trustedComment">An optional trusted comment.</param>
        /// <param name="outputFolder">The folder to write the signature (optional).</param>
        /// <returns>The full path to the signed file.</returns>
        /// <exception cref="FileNotFoundException"></exception>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="OverflowException"></exception>
        /// <exception cref="DirectoryNotFoundException"></exception>
        /// <exception cref="IOException"></exception>
        /// <exception cref="UnauthorizedAccessException"></exception>
        /// <exception cref="SecurityException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="PathTooLongException"></exception>
        /// <exception cref="NotSupportedException"></exception>
        public static string Sign(string fileToSign, MinisignPrivateKey minisignPrivateKey, string untrustedComment = "",
			string trustedComment = "", string outputFolder = "")
        {
            if (fileToSign != null && !File.Exists(fileToSign))
            {
                throw new FileNotFoundException("could not find fileToSign");
            }

            if (minisignPrivateKey == null)
                throw new ArgumentException("missing minisignPrivateKey input", "minisignPrivateKey");

            if (string.IsNullOrEmpty(untrustedComment))
            {
                untrustedComment = DefaultComment;
            }

            if (string.IsNullOrEmpty(trustedComment))
            {
                var timestamp = GetTimestamp();
                var filename = Path.GetFileName(fileToSign);
                trustedComment = "timestamp: " + timestamp + " file: " + filename;
            }

            if ((CommentPrefix + untrustedComment).Length > CommentMaxBytes)
            {
                throw new ArgumentOutOfRangeException("untrustedComment", "untrustedComment too long");
            }

            if ((TrustedCommentPrefix + trustedComment).Length > TrustedCommentMaxBytes)
            {
                throw new ArgumentOutOfRangeException("trustedComment", "trustedComment too long");
            }

            if (string.IsNullOrEmpty(outputFolder))
            {
                outputFolder = Path.GetDirectoryName(fileToSign);
            }

            //validate the outputFolder
            if (string.IsNullOrEmpty(outputFolder) || !Directory.Exists(outputFolder))
            {
                throw new DirectoryNotFoundException("outputFolder must exist");
            }

            if (outputFolder.IndexOfAny(Path.GetInvalidPathChars()) > -1)
                throw new ArgumentException("The given path to the output folder contains invalid characters!");

            var file = LoadMessageFile(fileToSign);

            var minisignSignature = new MinisignSignature();
            minisignSignature.KeyId = minisignPrivateKey.KeyId;
            minisignSignature.SignatureAlgorithm = Encoding.UTF8.GetBytes(Sigalg);
            var signature = PublicKeyAuth.SignDetached(file, minisignPrivateKey.SecretKey);
            minisignSignature.Signature = signature;

            var binarySignature = ArrayHelpers.ConcatArrays(
                minisignSignature.SignatureAlgorithm,
                minisignSignature.KeyId,
                minisignSignature.Signature
                );

            // sign the signature and the trusted comment with a global signature
            var globalSignature =
                PublicKeyAuth.SignDetached(
                    ArrayHelpers.ConcatArrays(minisignSignature.Signature, Encoding.UTF8.GetBytes(trustedComment)),
                    minisignPrivateKey.SecretKey);

            // prepare the file lines
            var signatureFileContent = new[]
            {
                CommentPrefix + untrustedComment,
                Convert.ToBase64String(binarySignature),
                TrustedCommentPrefix + trustedComment,
                Convert.ToBase64String(globalSignature)
            };

            var outputFile = fileToSign + SigSuffix;
            File.WriteAllLines(outputFile, signatureFileContent);
            return outputFile;
        }
Example #2
0
        /// <summary>
        ///     Generate a new Minisign key pair.
        /// </summary>
        /// <param name="password">The password to protect the secret key.</param>
        /// <param name="writeOutputFiles">If false, no files will be written.</param>
        /// <param name="outputFolder">The folder to write the files (optional).</param>
        /// <param name="keyPairFileName">The name of the files to write (optional).</param>
        /// <returns>A MinisignKeyPair object.</returns>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="OverflowException"></exception>
        /// <exception cref="DirectoryNotFoundException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="IOException"></exception>
        /// <exception cref="UnauthorizedAccessException"></exception>
        /// <exception cref="PathTooLongException"></exception>
        /// <exception cref="SecurityException"></exception>
        /// <exception cref="NotSupportedException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static MinisignKeyPair GenerateKeyPair(string password, bool writeOutputFiles = false,
			string outputFolder = "", string keyPairFileName = "minisign")
        {
            if (string.IsNullOrEmpty(password))
            {
                throw new ArgumentNullException("password", "password can not be null");
            }

            if (writeOutputFiles)
            {
                //validate the outputFolder
                if (string.IsNullOrEmpty(outputFolder) || !Directory.Exists(outputFolder))
                {
                    throw new DirectoryNotFoundException("outputFolder must exist");
                }

                if (outputFolder.IndexOfAny(Path.GetInvalidPathChars()) > -1)
                    throw new ArgumentException("The given path to the output folder contains invalid characters!");

                //validate the keyPairFileName
                if (string.IsNullOrEmpty(keyPairFileName))
                {
                    throw new ArgumentNullException("keyPairFileName", "keyPairFileName can not be empty");
                }
            }

            var minisignKeyPair = new MinisignKeyPair();
            var minisignPrivateKey = new MinisignPrivateKey();
            var keyPair = PublicKeyAuth.GenerateKeyPair();
            var keyId = SodiumCore.GetRandomBytes(KeyNumBytes);
            var kdfSalt = SodiumCore.GetRandomBytes(32);

            minisignPrivateKey.PublicKey = keyPair.PublicKey;
            minisignPrivateKey.KdfSalt = kdfSalt;
            minisignPrivateKey.SignatureAlgorithm = Encoding.UTF8.GetBytes(Sigalg);
            minisignPrivateKey.ChecksumAlgorithm = Encoding.UTF8.GetBytes(Chkalg);
            minisignPrivateKey.KdfAlgorithm = Encoding.UTF8.GetBytes(Kdfalg);
            minisignPrivateKey.KdfMemLimit = 1073741824; //currently unused
            minisignPrivateKey.KdfOpsLimit = 33554432; //currently unused

            var checksum =
                GenericHash.Hash(
                    ArrayHelpers.ConcatArrays(minisignPrivateKey.SignatureAlgorithm, keyId, keyPair.PrivateKey), null,
                    32);
            minisignPrivateKey.KeyId = keyId;
            minisignPrivateKey.SecretKey = keyPair.PrivateKey;
            minisignPrivateKey.Checksum = checksum;

            var dataToProtect = ArrayHelpers.ConcatArrays(keyId, keyPair.PrivateKey, checksum);
            var encryptionKey = PasswordHash.ScryptHashBinary(Encoding.UTF8.GetBytes(password),
                minisignPrivateKey.KdfSalt,
                PasswordHash.Strength.Sensitive,
                104);

            var encryptedKeyData = EncryptionHelpers.Xor(dataToProtect, encryptionKey);
            // set up the public key
            var minisignPublicKey = new MinisignPublicKey();
            minisignPublicKey.KeyId = keyId;
            minisignPublicKey.PublicKey = keyPair.PublicKey;
            minisignPublicKey.SignatureAlgorithm = Encoding.UTF8.GetBytes(Sigalg);
            keyPair.Dispose();
            if (writeOutputFiles)
            {
                var privateKeyOutputFileName = Path.Combine(outputFolder, keyPairFileName + PrivateKeyFileSuffix);
                var publicKeyOutputFileName = Path.Combine(outputFolder, keyPairFileName + PublicKeyFileSuffix);

                var binaryPublicKey = ArrayHelpers.ConcatArrays(
                    minisignPublicKey.SignatureAlgorithm,
                    minisignPublicKey.KeyId,
                    minisignPublicKey.PublicKey
                    );

                var publicFileContent = new[]
                {
                    CommentPrefix + "minisign public key " +
                    Utilities.BinaryToHex(minisignPublicKey.KeyId, Utilities.HexFormat.None, Utilities.HexCase.Upper),
                    Convert.ToBase64String(binaryPublicKey)
                };

                var binaryPrivateKey = ArrayHelpers.ConcatArrays(
                    minisignPrivateKey.SignatureAlgorithm,
                    minisignPrivateKey.KdfAlgorithm,
                    minisignPrivateKey.ChecksumAlgorithm,
                    minisignPrivateKey.KdfSalt,
                    BitConverter.GetBytes(minisignPrivateKey.KdfOpsLimit),
                    BitConverter.GetBytes(minisignPrivateKey.KdfMemLimit),
                    encryptedKeyData
                    );

                var privateFileContent = new[]
                {
                    CommentPrefix + PrivateKeyDefaultComment,
                    Convert.ToBase64String(binaryPrivateKey)
                };
                // files will be overwritten!
                File.WriteAllLines(publicKeyOutputFileName, publicFileContent);
                File.WriteAllLines(privateKeyOutputFileName, privateFileContent);

                minisignKeyPair.MinisignPublicKeyFilePath = publicKeyOutputFileName;
                minisignKeyPair.MinisignPrivateKeyFilePath = privateKeyOutputFileName;
            }

            minisignKeyPair.MinisignPublicKey = minisignPublicKey;
            minisignKeyPair.MinisignPrivateKey = minisignPrivateKey;
            return minisignKeyPair;
        }
Example #3
0
        /// <summary>
        ///     Load a public key into a MinisignPublicKey object.
        /// </summary>
        /// <param name="privateKey">A valid private key.</param>
        /// <param name="password">The password to decrypt the private key.</param>
        /// <returns>A MinisignPrivateKey object.</returns>
        /// <exception cref="OverflowException"></exception>
        /// <exception cref="CorruptPrivateKeyException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        public static MinisignPrivateKey LoadPrivateKey(byte[] privateKey, byte[] password)
        {
            if (privateKey == null)
                throw new ArgumentException("missing privateKey input", "privateKey");

            if (password == null)
                throw new ArgumentException("missing password input", "password");

            var minisignPrivateKey = new MinisignPrivateKey
            {
                SignatureAlgorithm = ArrayHelpers.SubArray(privateKey, 0, 2),
                KdfAlgorithm = ArrayHelpers.SubArray(privateKey, 2, 2),
                ChecksumAlgorithm = ArrayHelpers.SubArray(privateKey, 4, 2),
                KdfSalt = ArrayHelpers.SubArray(privateKey, 6, 32),
                KdfOpsLimit = BitConverter.ToInt64(ArrayHelpers.SubArray(privateKey, 38, 8), 0), //currently unused
                KdfMemLimit = BitConverter.ToInt64(ArrayHelpers.SubArray(privateKey, 46, 8), 0) //currently unused
            };

            if (!minisignPrivateKey.SignatureAlgorithm.SequenceEqual(Encoding.UTF8.GetBytes(Sigalg)))
            {
                throw new CorruptPrivateKeyException("bad SignatureAlgorithm");
            }

            if (!minisignPrivateKey.ChecksumAlgorithm.SequenceEqual(Encoding.UTF8.GetBytes(Chkalg)))
            {
                throw new CorruptPrivateKeyException("bad ChecksumAlgorithm");
            }

            if (!minisignPrivateKey.KdfAlgorithm.SequenceEqual(Encoding.UTF8.GetBytes(Kdfalg)))
            {
                throw new CorruptPrivateKeyException("bad KdfAlgorithm");
            }

            if (minisignPrivateKey.KdfSalt.Length != KeySaltBytes)
            {
                throw new CorruptPrivateKeyException("bad KdfSalt length");
            }

            var encryptedKeyData = ArrayHelpers.SubArray(privateKey, 54, 104);

            var decryptionKey = PasswordHash.ScryptHashBinary(password, minisignPrivateKey.KdfSalt,
                PasswordHash.Strength.Sensitive,
                104);

            var decryptedKeyData = EncryptionHelpers.Xor(encryptedKeyData, decryptionKey);
            minisignPrivateKey.KeyId = ArrayHelpers.SubArray(decryptedKeyData, 0, 8);
            minisignPrivateKey.SecretKey = ArrayHelpers.SubArray(decryptedKeyData, 8, 64);
            minisignPrivateKey.Checksum = ArrayHelpers.SubArray(decryptedKeyData, 72, 32);

            if (minisignPrivateKey.KeyId.Length != KeyNumBytes)
            {
                throw new CorruptPrivateKeyException("bad KeyId length");
            }

            var calculatedChecksum =
                GenericHash.Hash(
                    ArrayHelpers.ConcatArrays(minisignPrivateKey.SignatureAlgorithm, minisignPrivateKey.KeyId,
                        minisignPrivateKey.SecretKey), null, 32);

            if (!ArrayHelpers.ConstantTimeEquals(minisignPrivateKey.Checksum, calculatedChecksum))
            {
                throw new CorruptPrivateKeyException("bad private key checksum");
            }
            // extract the public key from the private key
            minisignPrivateKey.PublicKey =
                PublicKeyAuth.ExtractEd25519PublicKeyFromEd25519SecretKey(minisignPrivateKey.SecretKey);

            return minisignPrivateKey;
        }