简体   繁体   English

创建 RSA-SHA1 签名

[英]Create RSA-SHA1 signature

Is it possible to create a RSA-SHA1 signature with a X509-certificate stored in Azure Key Vault?是否可以使用存储在 Azure Key Vault 中的 X509 证书创建 RSA-SHA1 签名? [non-repudiation certificate] [不可否认证书]

Unfortunately, I can't change the hash-algorithm to SHA256 or something safer, and I really need to store the certficate in Azure Key Vault as Key.不幸的是,我无法将哈希算法更改为 SHA256 或更安全的算法,我确实需要将证书作为密钥存储在 Azure Key Vault 中。

So far I've tried到目前为止我试过了

await kvClient.SignAsync(keyVaultUrl, "RSNULL", digest); // digest = 20byte SHA1
await kvClient.SignAsync(keyVaultUrl, "RSNULL", ans1Digest); // asn1Digest = 35byte SHA1 wrapped in ANS1 structure

The signature length seems to be correct (256 bytes), but verification fails (on a node with a correctly implemented signature-verification implementation).签名长度似乎是正确的(256 字节),但验证失败(在具有正确实现的签名验证实现的节点上)。

I've also tried to implement the signature-algorithm manually like this (using keyVault.EncryptAsync ):我也试过像这样手动实现签名算法(使用keyVault.EncryptAsync ):

  • Data >> Calculate SHA1 digest数据 >> 计算 SHA1 摘要
  • Wrap SHA1 digest in ASN.1 structure (together with SHA1 object identifier)将 SHA1 摘要包装在 ASN.1 结构中(连同 SHA1 对象标识符)
  • Pad ANS.1 structure so digest matches the key-length of the certificate填充 ANS.1 结构,使摘要匹配证书的密钥长度
  • Encrypt the padded ANS1.structure using RSA1_5 >> Signature使用 RSA1_5 >> 签名加密填充的 ANS1.structure

I must be doing something wrong.我一定做错了什么。 Not sure if all steps are needed.不确定是否需要所有步骤。

If anyone from Microsoft reads this.如果 Microsoft 的任何人阅读此内容。 Can you implement SHA1 signing even if it isn't considered safe?即使 SHA1 签名被认为不安全,您能否实施它? Pretty, please with sugar on top:-)漂亮,请在上面加糖:-)

My blindly shot "professional" opinion is that you are not constructring PKCS#1 DigestInfo structure correctly.我盲目的“专业”意见是您没有正确构造 PKCS#1 DigestInfo 结构。 Following console application is working fine for me with both SHA1 and SHA256 algorithms (didn't test the others):以下控制台应用程序对 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;
        }
    }
}

November 2020 Update: 2020 年 11 月更新:

Here is a link to the sample describing how to do this using the latest Azure SDK client library:以下是描述如何使用最新的 Azure SDK 客户端库执行此操作的示例的链接:

How to encrypt and decrypt a single block of plain text with an RSA key 如何使用 RSA 密钥加密和解密单个纯文本块

Note that it also describes use of the DefaultAzureCredential for authentication, which is much simpler to use than the previous callback pattern.请注意,它还描述了使用DefaultAzureCredential进行身份验证,这比以前的回调模式更易于使用。

There is also a migration guide comparing how this would be done with the older client here .这里还有一个迁移指南,比较了如何使用旧客户端完成操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM