web-dev-qa-db-ja.com

.NETでRSAとSHA256を使用してファイルに署名するにはどうすればよいですか?

私のアプリケーションは一連のファイルを受け取り、それらに署名します。 (アセンブリに署名しようとはしていません。)秘密キーを取得する.p12ファイルがあります。

これは私が使用しようとしていたコードですが、System.Security.Cryptography.CryptographicException "Invalid algorithm specified."

X509Certificate pXCert = new X509Certificate2(@"keyStore.p12", "password");
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)pXCert.PrivateKey;
string id = CryptoConfig.MapNameToOID("SHA256");
return csp.SignData(File.ReadAllBytes(filePath), id);

これによると answer できません(RSACryptoServiceProviderはSHA-256をサポートしていません)が、Bouncy Castleのような別のライブラリを使用して可能になることを望んでいました。

私はこのようなものに慣れていないので、Bouncy Castleは非常に紛らわしいと感じています。私はJavaアプリをC#に移植していますが、同じタイプの暗号化を使用してファイルに署名する必要があるため、RSA + SHA256で動けなくなります。

Bouncy Castle、OpenSSL.NET、Security.Cryptography、または聞いたことのない別のサードパーティライブラリを使用してこれを行うにはどうすればよいですか? Javaで実行できる場合、C#で実行できると想定しています。

更新:

これは私がpoupouのanwserのリンクから得たものです

        X509Certificate2 cert = new X509Certificate2(KeyStoreFile, password");
        RSACryptoServiceProvider rsacsp = (RSACryptoServiceProvider)cert.PrivateKey;
        CspParameters cspParam = new CspParameters();
        cspParam.KeyContainerName = rsacsp.CspKeyContainerInfo.KeyContainerName;
        cspParam.KeyNumber = rsacsp.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2;
        RSACryptoServiceProvider aescsp = new RSACryptoServiceProvider(cspParam);
        aescsp.PersistKeyInCsp = false;
        byte[] signed = aescsp.SignData(File.ReadAllBytes(file), "SHA256");
        bool isValid = aescsp.VerifyData(File.ReadAllBytes(file), "SHA256", signed);

問題は、元のツールで得たのと同じ結果が得られないことです。コードを読んでわかる限り、実際の署名を行うCryptoServiceProviderはキーストアファイルのPrivateKeyを使用していません。あれは正しいですか?

43
scott

RSA + SHA256は機能します...

後の例mayは常に機能しない場合があります。名前ではなく、ハッシュアルゴリズムのOIDを使用する必要があります。最初の例のように、これは CryptoConfig.MapNameToOID(AlgorithmName) の呼び出しから取得されます。ここで、AlgorithmNameは提供するもの(つまり、「SHA256」)です。

最初に必要なのは、秘密鍵付きの証明書です。私は通常、公開鍵ファイルを使用してLocalMachineまたはCurrentUserストアから私のものを読み取ります(.cer)秘密鍵を識別し、証明書を列挙してハッシュで照合します...

X509Certificate2 publicCert = new X509Certificate2(@"C:\mycertificate.cer");

//Fetch private key from the local machine store
X509Certificate2 privateCert = null;
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
foreach( X509Certificate2 cert in store.Certificates)
{
    if (cert.GetCertHashString() == publicCert.GetCertHashString())
        privateCert = cert;
}

しかし、そこにたどり着いたら、秘密鍵付きの証明書を取得したら、それを再構築する必要があります。これは、証明書が秘密キーを作成する方法が原因で必要になる場合がありますが、その理由はよくわかりません。とにかく、最初にキーをエクスポートしてから、好きな中間形式を使用して再インポートすることでこれを行います。最も簡単なのはxmlです。

//Round-trip the key to XML and back, there might be a better way but this works
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
key.FromXmlString(privateCert.PrivateKey.ToXmlString(true));

これが完了したら、次のようにデータに署名できます。

//Create some data to sign
byte[] data = new byte[1024];

//Sign the data
byte[] sig = key.SignData(data, CryptoConfig.MapNameToOID("SHA256"));

最後に、秘密鍵で行ったように、再構築の必要なしに、証明書の公開鍵で直接検証を行うことができます。

key = (RSACryptoServiceProvider)publicCert.PublicKey.Key;
if (!key.VerifyData(data, CryptoConfig.MapNameToOID("SHA256"), sig))
    throw new CryptographicException();
55
csharptest.net

PrivateKey.toXMLString(true)またはprivateKey.exportParameters(true)の使用は、秘密鍵がエクスポート可能である必要があるため、安全な環境では使用できません。これは良い習慣ではありません。

より良い解決策は、「拡張」暗号プロバイダーを次のように明示的にロードすることです。

// Find my openssl-generated cert from the registry
var store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, "myapp.com", true);
var certificate = certificates[0];
store.Close();
// Note that this will return a Basic crypto provider, with only SHA-1 support
var privKey = (RSACryptoServiceProvider)certificate.PrivateKey;
// Force use of the Enhanced RSA and AES Cryptographic Provider with openssl-generated SHA256 keys
var enhCsp = new RSACryptoServiceProvider().CspKeyContainerInfo;
var cspparams = new CspParameters(enhCsp.ProviderType, enhCsp.ProviderName, privKey.CspKeyContainerInfo.KeyContainerName);
privKey = new RSACryptoServiceProvider(cspparams);
19
BKibler

キーファイルを変更して適切なCrypto Service Provider を指定し、.NETの問題を完全に回避することに決めました。

そのため、PEMプライベートキーとCRT公開証明書からPFXファイルを作成する場合、次のように実行します。

