Archive

Archive for the ‘T-Sql’ Category

Lost Database Master Key Password

November 3, 2011 1 comment

I had a situation occur recently where I needed to restore a DB to a new server but I didn’t have the ability the bring the Service Master Key over with it. Nor did I have the original password used to encrypt the Database Master Key (it was before my time).

What I did have was access to the box with the database still running and the associated key hierarchy still operational (symmetric keys and the like).

I’ve not done too much messing around with the database master key before, feeling it was best left well alone, however the need to migrate this DB was there and I needed to pull it across without loosing access to the encrypted data.

To prototype this activity out I created a database on my local DB server and utilized the services of another Sql Server 2008 Server elsewhere in the organization.

So, first off I needed to create the key hierarchy and a place to store my test encrypted data:

Create Master Key Encryption By Password = 'abc123!!!ORIGINAL';
Create Asymmetric Key AKey With Algorithm = RSA_2048;
Create Symmetric Key SKey With Algorithm = TRIPLE_DES_3KEY
    Encryption By Asymmetric Key AKey
Create Table dbo.ASaucerfulOfSecrets
    (
        TheSecret        VarBinary(MAX)
    )
Open Symmetric Key SKey Decryption By Asymmetric Key AKey;
Insert Into dbo.ASaucerfulOfSecrets Values
    ( EncryptByKey(Key_Guid('SKey'), N'Ssssh! This is a BIG secret!'
                                   , 1, 0x12345));
Close Symmetric Key SKey;

I confirmed that encryption was working as expected by simply pulling the data back:

Select *
From dbo.ASaucerfulOfSecrets
Open Symmetric Key SKey Decryption By Asymmetric Key AKey;
Select Cast(DecryptByKey(TheSecret, 1, 0x12345) As NVarChar(MAX))
  From dbo.ASaucerfulOfSecrets;
Close Symmetric Key SKey;

Which gave me the following results

TheSecret

0x00496C61F87C4941BF7CBE8C879146BA0100000043C4B3F8824C73EBF3C0…

(No column name)

Ssssh! This is a BIG secret!

Life so far was operating within the expected parameters. Now I backed up the DB and restored it to a different server.

Running the select again I got the expected exceptions raised due to the inability to open the Database Master Key:


(1 row(s) affected)

Msg 15581, Level 16, State 3, Line 3

Please create a master key in the database or open the master key in the session before performing this operation.

(1 row(s) affected)

Msg 15315, Level 16, State 1, Line 5

The key ‘SKey’ is not open. Please open the key before using it.

So, now came the exercise of correcting the issue. Given that we had supposedly lost the password to the original DB, it’s safe to assume that one would want to recreate it (and this time, write the bloody thing down somewhere!). So that’s the approach I took. Back on the source server I ran the following:

Alter Master Key Regenerate
    With Encryption By Password = 'abc123!!!New'

The Books OnLine warns that this can be an intensive process and I suppose it comes down to just how many keys you have defined in your hierarchy. For the purposes of this test I had but one key so I wasn’t worried. Now I had a Master Key password that I knew of so I performed another backup, moved the DB to the secondary server and restored it. Just for giggles I reran the select again and received (rather unsurprisingly) the same results.

Next I tried the select again but only after explicitly opening the Database Master Key (with the ‘new’ password):

Open Master Key Decryption By Password = 'abc123!!!New'
Select *
From dbo.ASaucerfulOfSecrets
Open Symmetric Key SKey Decryption By Asymmetric Key AKey;
Select Cast(DecryptByKey(TheSecret, 1, 0x12345) As NVarChar(MAX))
  From dbo.ASaucerfulOfSecrets;
Close Symmetric Key SKey;

This did indeed pull back the decrypted data. Now for the final action – adding the Service Master Key to the Database Master Key:

Open Master Key Decryption By Password = 'abc123!!!New';
Alter Master Key Add Encryption By Service Master Key;

This enabled the encryption to use either an explicitly opened master key, or use the service master key – which is exactly what I was after.

I was able to confirm the results by performing the select again (without opening the master key first) and this gave me my encrypted data back. Tada!

Categories: Encryption, Sql Server, T-Sql

Sql Agent Session Storage

November 10, 2009 Leave a comment

I’ve been fiddling around with some jobs recently that required values to be kept from one job step to another. Not finding anything much exciting I went for plan Z – roll my own.

They’re simple procedures but do work nicely.

First I create a Schema that everything will be bundled under:

  1. If Not Exists ( Select 1 From sys.schemas Where Name = 'Aracs.Utilities.Jobs')
  2.     Exec sp_ExecuteSql N'Create Schema [Aracs.Utilities.Jobs]';

 

Now we’ll need a backing store:

  1. Create Table [Aracs.Utilities.Jobs].SessionSaverStore
  2.     (
  3.          JobId                UniqueIdentifier        Not Null
  4.         ,ParameterName        NVarChar(128)            Not Null
  5.         ,Value                Sql_Variant                Null
  6.     );

 

Next I create the SessionDestroyer Procedure (since it can be called from the next one this keeps the dependencies happy and calm):

  1. If Object_Id('[Aracs.Utilities.Jobs].SessionDestroyer') Is Null
  2.     Exec sp_ExecuteSql N'Create Procedure [Aracs.Utilities.Jobs].SessionDestroyer As';
  3.  
  4. Go
  5.  
  6. Alter Procedure [Aracs.Utilities.Jobs].SessionDestroyer
  7.  
  8. As
  9.  
  10.     Declare     @JobHex                    NVarChar(32)    
  11.             ,@JobId                        UniqueIdentifier
  12.     ;
  13.     
  14.     Select    @JobHex = Substring(Program_Name, 32, 32)
  15.       From    sys.dm_Exec_Sessions
  16.       Where    Session_Id = @@Spid;
  17.  
  18.     Delete    [Aracs.Utilities.Jobs].SessionSaverStore
  19.       Where    JobId = Cast('' as xml).value('xs:hexBinary(sql:variable("@JobHex") )', 'VarBinary(MAX)');

 

