I'm looking a way to do something like this. I don't know how to call it, so i don't know if it exist or how to find it. Some keyword would be welcome :)
String var_1 = "user data";
String fix_1 = "supply data";
String mix = mixer(var_1,fix_1);
// mix = " something fully random "
String var_2 = "user data changed";
String fix_2 = fixer(var_2,mix);
And mix == mixer(var_2, fix_2);
So to resume, I need to generate a random data from 2 variables. 1 is variable from user and 1 is supply by me.
First time , I generate the data with these 2 variables with one function.
Then, if the user data change, with another function, I compute the new supply data with the first result and the new user data. And if I use again the computed data and the new user data, I must obtain the same data computed the first time.
Is there something to do that ? Like some cipher technique or so?
Thanks for Intel.
In fact there is something like this already which may satisfy you needs. In fact you know this function too. It's the good old XOR. And yes, it is used in crypto a lot. In fact it's the core idea of the stream ciphers and the One Time Pad.
It goes like this:
Assume you have a byte array of length n called var_1.
Assume you have a random value fix_1 of the same length.
If you do var_1 XOR fix_1 you get mix.
If you do mix XOR fix_1 you get var_1 again. (Basic math: fix_1 XOR fix_1 equals chain of zero value bytes and var_1 XOR zero bytes = var_1.
This whole thing will be as random and secure as random and secret fix_1 remains. If one of the values is not random the approach is not secure at all.
So following the idea of User253751 in comment, I was able to do it.
Step:
generate the private constant key => privateKey = encrypt(publicKey, Password_1) (the first public key is random )
if password change, generate a new public key by decoding the private constant key with password_2 => publicKey_Updated = decrypt(privateKey, Password_2)
Check if the new public key is valid : privateKey_Rebuild = encrypt(publicKey_Updated, Password_2) ====> if everything is ok, privateKey == privateKey_Rebuild.
---> I test it only with a low cryptage i use just for obfuscation, but it should work with symmetric key too. I'm not sur about Asymetric key, because to make this work, you need a crypting protocol who always give you the same crypted data with the same input. And RSA do not gave you the same crypted data even with the same input.
Here my code (not a copy/paste snippet beacause it use my own library), but you can catch the idea easily with the function name.
KeyObfusc publicKey_1 = KeyObfusc.fromPassword("publicKey_1");
KeyObfusc password_1 = KeyObfusc.fromPassword("password_1");
Encoder encoder_1 = new Encoder(password_1, CipherFormat.HEX);
Decoder decoder_1 = new Decoder(password_1, CipherFormat.HEX);
byte[] privateKey = encoder_1.toBytes(publicKey_1.getEncoded());
byte[] publicKey_1_Rebuild = decoder_1.fromBytesToBytes(privateKey);
LogDelay.send("password_1 : " + BytesTo.stringHex(password_1.getEncoded()));
LogDelay.send("publicKey_1 : " + BytesTo.stringHex(publicKey_1.getEncoded()));
LogDelay.send("privateKey : " + BytesTo.stringHex(privateKey));
LogDelay.send("publicKey_1 Rebuild : " + Arrays.equals(publicKey_1.getEncoded(), publicKey_1_Rebuild) +
" " + BytesTo.stringHex(publicKey_1_Rebuild));
LogDelay.send();
KeyObfusc password_2 = KeyObfusc.fromPassword("password_2");
Encoder encoder_2 = new Encoder(password_2, CipherFormat.HEX);
Decoder decoder_2 = new Decoder(password_2, CipherFormat.HEX);
byte[] publicKey_2 = decoder_2.fromBytesToBytes(privateKey);
byte[] privateKey_Rebuild = encoder_2.toBytes(publicKey_2);
LogDelay.send("password_2 : " + BytesTo.stringHex(password_2.getEncoded()));
LogDelay.send("publicKey_2 : " + BytesTo.stringHex(publicKey_2));
LogDelay.send("privateKey Rebuild: " + Arrays.equals(privateKey, privateKey_Rebuild) +
" " + BytesTo.stringHex(privateKey_Rebuild));
LogDelay.send();
Related
I am trying to create an app in flutter and the sample code which I have is in java.
Here is the sample java code gist
https://gist.github.com/kapiljhajhria/72a22ff75e238878f539f7bb21026208
and here is my flutter code gist
https://gist.github.com/kapiljhajhria/795d1a7c7cf1c76ca8e327bf8b2f51de
Here is a brief summary of what I am doing
Generate a unique Session Key: AES Random Key 256
Encrypt JSON data using the Session Key from step 1
Generate SHA256 hash of JSON data
Encrypt generated hash from step 3, using session key from step 1.
Encrypt the session key using the public key. Public key provided as certificate.cer file. I copied the String value and added it to the class as a constant in order to make it easier to use. Not sure if this was the best approach.
Created a POST request with 3 Parameters. As shown in the java code. I think I am doing this part correctly.
The response which I will get will be encrypted using the session key from step 1. So i will have to decrypt that response data. Haven't reached this step yet.
I don't have access to the server where this request is being made.
Since the post request is being made using web view, I can't figure out a way to get proper error from my request. All I get is web page which says "Invalid Request"
So My first guess is that I am not using public key properly to encrypt my session key.
if that part is correct, then I am not encrypting the data properly or my encryption method doesn't match encryption methods used in java code the java code. Maybe the session key which I am generating is not correct.
Any help would be greatly appreciated. Thank you. If you need anything from me then please let me know.
I used this document as my reference: https://developers.emsigner.com/signer-gateway/api-reference/signing-documents.html
You need 2 packages: pointycastle and x509, and to import them as follows:
import 'package:pointycastle/export.dart';
import 'package:x509/x509.dart';
Then you need these helper functions:
Uint8List generateSessionKey() {
final r = Random();
return Uint8List.fromList(List<int>.generate(32, (_) => r.nextInt(256)));
}
RSAPublicKey parseCert(String pemData) {
final cert = parsePem(pemData).first as X509Certificate;
final pub = cert.publicKey as RsaPublicKey;
return RSAPublicKey(pub.modulus, pub.exponent);
}
Uint8List encryptUsingPublicKey(RSAPublicKey key, Uint8List data) {
final cipher = PKCS1Encoding(RSAEngine())
..init(true, PublicKeyParameter<RSAPublicKey>(key));
return cipher.process(data);
}
Uint8List encryptUsingSessionKey(Uint8List key, Uint8List data) {
final cipher = PaddedBlockCipher('AES/ECB/PKCS7')
..init(true, PaddedBlockCipherParameters(KeyParameter(key), null));
return cipher.process(data);
}
Uint8List sha256Digest(Uint8List data) {
return SHA256Digest().process(data);
}
And you'd build your 3 parameters like this:
final pem = File('cert2.pem').readAsStringSync();
final publicKey = parseCert(pem);
final sessionKey = generateSessionKey();
final encryptedSessionKey = encryptUsingPublicKey(publicKey, sessionKey);
final jsonString = json.encode(<String, dynamic>{
'FileType': 'PDF',
'SignaturePosition': 'Top-Left',
'AuthToken': 'some token',
'File': '',
'SUrl': 'http://localhost:3000/Success',
'FUrl': 'http://localhost:3000/Error',
'CUrl': 'http://localhost:3000/Cancel',
'ReferenceNumber': 'generate unique reference number',
});
final jsonBytes = utf8.encode(jsonString) as Uint8List;
final encryptedJson = encryptUsingSessionKey(sessionKey, jsonBytes);
final hash = sha256Digest(jsonBytes);
final encryptedHash = encryptUsingSessionKey(sessionKey, hash);
final p1 = base64.encode(encryptedSessionKey);
final p2 = base64.encode(encryptedJson);
final p3 = base64.encode(encryptedHash);
BUT, the big problem I see is how you then do the POST, because you want to be in a web page, right? And the normal flutter web view doesn't support an initial post. It does look like there's another package. Just search for flutter webview post.
By the way, if you don't want to use the pointycastle registry, you can rewrite encryptUsingSessionKey without as:
final cipher = PaddedBlockCipherImpl(
PKCS7Padding(),
ECBBlockCipher(AESEngine()),
)..init(true, PaddedBlockCipherParameters(KeyParameter(key), null));
return cipher.process(data);
Finally, at least until you get the web view issue understood, you can just use http to do the post. But, let it do the work of encoding the parameters and setting the content type, as follows:
final response = await http.post(
Uri.parse('https://somewhere/V3_0/Index'),
body: <String, String>{
'Parameter1': p1,
'Parameter2': p2,
'Parameter3': p3,
},
);
print(response.statusCode);
print(response.body);
Have a look at this project, It's an example for the Encryption by Flutter with a good documentation, have 3 types of encryptions:
AES encryptions
Fernet encryptions
Salsa20 encryptions
TL;DR: RSA public key generated in iOS and stored in the keychain, exported as base64 and sent to a java backend, is not recognized.
I'm implementing a chat encryption feature in an iOS app, and I'm using symmetric + asymmetric keys to handle it.
Without going too much into details, at backend I use the user's public key to encrypt a symmetric key used to encrypt and decrypt messages.
I created two frameworks, respectively in Swift and in Java (backend) to handle key generation, encryption, decryption, etc. I also have tests for them, so I'm 100% everything works as expected.
However, it looks like the backend is unable to recognize the format of the public key passed from iOS. Using RSA both sides, this is the code I use in Swift to generate the key:
// private key parameters
static let privateKeyParams: [String : Any] = [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: "..." // I have a proper unique tag here
]
// public key parameters
static let publicKeyParams: [String : Any] = [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: "..." // I have a proper unique tag here
]
// global parameters for our key generation
static let keyCreationParameters: [String : Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String: 2048,
kSecPublicKeyAttrs as String: publicKeyParams,
kSecPrivateKeyAttrs as String: privateKeyParams
]
...
var publicKey, privateKey: SecKey?
let status = SecKeyGeneratePair(Constants.keyCreationParameters as CFDictionary, &publicKey, &privateKey)
I use specular code to read the keys from the keychain.
This is the piece of code I use to export the public key as a base64 string:
extension SecKey {
func asBase64() throws -> String {
var dataPtr: CFTypeRef?
let query: [String:Any] = [
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: "...", // Same unique tag here
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecReturnData as String: kCFBooleanTrue
]
let result = SecItemCopyMatching(query as CFDictionary, &dataPtr)
switch (result, dataPtr) {
case (errSecSuccess, .some(let data)):
// convert to Base64 string
let base64PublicKey = data.base64EncodedString(options: [])
return base64PublicKey
default:
throw CryptoError.keyConversionError
}
}
}
At backend level I use this Java code to convert the base64 string to a public key:
public PublicKey publicKeyFrom(String data) throws NoSuchAlgorithmException, InvalidKeySpecException {
byte[] publicBytes = Base64.decodeBase64(data);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
But this fails at the last line, with this exception:
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
Doing some manual debugging, I noticed that the format of the public key is different - when I generate a key in iOS and then export as base 64, it looks like this:
MIIBCgKCAQEA4M/bRDdH0f6qFIXxOg13RHka+g4Yv8u9PpPp1IR6pSwrM1aq8B6cyKRwnLe/MOkvODvDfJzvGXGQ01zSTxYWAW1B4uc/NCEemCmZqMosSB/VUJdNxxWtt2hJxpz06hAawqV+6HmweAB2dUn9tDEsQLsNHdwYouOKpyRZGimcF9qRFn1RjR0Q54sUh1tQAj/EwmgY2S2bI5TqtZnZw7X7Waji7wWi6Gz88IkuzLAzB9VBNDeV1cfJFiWsZ/MIixSvhpW3dMNCrJShvBouIG8nS+vykBlbFVRGy3gJr8+OcmIq5vuHVhqrWwHNOs+WR87K/qTFO/CB7MiyiIV1b1x5DQIDAQAB
for a total of 360 characters, whereas doing the same in Java (still using RSA) it's like:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAAnWO4BXUGP0qM3Op36YXkWNxb4I2pPZuZ7jJtfUO7v+IO1mq43WzNaxLqqLPkTnMrv2ACRDK55vin+leQlL1z0LzVxjtZ9F6pajQo1r7PqBlL5N8bzBFKpagEf0QfyHPw0/0kG9DMnvQ+Im881QyN2zdl33wp5Fi+jRT7cunFQIDAQAB
with a length of 216 characters.
I'm unable to figure out what's wrong - apparently I wouldn't be surprised if iOS handles keys in a different key, and require special processing in order to talk with other folks.
Any idea?
We ran into the exact same problem when connecting an iOS app to a Java backend. And the CryptoExportImportManager mentioned by pedrofb helped us out too, which is awesome. However, the code in the CryptoExportImportManager class is a bit elaborated and might be hard to maintain. This is because a top-down approach is used when adding new components to the DER encoding. As a result, numbers contained by length fields must be calculated ahead (i.e. before the contents to which the length applies has been defined). I therefore created a new class that we now use to convert the DER encoding of an RSA public key:
class RSAKeyEncoding: NSObject {
// ASN.1 identifiers
private let bitStringIdentifier: UInt8 = 0x03
private let sequenceIdentifier: UInt8 = 0x30
// ASN.1 AlgorithmIdentfier for RSA encryption: OID 1 2 840 113549 1 1 1 and NULL
private let algorithmIdentifierForRSAEncryption: [UInt8] = [0x30, 0x0d, 0x06,
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00]
/// Converts the DER encoding of an RSA public key that is either fetched from the
/// keychain (e.g. by using `SecItemCopyMatching(_:_:)`) or retrieved in another way
/// (e.g. by using `SecKeyCopyExternalRepresentation(_:_:)`), to a format typically
/// used by tools and programming languages outside the Apple ecosystem (such as
/// OpenSSL, Java, PHP and Perl). The DER encoding of an RSA public key created by
/// iOS is represented with the ASN.1 RSAPublicKey type as defined by PKCS #1.
/// However, many systems outside the Apple ecosystem expect the DER encoding of a
/// key to be represented with the ASN.1 SubjectPublicKeyInfo type as defined by
/// X.509. The two types are related in a way that if the SubjectPublicKeyInfo’s
/// algorithm field contains the rsaEncryption object identifier as defined by
/// PKCS #1, the subjectPublicKey field shall contain the DER encoding of an
/// RSAPublicKey type.
///
/// - Parameter rsaPublicKeyData: A data object containing the DER encoding of an
/// RSA public key, which is represented with the ASN.1 RSAPublicKey type.
/// - Returns: A data object containing the DER encoding of an RSA public key, which
/// is represented with the ASN.1 SubjectPublicKeyInfo type.
func convertToX509EncodedKey(_ rsaPublicKeyData: Data) -> Data {
var derEncodedKeyBytes = [UInt8](rsaPublicKeyData)
// Insert ASN.1 BIT STRING bytes at the beginning of the array
derEncodedKeyBytes.insert(0x00, at: 0)
derEncodedKeyBytes.insert(contentsOf: lengthField(of: derEncodedKeyBytes), at: 0)
derEncodedKeyBytes.insert(bitStringIdentifier, at: 0)
// Insert ASN.1 AlgorithmIdentifier bytes at the beginning of the array
derEncodedKeyBytes.insert(contentsOf: algorithmIdentifierForRSAEncryption, at: 0)
// Insert ASN.1 SEQUENCE bytes at the beginning of the array
derEncodedKeyBytes.insert(contentsOf: lengthField(of: derEncodedKeyBytes), at: 0)
derEncodedKeyBytes.insert(sequenceIdentifier, at: 0)
return Data(derEncodedKeyBytes)
}
private func lengthField(of valueField: [UInt8]) -> [UInt8] {
var length = valueField.count
if length < 128 {
return [ UInt8(length) ]
}
// Number of bytes needed to encode the length
let lengthBytesCount = Int((log2(Double(length)) / 8) + 1)
// First byte encodes the number of remaining bytes in this field
let firstLengthFieldByte = UInt8(128 + lengthBytesCount)
var lengthField: [UInt8] = []
for _ in 0..<lengthBytesCount {
// Take the last 8 bits of length
let lengthByte = UInt8(length & 0xff)
// Insert them at the beginning of the array
lengthField.insert(lengthByte, at: 0)
// Delete the last 8 bits of length
length = length >> 8
}
// Insert firstLengthFieldByte at the beginning of the array
lengthField.insert(firstLengthFieldByte, at: 0)
return lengthField
}
}
Usage
You could use this class in the function asBase64() like this:
extension SecKey {
func asBase64() throws -> String {
var dataPtr: CFTypeRef?
let query: [String:Any] = [
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: "...", // Same unique tag here
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecReturnData as String: kCFBooleanTrue
]
let result = SecItemCopyMatching(query as CFDictionary, &dataPtr)
switch (result, dataPtr) {
case (errSecSuccess, .some(let data)):
// convert to X509 encoded key
let convertedData = RSAKeyEncoding().convertToX509EncodedKey(data)
// convert to Base64 string
let base64PublicKey = convertedData.base64EncodedString(options: [])
return base64PublicKey
default:
throw CryptoError.keyConversionError
}
}
}
UPDATE - Other Issue
After using the above class for a while, we stumbled upon another issue. Occasionally, the public key that is fetched from the keychain seems to be invalid because, for some reason, it has grown in size. This behavior matches with findings described in the question (although in our case the Base64 encoded key has grown to a size of 392 characters instead of 360 characters). Unfortunately, we didn’t find the exact cause of this strange behavior, but we found two solutions. The first solution is to specify kSecAttrKeySizeInBits along with kSecAttrEffectiveKeySize when defining the query, like in the below code snippet:
let keySize = ... // Key size specified when storing the key, for example: 2048
let query: [String: Any] = [
kSecAttrKeySizeInBits as String: keySize,
kSecAttrEffectiveKeySize as String: keySize,
... // More attributes
]
var dataPtr: CFTypeRef?
let result = SecItemCopyMatching(query as CFDictionary, &dataPtr)
The second solution is to always delete the old key from the keychain (if any) before adding a new key with the same tag.
UPDATE - Alternative Solution
I published this project on GitHub that can be used as an alternative to the above class.
References
A Layman’s Guide to a Subset of ASN.1, BER, and DER
RFC 5280 (X.509 v3)
RFC 8017 (PKCS #1 v2.2)
Some code I found here inspired me when creating the lengthField(...) function.
Java requires a public key encoded in DER format. Unfortunately iOS does not support this standard format and it is needed an additional conversion (I do not know if this will have improved in the latest versions of swift)
See my answer here You can convert the key using CryptoExportImportManager
func exportPublicKeyToDER(keyId:String) -> NSData?{
let publicKey = loadKeyStringFromKeyChainAsNSData(PUBLIC_KEY + keyId)
let keyType = kSecAttrKeyTypeRSA
let keySize = 2048
let exportImportManager = CryptoExportImportManager()
if let exportableDERKey = exportImportManager.exportPublicKeyToDER(publicKey, keyType: keyType as String, keySize: keySize) {
return exportableDERKey
} else {
return nil
}
}
It's my first time dealing with Password hashing in a web application.
I used https://www.codeproject.com/articles/704865/salted-password-hashing-doing-it-right for theory and copied a sample from https://github.com/defuse/password-hashing.
In my understanding, the salt should be unique for every account. So my question would be:
why is the salt generated in this method:
public static String createHash(char[] password)
throws CannotPerformOperationException
{
// Generate a random salt
SecureRandom random = new SecureRandom();
byte[] salt = new byte[SALT_BYTE_SIZE];
random.nextBytes(salt);
// Hash the password
byte[] hash = pbkdf2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);
int hashSize = hash.length;
// format: algorithm:iterations:hashSize:salt:hash
String parts = "sha1:" +
PBKDF2_ITERATIONS +
":" + hashSize +
":" +
toBase64(salt) +
":" +
toBase64(hash);
return parts;
}
What I would Need is a function which stores a hashed password and the used salt from a database. How can I retrieve the used salt from here?
System.out.println(salt);
Always writes
[B#29453f44
In the console. Why is this the case? And what data type would I Need to store the salt in the mysql database? Or do I have the wrong Approach?
If I understand your questions correctly then:
In my understanding, the salt should be unique for every account.
byte[] salt = new byte[SALT_BYTE_SIZE];
random.nextBytes(salt);
Generates a random salt, which makes it unique.
You could also use use the ID for the user from the database or something else unique, but a randomly generated salt is also unique, since afterall, for each new user a new salt is randomly generated.
This salt is then in your code concatenated together with the hash, the hashSize, the algorithm and the number of iterations into parts
// format: algorithm:iterations:hashSize:salt:hash
String parts = "sha1:" +
PBKDF2_ITERATIONS +
":" + hashSize +
":" +
toBase64(salt) +
":" +
toBase64(hash);
return parts;
Often you know the lengths (byte size) of the different parts in parts and can thus extract the part you need. In your case you have even added a : as a separator which makes it even simpler to extract the part you are interested in.
And what data type would I Need to store the salt in the mysql database?
Once you have gotten your parts, this is what you save in the database as text (varchar or char). You do not separate it and store salt separately. Just mash it all in together.
When a user then wants to sign in, they provide a password. Now you fetch parts for the user from the database, you extract the salt, number of iterations and so on from parts, since afterall, you know exactly how it is concatenated. Then you use that information to hash the inputted password from the user again. Now you compare the new hash, with the old hash. If they are the same, well, then the user gave the correct password, if not, he didn't.
Always writes [B#29453f44 In the console.
As #JonSkeet said, the answer is given in Converting String to Sha-256 Hash
I have java application that sending(HTTP post method) credit card numbers to the supplier via the XML.
When writing the XML file, currently credit card number also printing in the file. But it is not good practice to write the credit card numbers directly in to the file without encrypting.
I do not want to encrypt the entire XML, because we are sending the secure information through HTTPS link and hence I assume it provide necessary security. So I want to encrypt only the credit card number element while writing to the file in the Java environment.
String xmlRequest = <?xml version='1.0'?>
<ReservationRequest>
<Passenger>
<firstName>XXX</firstName>
<lastName>YYY</lastName>
</Passenger>
<CreditCard Currency='USD'>
<Number>1234567812345678</Number>
<cvv>123</cvv>
<Expiration>12/12</Expiration>
</CreditCard>
</ReservationRequest>
Please note that I want to write to file the same string that send to the supplier.
String reqXml = "" ;
String creditCardNumber = "1234567890128899" ;
reqXml = "<RentalPaymentPref>" +
"<PaymentCard CardType=\"1\" CardCode=\"VI\" CardNumber=\""+creditCardNumber+"\" ExpireDate=\"0912\" SeriesCode=\"123\">" +
"<CardHolderName>Ruchira kariyawasam</CardHolderName>" +
"</PaymentCard>" +
"</RentalPaymentPref>" ;
StringBuffer reqXmlString = new StringBuffer(reqXml.toString());
short startIndex = (short)(reqXml.indexOf("CardNumber")+12);
if(null!=creditCardNumber && (!creditCardNumber.equals("")))
{
reqXmlString.replace(startIndex,(startIndex+12),"xxxxxxxxxxxx");
}
System.out.println("reqXmlString---->"+reqXmlString);
// Remaining file writing code goes here.
Here check whether card number is blank or empty. If we get the card number, then only mask the string content while writing to the file.
Output
<RentalPaymentPref>
<PaymentCard CardType="1" CardCode="VI" CardNumber="xxxxxxxxxxxx8899" ExpireDate="0912" SeriesCode="123">
<CardHolderName>Ruchira kariyawasam</CardHolderName>
</PaymentCard>
</RentalPaymentPref>
How can encrypt the data base fields when using the hibernate?
We have developed the product some of the clients are using that application Some clients is asking about the data base encryption
Is there any possible to encrypt the data in application level with out more changes in the code.
Please give me the suggestion as soon as possible.
Try this:
Put an attribute in your entity:
private byte[] encryptedBody;
Use this getter and setters:
#Column(columnDefinition= "LONGBLOB", name="encryptedBody")
#ColumnTransformer(
read="AES_DECRYPT(encryptedBody, 'yourkey')",
write="AES_ENCRYPT(?, 'yourkey')")
public byte[] getEncryptedBody() {
return encryptedBody;
}
public void setEncryptedBody(byte[] encryptedBody) {
this.encryptedBody = encryptedBody;
}
And then when you retrive the column use:
private final Charset UTF8_CHARSET = Charset.forName("UTF-8");
String decodeUTF8(byte[] bytes) {
return new String(bytes, UTF8_CHARSET);
}
String s = decodeUTF8(entity.getEncryptedBody());
BEWARE: AES_DECRYPT and AES_ENCRYPT belong to MySQL. If you have a different data base engine find similar functions.
Hope this helps.
You can use the #ColumnTransformer annotation like this:
#ColumnTransformer(
read = "pgp_sym_decrypt(" +
" storage, " +
" current_setting('encrypt.key')" +
")",
write = "pgp_sym_encrypt( " +
" ?, " +
" current_setting('encrypt.key')" +
") "
)
#Column(columnDefinition = "bytea")
private String storage;
This way, Hibernate will be able to encrypt the entity attribute when you persist or merge it and decrypt it when you read the entity.
I think that you are looking for column transformers. You can find how to do it in the Hibernate reference:
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/mapping.html#mapping-column-read-and-write
I hope that helps!
You could use jasypt. It has an Hibernate integration that allows you to encrypt properties while saving (and decrypt while loading).
http://www.jasypt.org/hibernate.html