Hibernate/JPA и MS SQL Server - открыть симметричный ключ перед DecryptByKey

В базе данных SQL Server 2012 у меня есть таблица со столбцом varbinary(128), в которой хранятся данные, зашифрованные ключом, поддерживаемым сертификатом (AppCert) (Secret_Key), и средством проверки подлинности с использованием хэша первичного ключа SHA2_512:

CREATE TABLE [Lookup].[SecretStuff] (
    [Id] tinyint NOT NULL, 
    [Secret] varbinary(128) NOT NULL
        CONSTRAINT [PK_Lookup-SecretStuff_Id] PRIMARY KEY CLUSTERED ([Id] ASC), 
        CONSTRAINT [UN_Lookup-SecretStuff_Secret] UNIQUE NONCLUSTERED ([Secret])
);

OPEN SYMMETRIC KEY [Secret_Key] DECRYPTION BY CERTIFICATE [AppCert];
INSERT INTO [Lookup].[SecretStuff] ([Id], [Secret]) VALUES (1, ENCRYPTBYKEY(KEY_GUID('Secret_Key'), CONVERT(nvarchar(512), 'I have a secret'), 1, HASHBYTES('SHA2_512', CONVERT(varbinary(128), CONVERT(tinyint, 1)))));
CLOSE SYMMETRIC KEY [Secret_Key];

OPEN SYMMETRIC KEY [Secret_Key] DECRYPTION BY CERTIFICATE [AppCert];
SELECT [Id], CONVERT(nvarchar, DECRYPTBYKEY([Secret], 1, HASHBYTES('SHA2_512', CONVERT(varbinary, [Id])))) AS [Secret] FROM [Lookup].[SecretStuff];
CLOSE SYMMETRIC KEY [Secret_Key];

Это все прекрасно работает. Теперь у меня есть приложение Spring Boot, использующее JPA/Hibernate, которое подключено и протестировано / проверено для работы с этой базой данных. Класс SecretStuff:

@Entity
@Table(name="SecretStuff", schema="Lookup")
public class SecretStuff {

    @Id
    @Column(name = "Id", 
            nullable = false)
    private Integer id;

    @Column(name = "Secret", 
            nullable = false, 
            unique = true)
    @Size(min = 1, max = 255)
    @ColumnTransformer(
            read = "CONVERT(nvarchar(89), DECRYPTBYKEY([Secret], 1, 
                    HASHBYTES('SHA2_512', CONVERT(varbinary(128), [Id]))))")
    private String secret;
// getters/setter omitted
}

Когда я тестирую класс SecretStuff, я вижу следующий сгенерированный Hibernate SQL:

select
    secretstuf0_.Id as Id1_7_0_,
    CONVERT(nvarchar(89), DECRYPTBYKEY(secretstuf0_.[Secret],
    1,
    HASHBYTES('SHA2_512',
    CONVERT(varbinary(128),
    secretstuf0_.[Id])))) as Secret2_7_0_ 
from
    Lookup.SecretStuff secretstuf0_ 
where
    secretstuf0_.Id=?

Совершенно разумный запрос, который выполняет и возвращает 1 строку с Id = 1 Secret = NULL, потому что ключ SYMMETRIC не открывается перед выполнением запроса.

Мой вопрос:Как я могу выполнить команду OPEN SYMMETRIC KEY... в одной транзакции перед запросом и команду CLOSE SYMMETRIC KEY... в одной транзакции после запроса?

Я использовал Hibernate.initialize внутри методов класса обслуживания, чтобы лениво извлекать отношения ко многим внутри транзакций. Буду ли я использовать подобный подход здесь? Как?

Я видел эту статью, когда набирал этот вопрос, но ему уже пару лет, и он использует подход EntityManager с NativeQuery. Есть ли обновленный способ управления этим с помощью JPA/Hibernate?

1 ответ

Хотя это и не точное решение, которое я искал, статья, на которую я ссылался в своем первоначальном вопросе, и некоторая помощь от Trevor Abell, привели меня к следующему коду, который, кажется, соответствует моим потребностям. Я выкладываю это здесь на случай, если кто-то еще ищет подобное решение. Я все еще открыт для других решений.

Ключ использовал EntityManager для запуска команд SYMMETRIC KEY вокруг запроса. Я добавлю интерфейс @Repository и класс @Service для полноты, но ключом был класс @Service:

@Repository
public interface SecretStuffRepository extends JpaRepository<SecretStuff, Integer> {}

@Service
@Transactional(readOnly = true)
public class SecretStuffService {

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private SecretStuffRepository repository;

    public Iterable<SecretStuff> findAll() {

        this.entityManager.createNativeQuery("
              OPEN SYMMETRIC KEY [Secret_Key] 
              DECRYPTION BY CERTIFICATE [AppCert];
          ").executeUpdate();
        Iterable<SecurityQuestion> questions = this.repository.findAll();
        this.entityManager.createNativeQuery("
              CLOSE SYMMETRIC KEY [Secret_Key];
          ").executeUpdate();
        return stuff;
    }
}

Теперь, когда я тестирую SecretStuffService, я вижу следующий сгенерированный Hibernate SQL:

Hibernate: 
    OPEN SYMMETRIC KEY [Secret_Key] DECRYPTION 
BY
    CERTIFICATE [AppCert];
Hibernate: 
    select
        secretstuf0_.Id as Id1_5_0_,
        CONVERT(nvarchar(89),
        DECRYPTBYKEY(secretstuf0_.[Secret],
        1,
        HASHBYTES('SHA2_512',
        CONVERT(varbinary(128),
        secretstuf0_.[Id])))) as Secret2_5_0_ 
    from
        Lookup.SecretStuff secretstuf0_ 
    where
        secretstuf0_.Id=?
Hibernate: 
    CLOSE SYMMETRIC KEY [Secret_Key];

Вызов SecretStuffService.findAll() теперь открывает ключ, вызывает метод findAll () хранилища и закрывает ключ внутри той же транзакции. Помните, что NativeQuery в этом случае относится только к Microsoft SQL Server, поэтому вам нужно будет заменить соответствующую команду для вашего поставщика базы данных.

Другие вопросы по тегам