openssl pkcs12 -export -aes256 -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider" -inkey priv.pem -in pub.crt -out priv.pfx

重要な部分は-CSP "Microsoft Enhanced RSA and AES Cryptographic Provider" です。

-inkeyは秘密鍵ファイルを指定し、-inは、組み込む公開証明書を指定します。

手元にあるファイル形式のためにこれを微調整する必要があるかもしれません。このページのコマンドラインの例は、次の場合に役立ちます。 https://www.sslshopper.com/ssl-converter.html

私はここでこのソリューションを見つけました: http://hintdesk.com/c-how-to-fix-invalid-algorithm-specified-when-signing-with-sha256/

9
Timo

これは私がその問題にどのように対処したかです:

 X509Certificate2 privateCert = new X509Certificate2("certificate.pfx", password, X509KeyStorageFlags.Exportable);

 // This instance can not sign and verify with SHA256:
 RSACryptoServiceProvider privateKey = (RSACryptoServiceProvider)privateCert.PrivateKey;

 // This one can:
 RSACryptoServiceProvider privateKey1 = new RSACryptoServiceProvider();
 privateKey1.ImportParameters(privateKey.ExportParameters(true));

 byte[] data = Encoding.UTF8.GetBytes("Data to be signed"); 

 byte[] signature = privateKey1.SignData(data, "SHA256");

 bool isValid = privateKey1.VerifyData(data, "SHA256", signature);
7
user3658415

証明書を使用してRSACryptoServiceProviderを取得する場合、基盤となるCryptoAPIプロバイダーが何であるかは本当に重要です。デフォルトでは、「makecert」で証明書を作成すると、「RSA-FULL」になり、署名用のSHA1ハッシュのみがサポートされます。 SHA2をサポートする新しい「RSA-AES」が必要です。

そのため、追加オプション-sp "Microsoft Enhanced RSA and AES Cryptographic Provider"(または同等の-sy 24)を使用して証明書を作成すると、キージャグリングなどがなくてもコードが機能します。

5
Kastorskij

証明書を(Microsoft Enhanced RSAおよびAES暗号化プロバイダーに)変更せずに文字列に署名する方法を次に示します。

        byte[] certificate = File.ReadAllBytes(@"C:\Users\AwesomeUser\Desktop\Test\ServerCertificate.pfx");
        X509Certificate2 cert2 = new X509Certificate2(certificate, string.Empty, X509KeyStorageFlags.Exportable);
        string stringToBeSigned = "This is a string to be signed";
        SHA256Managed shHash = new SHA256Managed();
        byte[] computedHash = shHash.ComputeHash(Encoding.Default.GetBytes(stringToBeSigned));


        var certifiedRSACryptoServiceProvider = cert2.PrivateKey as RSACryptoServiceProvider;
        RSACryptoServiceProvider defaultRSACryptoServiceProvider = new RSACryptoServiceProvider();
        defaultRSACryptoServiceProvider.ImportParameters(certifiedRSACryptoServiceProvider.ExportParameters(true));
        byte[] signedHashValue = defaultRSACryptoServiceProvider.SignData(computedHash, "SHA256");
        string signature = Convert.ToBase64String(signedHashValue);
        Console.WriteLine("Signature : {0}", signature);

        RSACryptoServiceProvider publicCertifiedRSACryptoServiceProvider = cert2.PublicKey.Key as RSACryptoServiceProvider;
        bool verify = publicCertifiedRSACryptoServiceProvider.VerifyData(computedHash, "SHA256", signedHashValue);
        Console.WriteLine("Verification result : {0}", verify);
4
AdmiralThrawn

これによると blog it should FX 3.5で動作します(下記の注を参照)。ただし、.NET暗号のほとんどはCryptoAPIに基づいていることを思い出してください([〜#〜] cng [〜#〜]は最近のFXリリースでますます公開されています。

keyポイントは、CryptoAPIアルゴリズムのサポートはCrypto Service Provider(CSP)に依存し、少し異なるWindowsバージョン間(つまり、Windows 7で動作しているものがWindows 2000で動作しない場合があります)。

(ブログエントリから)コメントを読んで、RSACCryptoServiceProviderを作成するときに、(デフォルトの代わりに)AES CSPを指定するpossible回避策を参照してください。インスタンス。それは一部の人々、YMMVでうまくいくようです。

注:リリースされたすべての.NETフレームワークにはCryptoAPIで使用できないSHA256のmanaged実装が含まれているため、これは多くの人々を混乱させます。 FWIW Mono はそのような問題に悩まされません;-)

3
poupou

最近のフレームワークで使用できます。

    public byte[] GetSignature(byte[] inputData)
    {
        using (var rsa = this.signingCertificate.GetRSAPrivateKey())
        {
            return rsa.SignData(inputData, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        }
    }

    public bool ValidateSignature(byte[] inputData, byte[] signature)
    {
        using (var rsa = this.signingCertificate.GetRSAPublicKey())
        {
            return rsa.VerifyData(inputData, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        }
    }

上記のsigningCertificateX509Certificate2秘密鍵付き。この方法では、既存のキーをインポートする必要はなく、安全な環境で機能します。

1
Vinay Chandra

使用している証明書がユーザー/コンピューターの証明書ストアにない場合、間違った秘密キーが使用されている(またはフラットアウトエラーでしたか?私は覚えていません)という.NETで同様の問題に気付きました。それを保存された場所にインストールすると、私のシナリオの問題が修正され、物事は期待通りに動作し始めました-おそらくあなたはそれを試すことができます。

0
Sander