Along comes the SessionSaverStore next:

  1. If Object_Id('[Aracs.Utilities.Jobs].SessionSaverStore') Is Null
  2.     Create Table [Aracs.Utilities.Jobs].SessionSaverStore
  3.         (
  4.              JobId                        UniqueIdentifier
  5.             ,ParameterName                NVarChar(128)
  6.             ,Value                        Sql_Variant
  7.         );
  8.         
  9. If Object_Id('[Aracs.Utilities.Jobs].SessionSaver') Is Null
  10.     Exec sp_ExecuteSql N'Create Procedure [Aracs.Utilities.Jobs].SessionSaver As';
  11.  
  12. Go
  13.  
  14. Alter Procedure [Aracs.Utilities.Jobs].SessionSaver
  15.              @ParameterName                NVarChar(128)
  16.             ,@Value                        Sql_Variant
  17.             ,@StartFresh                Bit = 0
  18.  
  19. As
  20.  
  21.     Declare     @JobHex                    NVarChar(32)
  22.             ,@JobId                        UniqueIdentifier
  23.     ;
  24.     
  25.     Select    @JobHex = Substring(Program_Name, 32, 32)
  26.       From    sys.dm_Exec_Sessions
  27.       Where    Session_Id = @@Spid;
  28.  
  29.     Set @JobId = Cast('' as xml).value('xs:hexBinary(sql:variable("@JobHex") )', 'VarBinary(MAX)');      
  30.     If Exists ( Select 1 From MSDB.dbo.sysJobs Where Job_Id = @JobId )
  31.     Begin
  32.         If @StartFresh = 1
  33.             Exec [Aracs.Utilities.Jobs].SessionDestroyer;
  34.             
  35.         If Exists (
  36.             Select    1
  37.               From    [Aracs.Utilities.Jobs].SessionSaverStore
  38.               Where JobId = @JobId
  39.                 And ParameterName = @ParameterName
  40.         )
  41.             Update    [Aracs.Utilities.Jobs].SessionSaverStore
  42.               Set    Value = @Value
  43.               Where    JobId = @JobId
  44.                 And    ParameterName = @ParameterName;
  45.         
  46.         Else
  47.             Insert
  48.               Into    [Aracs.Utilities.Jobs].SessionSaverStore
  49.                     (
  50.                          JobId
  51.                         ,ParameterName
  52.                         ,Value
  53.                     )
  54.                 Values
  55.                     (
  56.                          @JobId
  57.                         ,@ParameterName
  58.                         ,@Value
  59.                     );
  60.     End

 

Finally the SessionRetriever:

  1. If Object_Id('[Aracs.Utilities.Jobs].SessionRetriever') Is Null
  2.     Exec sp_ExecuteSql N'Create Procedure [Aracs.Utilities.Jobs].SessionRetriever As';
  3.  
  4. Go
  5.  
  6. Alter Procedure [Aracs.Utilities.Jobs].SessionRetriever
  7.              @ParameterName                NVarChar(128)
  8.             ,@Value                        Sql_Variant        Output
  9. As
  10.  
  11.     Declare     @JobHex                    NVarChar(32)    
  12.             ,@JobId                        UniqueIdentifier
  13.             ,@Valuei                    Sql_Variant
  14.             ,@DataType                    NVarChar(128)
  15.     ;
  16.     
  17.     Select    @JobHex = Substring(Program_Name, 32, 32)
  18.       From    sys.dm_Exec_Sessions
  19.       Where    Session_Id = @@Spid;
  20.  
  21.     Set @JobId = Cast('' as xml).value('xs:hexBinary(sql:variable("@JobHex") )', 'VarBinary(MAX)');      
  22.     If Exists ( Select 1 From MSDB.dbo.sysJobs Where Job_Id = @JobId )
  23.     Begin
  24.         Select    @Value = Value
  25.           From    [Aracs.Utilities.Jobs].SessionSaverStore
  26.           Where    JobId = @JobId
  27.             And    ParameterName = @ParameterName;
  28.  
  29.     End

 

Use is pretty straightforward. As an Proof of concept I created a Sql Agent Job with two steps – Store’ and ‘Fetch’

Store contained the following:

  1. Exec [Aracs.Utilities.Jobs].SessionSaver 'Name', 'Fred', 1
  2. Exec [Aracs.Utilities.Jobs].SessionSaver 'Age', 37

 

And Fetch:

  1. Declare @Name Sql_Variant
  2. Declare @Age Sql_Variant
  3.  
  4.   Exec[Aracs.Utilities.Jobs].SessionRetriever 'Name', @Name Output;
  5.   Exec[Aracs.Utilities.Jobs].SessionRetriever 'Age', @Age Output;
  6.  
  7. Print Cast(@Name As VarChar(128)) + ' ' + Cast( @Age As VarChar(4));

 

Granted it’s missing some bells and whistles (like exception handling) and I’d wager I could stuff the Retriever in as a function. However as a first hit I was pretty chuffed with it!

Categories: Sql Server, T-Sql
Follow

Get every new post delivered to your Inbox.