apache / shardingsphere

Distributed SQL transaction & query engine for data sharding, scaling, encryption, and more - on any database.
Apache License 2.0
19.85k stars 6.72k forks source link

Add a new field (original type of entity-field) to the “EncryptContext” during EncryptAlgorithm#decrypt #25641

Closed gyCode2017 closed 2 months ago

gyCode2017 commented 1 year ago

Is your feature request related to a problem?

When I am using the sharding encryption jdbc module, I want to achieve automatic type conversion of ciphertext fields after decryption, such as the amount - BigDecimal. However, EncryptContext lacks an original field type configuration, so I would like to propose a requirement to add a field that can be used in my decryption method

Describe the feature you would like.

code position: org.apache.shardingsphere.encrypt.merge.dql.EncryptMergedResult#getValue

` @Override public Object getValue(final int columnIndex, final Class<?> type) throws SQLException { Optional encryptContext = metaData.findEncryptContext(columnIndex); if (!encryptContext.isPresent() || !metaData.isQueryWithCipherColumn(encryptContext.get().getTableName(), encryptContext.get().getColumnName())) { return mergedResult.getValue(columnIndex, type); } Optional encryptAlgorithm = metaData.findEncryptor(encryptContext.get().getTableName(), encryptContext.get().getColumnName()); if (!encryptAlgorithm.isPresent()) { return mergedResult.getValue(columnIndex, type); } Object cipherValue = mergedResult.getValue(columnIndex, Object.class);

   // add this line
    encryptContext.get().setEntityFiledType(type);

    return null == cipherValue ? null : encryptAlgorithm.get().decrypt(cipherValue, encryptContext.get());
}`

then usage in org.apache.shardingsphere.encrypt.algorithm.AESEncryptAlgorithm

// org.apache.shardingsphere.encrypt.algorithm.AESEncryptAlgorithm#decrypt

`public Object decrypt(String ciphertext, EncryptContext encryptContext) { if (null == ciphertext) { return null; }

    byte[] result = getCipher(Cipher.DECRYPT_MODE).doFinal(Base64.getDecoder().decode(ciphertext));
    String s =  new String(result, StandardCharsets.UTF_8);

    // add this line
    if (encryptContext.getEntityFiledType() == BigDecimal.class) {
        return new BigDecimal(s);
    }

    // todo date、localDate、localDateTime

    return s;
}`

Hope to help solve or answer the problem!thank you very much~

azexcy commented 1 year ago

Hi @tuichenchuxin Can you help with some advice?

sandynz commented 1 year ago

Hi @GaoEinstein , I had a look at the code, AESEncryptAlgorithm.decrypt always return String type for now, but not different types of Object. Since EncryptContext just contains columnName, without column metadata. So it could not decode to related Java types, e.g. BigDecimal.

Are you interesting to supply a design proposal (contains how to change related API), we could discuss it then.

gyCode2017 commented 1 year ago

A relatively simple solution may be as follows.

` @RequiredArgsConstructor @Getter @Setter public final class TypedEncryptContext {

private final EncryptContext encryptContext;

/**
 * entity字段的原始类型
 */
private final Class<?> entityFieldType;

} public interface TypedEncryptAlgorithm<I, O> {

/**
 * 数据库中某字段的数据解密
 * @param cipherValue 数据库中存储的密文
 * @param encryptContext 上下文
 * @return 解密结果
 */
I decrypt(O cipherValue, TypedEncryptContext encryptContext);

} `

` public final class TypedEncryptAlgorithmAdapter {

public static Object decrypt(Object cipherValue, EncryptContext encryptContext, StandardEncryptAlgorithm delegate,
                        Class<?> type) {
    if (delegate instanceof TypedEncryptAlgorithm) {
        TypedEncryptContext typedEncryptContext = new TypedEncryptContext(encryptContext, type);
        return ((TypedEncryptAlgorithm) delegate).decrypt(cipherValue, typedEncryptContext);
    }
    return delegate.decrypt(cipherValue, encryptContext);
}

} ` An alternative solution would be to override the SPI, EncryptResultDecoratorEngine, by modifying META-INF/services/org.apache.shardingsphere.infra.merge.engine.ResultProcessEngine. Additionally, we can override EncryptDQLResultDecorator and EncryptMergedResult. By implementing TypedEncryptAlgorithmAdapter in org.apache.shardingsphere.encrypt.merge.dql.EncryptMergedResult#getValue, we can execute the decryption process and effectively address this issue.

If the official version allows modifications in org.apache.shardingsphere.encrypt.merge.dql.EncryptMergedResult#getValue by adding the field type to the context, the work I have done above would be unnecessary.

sandynz commented 1 year ago

@strongduanmu , could you help to have a look at it? Maybe it's related to encrypt refactoring.

strongduanmu commented 1 year ago

Hi @GaoEinstein I wonder where the primitive type is obtained from? hardcode? Logical columns don't actually exist in the database, so we can't get the logical type.

gyCode2017 commented 1 year ago

Hi @GaoEinstein I wonder where the primitive type is obtained from? hardcode? Logical columns don't actually exist in the database, so we can't get the logical type.

Hi @strongduanmu ,5.3.2 org.apache.shardingsphere.encrypt.merge.dql.EncryptMergedResult#getValue. The second param final Class<?> type

gyCode2017 commented 1 year ago

Hi @GaoEinstein I wonder where the primitive type is obtained from? hardcode? Logical columns don't actually exist in the database, so we can't get the logical type.

Isn't the second parameter the actual type of the field? Did I make a mistake? I hope to receive a correction and reply. thank you

strongduanmu commented 1 year ago

@GaoEinstein Can you investigate how different database JDBC drivers handle the getValue(1, Type) method? This doesn't seem to be something that encryption needs to handle, but the logic of the JDBC driver layer, what do you think?

github-actions[bot] commented 1 year ago

There hasn't been any activity on this issue recently, and in order to prioritize active issues, it will be marked as stale.