Create RSA-SHA1 signature

后端 未结 2 1758
粉色の甜心
粉色の甜心 2021-01-06 09:52

Is it possible to create a RSA-SHA1 signature with a X509-certificate stored in Azure Key Vault? [non-repudiation certificate]

Unfortunately, I can\'t change the has

相关标签:
2条回答
  • 2021-01-06 10:25

    My blindly shot "professional" opinion is that you are not constructring PKCS#1 DigestInfo structure correctly. Following console application is working fine for me with both SHA1 and SHA256 algorithms (didn't test the others):

    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;
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-06 10:40

    November 2020 Update:

    Here is a link to the sample describing how to do this using the latest Azure SDK client library:

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

    Note that it also describes use of the DefaultAzureCredential for authentication, which is much simpler to use than the previous callback pattern.

    There is also a migration guide comparing how this would be done with the older client here.

    0 讨论(0)
提交回复
热议问题