web-dev-qa-db-ja.com

証明書によって署名されたストアドプロシージャのSSDTスキーマ比較

Visual Studio Professional2015でSSDT14.0.50730.0を使用しています。2つのインスタンス間でデータベースを比較していますが、署名に関して一致しないストアドプロシージャがあることがわかりました。

どちらの場合も、証明書は同じスクリプトから各インスタンスで作成され、署名も各インスタンスで同じスクリプトを使用してパスワードを使用してCERTIFICATEによって追加されました。

ただし、スキーマ比較では、ソースとターゲットの両方で、署名値が一致しないSIGNATUREを持つCERTIFICATEがあるものとしてプロシージャが表示されます。生成された公開スクリプトは、SIGNATURE BY CERTIFICATEを削除し、プロシージャを変更してから、SIGNATURE BYCERTIFICATEを追加します。署名付き。シグニチャ値は、ソースインスタンスに表示された値と一致します。ただし、スクリプトは「公開鍵の署名が無効です」というメッセージで失敗します。

これらの手順を同期させるにはどうすればよいですか?

2
Mark Freeman

証明書の作成とモジュールの署名に関する詳細に入る前に、少なくとも2つのインスタンスが同じ証明書を持っているかどうかを確認できます。両方のインスタンスで以下を実行します。

_SELECT [thumbprint], [cert_serial_number], [sid],
       [issuer_name], [subject],
       [expiry_date], [start_date]
FROM   sys.certificates
WHERE  [name] = N'cert_name';
_

証明書が同じである場合、それらのフィールドはすべて、両方のインスタンス(または同じインスタンス上の両方のデータベース)で同じ値になります。ただし、これは証明書のSHA-1ハッシュであるため、主にthumbprintフィールドを確認する必要があります。その値が2つの場所で異なる場合は、同じ証明書を扱っていません。


あなたはそれを言う

証明書は、同じスクリプトから各インスタンスで作成されました

ただし、証明書が作成されている方法方法については正確に言及しないでください。これらは、次の構文を使用してSQL Serverで生成できます(これは「自己署名」と呼ばれます)。

_CREATE CERTIFICATE [cert_name]
  ENCRYPTION BY PASSWORD = 'certpassword' -- this is optional but best to use
  WITH SUBJECT = 'cert_subject';
_

このメソッドは、(実行されるたびに)新しい秘密鍵を生成し、_ENCRYPTION BY PASSWORD_句で指定されたパスワードで暗号化します。それ以外の場合は、データベースマスター鍵で暗号化します(そのため、パスワードを指定することをお勧めします)。

または、既存のアセンブリ、ファイル、またはバイナリリテラルを提供して作成できます。

_CREATE CERTIFICATE [cert_name]
FROM
   Assembly assembly_name
 | EXECUTABLE FILE = 'path_to_DLL'
 | FILE = 'path_to_file'
   WITH PRIVATE KEY ( FILE = 'path_to_private_key' )
 | BINARY = 0x......
   WITH PRIVATE KEY ( BINARY = 0x..... )
_

FILEおよびBINARYメソッドでは、オプションで秘密鍵ファイルまたはバイナリリテラルを指定できます。したがって、秘密鍵を指定するとともにこれらの方法の1つを使用する場合、または署名されたAssemblyを使用する場合、この方法で証明書を作成すると、毎回同じ秘密鍵が提供されます。

