使用 java 的可搜索行级加密?
searchable row level encryption using java?
我正在设计一个 java 应用程序,它使用算法将其他来源的数据导入数据库。该应用程序还会在数据库中搜索记录。
如何实现行级安全性,使数据库甚至不知道数据已加密,而且允许使用从 java代码?
我可以使用BouncyCastle 将每行数据中的每个字段在插入数据库之前对其进行加密。但是,如果数据库中的每一行和字段都单独加密,我该如何搜索行呢?答案是否像在将搜索参数传递到 SQL 或 JPA SELECT 查询之前使用相同的密钥加密每个搜索参数一样简单?还是需要更复杂的方法?
我目前正在使用 MySQL,但如果这与数据库供应商无关就更好了。
好的加密最重要的特性之一是相似的明文被加密成截然不同的密文。两个密文的大约一半位将匹配。这 属性 使得很难(不可能)制定任何类型的查询,通过 LIKE
查找子字符串或确定字段值是大于还是小于给定值。
还有一个属性,那就是语义安全。当用同一个密钥加密同一个明文时,产生的密文应该是不同的。 属性 使攻击者无法获得有关明文块的一些元信息,但由于建议的解决方案的工作方式,必须删除此 属性。
让我们以AES作为CBC模式下的基本加密原语为例。它的块大小为 16 字节,因此密文将是它的倍数。如果这是一个太大的开销,您应该使用具有三个不同密钥的三重 DES(= 168 位安全性的 24 字节密钥)。
简单示例:
所有 table 单元都使用相同的密钥加密。现在,您想要查询 table 以获取其中一列具有特定值的行。首先,您加密值以使用相同的密钥进行匹配,并且由于我们说过没有语义安全性,因此生成的密文将 完全 与 table 中的密文相匹配。
query("SELECT * FROM table WHERE col = '" + encrypt(x) + "';");
然后您遍历结果集并解密每个值。 警告:为简单起见,未对查询进行参数化。使用准备好的语句来禁用 SQL 注入。
实现非语义安全:
ECB 模式是不安全的支柱,我建议使用带有静态 IV 的 CBC 模式(可能全是 0x00 字节:new byte[16];
)。还有其他确定性的操作模式,但稍后会详细介绍。
限制:
- 没有
order by
- 不直接用SQL
中的值进行计算
- where 子句中没有
<
、>
、<=
或 >=
- ...(现在想不起来)
让它变得有趣:
您可以采取一些措施来提高安全性。
如果您事先知道您永远不会尝试查看两列是否具有相同的值,那么您可以使用半随机方法,您可以为每个 table 的每一列分配一个不同的随机初始化矢量 (IV)。这样一来,攻击者就无法尝试将一列的密文与另一列的密文进行匹配,以找到相似之处来获取有关明文的一些元数据。
如果减少开销不是那么重要,您可以选择像 SIV 这样的确定性身份验证加密模式,而不是 CCM 或 GCM(不确定 EAX)。它只有身份验证标签的开销(AES 为 16 字节)。通过使用它,您可以随时检查密文是否被某人操纵,并且可以检查密文值是否从另一个 table 单元格移动,因为您可以简单地使用列名作为关联数据。仍然很难检测到它是否在没有严重性能下降的情况下在列中移动。
幻想解决限制
Order-Preserving Encryption 可用于解决上面提出的第一个限制,但您会损害安全性,因为
Intuitively, it says that certain attackers can learn half the bits of a plaintext given its ciphertext.
来源:How does order-preserving encryption work?
如果 SQL 风格直接在 SQL 中提供加密功能,则可以避免第二个限制(也可能是其他限制),但这可能太慢而无法在大型应用程序中使用规模。
Public-密钥加密
您可能已经注意到,我自始至终只提到了对称加密。不必仅使用对称加密,但例如 RSA 的问题是密文是 巨大 (2048 位密钥的 256 字节)与 AES 的小开销相比。基于 ECC 的加密的足迹要好得多(例如 ElGamal Encrypt)。
Public-Key Crypto 的另一个好处是你可以查询任何你想要的数据,但是没有私钥就无法解密。所以你总是可以放入数据(使用 public 密钥),但只能用私钥取出数据。
我正在设计一个 java 应用程序,它使用算法将其他来源的数据导入数据库。该应用程序还会在数据库中搜索记录。
如何实现行级安全性,使数据库甚至不知道数据已加密,而且允许使用从 java代码?
我可以使用BouncyCastle 将每行数据中的每个字段在插入数据库之前对其进行加密。但是,如果数据库中的每一行和字段都单独加密,我该如何搜索行呢?答案是否像在将搜索参数传递到 SQL 或 JPA SELECT 查询之前使用相同的密钥加密每个搜索参数一样简单?还是需要更复杂的方法?
我目前正在使用 MySQL,但如果这与数据库供应商无关就更好了。
好的加密最重要的特性之一是相似的明文被加密成截然不同的密文。两个密文的大约一半位将匹配。这 属性 使得很难(不可能)制定任何类型的查询,通过 LIKE
查找子字符串或确定字段值是大于还是小于给定值。
还有一个属性,那就是语义安全。当用同一个密钥加密同一个明文时,产生的密文应该是不同的。 属性 使攻击者无法获得有关明文块的一些元信息,但由于建议的解决方案的工作方式,必须删除此 属性。
让我们以AES作为CBC模式下的基本加密原语为例。它的块大小为 16 字节,因此密文将是它的倍数。如果这是一个太大的开销,您应该使用具有三个不同密钥的三重 DES(= 168 位安全性的 24 字节密钥)。
简单示例:
所有 table 单元都使用相同的密钥加密。现在,您想要查询 table 以获取其中一列具有特定值的行。首先,您加密值以使用相同的密钥进行匹配,并且由于我们说过没有语义安全性,因此生成的密文将 完全 与 table 中的密文相匹配。
query("SELECT * FROM table WHERE col = '" + encrypt(x) + "';");
然后您遍历结果集并解密每个值。 警告:为简单起见,未对查询进行参数化。使用准备好的语句来禁用 SQL 注入。
实现非语义安全:
ECB 模式是不安全的支柱,我建议使用带有静态 IV 的 CBC 模式(可能全是 0x00 字节:new byte[16];
)。还有其他确定性的操作模式,但稍后会详细介绍。
限制:
- 没有
order by
- 不直接用SQL 中的值进行计算
- where 子句中没有
<
、>
、<=
或>=
- ...(现在想不起来)
让它变得有趣:
您可以采取一些措施来提高安全性。
如果您事先知道您永远不会尝试查看两列是否具有相同的值,那么您可以使用半随机方法,您可以为每个 table 的每一列分配一个不同的随机初始化矢量 (IV)。这样一来,攻击者就无法尝试将一列的密文与另一列的密文进行匹配,以找到相似之处来获取有关明文的一些元数据。
如果减少开销不是那么重要,您可以选择像 SIV 这样的确定性身份验证加密模式,而不是 CCM 或 GCM(不确定 EAX)。它只有身份验证标签的开销(AES 为 16 字节)。通过使用它,您可以随时检查密文是否被某人操纵,并且可以检查密文值是否从另一个 table 单元格移动,因为您可以简单地使用列名作为关联数据。仍然很难检测到它是否在没有严重性能下降的情况下在列中移动。
幻想解决限制
Order-Preserving Encryption 可用于解决上面提出的第一个限制,但您会损害安全性,因为
Intuitively, it says that certain attackers can learn half the bits of a plaintext given its ciphertext.
来源:How does order-preserving encryption work?
如果 SQL 风格直接在 SQL 中提供加密功能,则可以避免第二个限制(也可能是其他限制),但这可能太慢而无法在大型应用程序中使用规模。
Public-密钥加密
您可能已经注意到,我自始至终只提到了对称加密。不必仅使用对称加密,但例如 RSA 的问题是密文是 巨大 (2048 位密钥的 256 字节)与 AES 的小开销相比。基于 ECC 的加密的足迹要好得多(例如 ElGamal Encrypt)。
Public-Key Crypto 的另一个好处是你可以查询任何你想要的数据,但是没有私钥就无法解密。所以你总是可以放入数据(使用 public 密钥),但只能用私钥取出数据。