创建RSA-SHA1签名

时间:2018-03-03 02:15:30

标签: c# rsa digital-signature sha1 azure-keyvault

是否可以使用存储在Azure Key Vault中的X509证书创建RSA-SHA1签名? [不可否认证书]

不幸的是,我无法将哈希算法更改为SHA256或更安全的东西,我真的需要将证书存储在Azure Key Vault中作为密钥。

到目前为止,我已经尝试了

standard_init_linux.go:185: exec user process caused "no such file or directory"

签名长度似乎正确(256字节),但验证失败(在具有正确实现的签名验证实现的节点上)。

我也试图像这样手动实现签名算法(使用await kvClient.SignAsync(keyVaultUrl, "RSNULL", digest); // digest = 20byte SHA1 await kvClient.SignAsync(keyVaultUrl, "RSNULL", ans1Digest); // asn1Digest = 35byte SHA1 wrapped in ANS1 structure ):

  • 数据>>计算SHA1摘要
  • 在ASN.1结构中包装SHA1摘要(与SHA1对象标识符一起)
  • Pad ANS.1结构,因此摘要与证书的密钥长度匹配
  • 使用RSA1_5>加密填充的ANS1.structure;签名

我一定是做错了。不确定是否需要所有步骤。

如果有微软的人读到这个。即使它不被认为是安全的,你能实施SHA1签名吗?漂亮,请加糖在上面: - )

2 个答案:

答案 0 :(得分:4)

我的盲目拍摄“专业”意见是你没有正确构造PKCS#1 DigestInfo结构。使用SHA1和SHA256算法(不测试其他算法)后,控制台应用程序正常运行:

using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.KeyVault;
using Microsoft.IdentityModel.Clients.ActiveDirectory;

namespace AzureKeyVaultTestApp1
{
    static class Program
    {
        static HashAlgorithmName _hashAlg = HashAlgorithmName.SHA1;
        static string _clientId = "00000000-0000-0000-0000-000000000000";
        static string _clientSecret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
        static string _certId = "https://XXXXXXXX.vault.azure.net/certificates/TestCert1/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
        static string _keyId = "https://XXXXXXXX.vault.azure.net/keys/TestCert1/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

        static async Task<string> AuthenticationCallback(string authority, string resource, string scope)
        {
            var context = new AuthenticationContext(authority);
            var result = await context.AcquireTokenAsync(resource, new ClientCredential(_clientId, _clientSecret));
            return result.AccessToken;
        }

        static async Task Main(string[] args)
        {
            KeyVaultClient client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(AuthenticationCallback));

            // Compute digest of data
            byte[] dataToSign = Encoding.ASCII.GetBytes("Hello world!");
            byte[] hash = HashAlgorithm.Create(_hashAlg.Name).ComputeHash(dataToSign);

            // Construct DER encoded PKCS#1 DigestInfo structure defined in RFC 8017
            byte[] pkcs1DigestInfo = CreatePkcs1DigestInfo(hash, _hashAlg);

            // Sign digest with private key
            var keyOperationResult = await client.SignAsync(_keyId, "RSNULL", pkcs1DigestInfo).ConfigureAwait(false);
            byte[] signature = keyOperationResult.Result;

            // Get public key from certificate
            var certBundle = await client.GetCertificateAsync(_certId).ConfigureAwait(false);
            X509Certificate2 cert = new X509Certificate2(certBundle.Cer);
            RSA rsaPubKey = cert.GetRSAPublicKey();

            // Verify digest signature with public key
            if (!rsaPubKey.VerifyHash(hash, signature, _hashAlg, RSASignaturePadding.Pkcs1))
                throw new Exception("Invalid signature");
        }

        private static byte[] CreatePkcs1DigestInfo(byte[] hash, HashAlgorithmName hashAlgorithm)
        {
            if (hash == null || hash.Length == 0)
                throw new ArgumentNullException(nameof(hash));

            byte[] pkcs1DigestInfo = null;

            if (hashAlgorithm == HashAlgorithmName.MD5)
            {
                if (hash.Length != 16)
                    throw new ArgumentException("Invalid lenght of hash value");

                pkcs1DigestInfo = new byte[] { 0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
                Array.Copy(hash, 0, pkcs1DigestInfo, pkcs1DigestInfo.Length - hash.Length, hash.Length);
            }
            else if (hashAlgorithm == HashAlgorithmName.SHA1)
            {
                if (hash.Length != 20)
                    throw new ArgumentException("Invalid lenght of hash value");

                pkcs1DigestInfo = new byte[] { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
                Array.Copy(hash, 0, pkcs1DigestInfo, pkcs1DigestInfo.Length - hash.Length, hash.Length);
            }
            else if (hashAlgorithm == HashAlgorithmName.SHA256)
            {
                if (hash.Length != 32)
                    throw new ArgumentException("Invalid lenght of hash value");

                pkcs1DigestInfo = new byte[] { 0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
                Array.Copy(hash, 0, pkcs1DigestInfo, pkcs1DigestInfo.Length - hash.Length, hash.Length);
            }
            else if (hashAlgorithm == HashAlgorithmName.SHA384)
            {
                if (hash.Length != 48)
                    throw new ArgumentException("Invalid lenght of hash value");

                pkcs1DigestInfo = new byte[] { 0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
                Array.Copy(hash, 0, pkcs1DigestInfo, pkcs1DigestInfo.Length - hash.Length, hash.Length);
            }
            else if (hashAlgorithm == HashAlgorithmName.SHA512)
            {
                if (hash.Length != 64)
                    throw new ArgumentException("Invalid lenght of hash value");

                pkcs1DigestInfo = new byte[] { 0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
                Array.Copy(hash, 0, pkcs1DigestInfo, pkcs1DigestInfo.Length - hash.Length, hash.Length);
            }

            return pkcs1DigestInfo;
        }
    }
}

答案 1 :(得分:1)

2020年11月更新:

这里是示例的链接,描述了如何使用最新的Azure SDK客户端库执行此操作:

How to encrypt and decrypt a single block of plain text with an RSA key

请注意,它还描述了如何使用DefaultAzureCredential进行身份验证,它比以前的回调模式更容易使用。

还有一个迁移指南,比较了如何与旧客户端here进行迁移。