では、どのようにしてFILEまたはBINARYの値を取得しますか?

  • ファイル:

    最初の証明書を作成したら、以下を使用してそれをエクスポートします(秘密鍵を含む!)。

    _BACKUP CERTIFICATE [cert_name]
      TO FILE = 'path\to\cert_file.cer' 
      WITH PRIVATE KEY
      (
        DECRYPTION BY PASSWORD = 'cert_password',
        FILE = 'path\to\key_file.pvk',
        ENCRYPTION BY PASSWORD = 'file_password'
      );
    _

    そして新しい場所で:

    _CREATE CERTIFICATE [cert_name]
      FROM FILE = 'path\to\cert_file.cer'
      WITH PRIVATE KEY
      (
        FILE = 'path\to\key_file.pvk',
        DECRYPTION BY PASSWORD = 'file_password',
        ENCRYPTION BY PASSWORD = 'cert_password'
      );
    _
  • BINARY:(SQL Server 2012以降でのみ使用可能)

    最初の証明書を作成したら、以下を使用して証明書と秘密鍵の値を取得します。

    _SELECT CERTENCODED(CERT_ID('cert_name')) AS [CertificateBinary],
           CERTPRIVATEKEY(
                          CERT_ID('cert_name'),
                          'temp_password',
                          'cert_password'
                         ) AS [PrivateKeyBinary];
    _

    そして新しい場所で:

    _CREATE CERTIFICATE [cert_name]
      FROM BINARY = 0x{CertificateBinary}
      WITH PRIVATE KEY
      (
        BINARY = 0x{PrivateKeyBinary},
        DECRYPTION BY PASSWORD = 'temp_password',
        ENCRYPTION BY PASSWORD = 'cert_password'
      );
    _

SQL Server 2012以降を使用している場合は、BINARYメソッドが完全に自己完結型であるため(つまり、外部ファイルに依存しないため)、最も簡単です。

注意:

  • 上記のどの方法を選択したかに関係なく、最初の証明書の作成は1回だけ行う必要があり、バックアップファイルまたはバイナリリテラルをSSDTプロジェクトに組み込むことができるように、おそらく手動で行う必要があります。
  • ターゲットデータベースで証明書を作成するために秘密鍵が使用されている場合は、秘密鍵が削除された場合、またはで使用されなかった場合にのみ使用されるため、_WITH SIGNATURE_の_ADD SIGNATURE_句は必要ありません。証明書の作成。
    この場合(つまり、_sys.certificates_のすべてのフィールドが一致しおよび_CREATE CERTIFICATE_の秘密鍵を使用)、モジュールに署名しますまったく同じ定義を持つ2つの異なる場所(バイトごとに同じ)は、同じ署名を生成します
  • 秘密鍵が証明書の作成に使用されていない場合、または削除されている場合(はい、そうすることができます)、_WITH SIGNATURE_句を指定する必要がありますそして、少なくともある時点で秘密鍵を持っていた場所から、同じ秘密鍵と同じモジュール定義によって生成された署名のバイナリリテラルを渡します。これは、署名を提供するモジュールにのみ署名できるため、より安全な設定です。証明書に秘密鍵がない場合でも、署名されたモジュールの有効性を確認するために使用できますが、新しいモジュールまたは変更されたモジュールに署名するために使用することはできません(これはちょっとクールです)。

[〜#〜] but [〜#〜]、次の場合:

  • 両方の場所の証明書は同一であり、
  • スキーマ比較は、モジュール定義に異なるフラグを立てませんでした。
  • _ADD SIGNATURE_は、_WITH SIGNATURE_句を使用して宛先データベースで使用されています。
  • 使用されているバイナリリテラルは、次の方法でソースデータベースにあるものと同じです。

    _SELECT OBJECT_NAME(major_id), crypt_property
    FROM   sys.crypt_properties;
    _

次に、両方の場所でそのモジュールの_OBJECT_DEFINITION_を確認する必要があります。それらは、ケーシングなどを含めて同一である必要があります。念のためにSELECT CONVERT(VARBINARY(MAX), OBJECT_DEFINITION(OBJECT_ID(N'{module_name}')))を実行できます。スキーマの比較で、コメントの違いや、定義の先頭にあるCREATEキーワードの大文字と小文字の違いや空白などが無視されている可能性があります。


詳細については、次のMSDNページを参照してください。

3
Solomon Rutzky