Showing posts with label encryption. Show all posts
Showing posts with label encryption. Show all posts

Friday, August 31, 2018

WSSE - XML SOAP security and key encryption and storing (EncryptedData/EncryptedKey)

Leave a Comment

I have now spent the last couple of days to find documentation about this..

I need to send a XML via SOAP with the WSSE security header, but don't know how to encrypt and store the encrypted keys

Here is an example

<?xml version="1.0" encoding="UTF-8" standalone="no"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">     <soap:Header>         <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="1">             <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="EK-1B758D26C51BFCD86614340101135741">                 <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>                 <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">                     <wsse:SecurityTokenReference>                         <wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">MIIDODCCAiCgAwIBAgIGAU0FlCVCMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxCYW5rIENvbm5lY3QxFTATBgNVBAsTDEJhbmsgQ29ubmVjdDEdMBsGA1UEAxMUQmFuayBDb25uZWN0IElBLXRlc3QwHhcNMTUwNDI5MTQyODI0WhcNMTgwNDI5MTQyODI0WjAcMRowGAYDVQQDExFiYW5rIGNvbm5lY3QtdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI23KdtaRKPTFTe/A1PnsF9dpSlTiXurKmio0OCgTP9wClHwync3JsInRwGTooA20P9zWobUnEFbEiAgRVYCxuYoldRE6NLhSC854/YTjMBeevH1TNa38lpavGiI4UwFhg70U9/JuYs21hoFyzVfaWlVfOkAMm1U/n4wHq6FZW461S5PY4A/UI1Mr8WgeIHU9GqMBtFvjynzq3SLenOPgdmKtyJ3V8EOU+DlgwKmDbxMVMtYNDZtoQvOWnuvlJ6ICDcqcW7OUkmwCKodjxxPvrdaPxyZDhT7h4FgRtrAOS8qR6L7x9D4ZIoxOMPudGvr99OSb4KVtaAEt/R7hKxG3OsCAwEAAaNCMEAwHwYDVR0jBBgwFoAU680YSkZnx1IaJAmI49LlTGiia0wwHQYDVR0OBBYEFMaWOY7Vf/iB3WVA96j5kRtbF8prMA0GCSqGSIb3DQEBCwUAA4IBAQAJ+bssSFWE6KsYT7HSDKag4Eot7yNGMY4Don/MilDnOREdu20QUS131DKrSkpBQiCXbyRUQjUoun4yue0EG+rlG3QUIlNNdJ4KZJB+dTYdLUV7XTYJNPimKAmoZ+PFNvT1eGgWcMT+MbTfpk0mw0V8IprYGa8UPchd6vtSVwpbTcPc/F4bgUTlm/V+FG4bQS61gF0koj0DEZjzat7CBHpozRgfRlXgwu26vnhWGc99uKH4GAKN4JpqPi/6Yz+7iQNJUC3yeezgBxFrIXuLpkBZSP4zunf9VxsICnxkFUXOTuYBdcbhPNzqMknD5ijFcFRZITwdv7x3uJGLkM7iUfBp</wsse:KeyIdentifier>                     </wsse:SecurityTokenReference>                 </ds:KeyInfo>                 <xenc:CipherData>                     <xenc:CipherValue>af9+FhA91ytLwjeRvTYJsRCkhjHmAQGwqYwMBoNZBn7BZhF/a6EUpM9ByarVhx1SRCpjW5fb8tBVuJO1ZkjfTUZ5EAh/oDLbkmwPdSAAVzmAURHwCq3XQgMZV3lAczlLnPamxjjZBCGqxvAmBo1CvFFPC4AcBedqY92mP8XGyVHpS7JYKOxqXK2vUA1by7371x+Mu0aoS2zJPyPLa1IPwOYgR9qicmWz1RNPiEVA8ZBCN0NRyg7FLJxdUcE81z+1SjButBo2j3qcwkNcecHzZAnweY+LSWp3H5JA3WNzUHUuvFHEaPzT5jd7fUI16xo8NLK8/Rd8Eq/zDD+T3baeVQ==</xenc:CipherValue>                 </xenc:CipherData>                 <xenc:ReferenceList>                     <xenc:DataReference URI="#ED-1B758D26C51BFCD86614340101135852"/>                 </xenc:ReferenceList>             </xenc:EncryptedKey>         </wsse:Security>         <technicalAddress xmlns="http://example.com/schema/2014" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#"/>         <activationHeader xmlns="http://example.com/schema/2014" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#">             <organisationIdentification>                 <mainRegistrationNumber>8079</mainRegistrationNumber>                 <isoCountryCode>DK</isoCountryCode>             </organisationIdentification>             <functionIdentification>112233445566778899</functionIdentification>             <erpInformation/>             <endToEndMessageId>d28b6a7dad414014a59029ef1a7e84d4</endToEndMessageId>             <createDateTime>2015-06-11T10:08:33.258+02:00</createDateTime>         </activationHeader>     </soap:Header>     <soap:Body>         <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="ED-1B758D26C51BFCD86614340101135852" Type="http://www.w3.org/2001/04/xmlenc#Content">             <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>             <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">                 <wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey">                     <wsse:Reference URI="#EK-1B758D26C51BFCD86614340101135741"/>                 </wsse:SecurityTokenReference>             </ds:KeyInfo>             <xenc:CipherData>                 <xenc:CipherValue>dTSVuEJ90OYguQOsOz2ZtcE2mybwuvVl19pp7/e5yuvNygx3w5v+prpEvbjYLauiIAB3lrVDK2astJeYJGnDbaVJVeU0YqH5ItYVn7Wz36jJM52KB+UNbYo8EdTKYjsZuADzH+tAoA+pwYxGBXMEQctNI+C711HgP2hbpHNYOG7nAMOIrP/0B3FCy+st+9CbYlwAEENreTYunEEA41hciFnWCsIx0el7OeuiA6V51fAmvrF19RPNKwaptvbvmVdKj//RQ/0U1kRny16mDnFfX92bI3HBQm4XJA0nEfSvio7EUAAdhe77GMfu7+JELqXNowPGPLlvrbCFYnQhxGRITHtTIEbtJA6MKtBzHgjtw5pt7oWxKgGUnaJTfOPOSv43RLFGggkT/+gTjnZOagu8hhXp0x5HXJuZzw90aIS3jAfSPDc2ivct4WhWk0wcuQyC2rAh4I7gtiR+LqJJGqvucw4S+NR95FunKHKEW4yasKW1oU31/rRbp4Bmwo6BPsQlxnaSHPtk68IVkYDBslz1A5gOP+M/Iam2WI02y6sE/7aAH1ruN3pZlVuYFc3JDNHOPOvevP110d60lroknGdc9vxcFfj48OCKw/8Ed6tiXtAvk0Qu9Qt4ZyLUoPKIWEqjdLjwVadTDJQFAxRptNgiCos7s0czadUu7FNCRxfndjDxhA7trvys44ufEyK++YzZIgNu3r4dywNI22Nm+JZtLj+rX8ARE6FTPlxGBD0SSdXsfCfY2N1ytBBHQRnPsVaHK1p7KOhwQVbqEupcGyvaRolnymOzDLGFdS06OGYFrYXdgIbuqYtZP8QerXtUl0sWNAvvqHSPCQcpKecpMEecar+FUVwLEA+H1wzOprCMbRR+EgIboeDqQ7GxXqugkuFyvnlLDgxnaWhEhQb/5kAcQmnyUZ57MhDcUJqqQ4Cdmwrcxho1P+YqWY9yn0E86F+hl5976a/gH5KBobB84OWmgcX42eAmqpJf+8c8SuBv+7NctbQOk21aYlFEpkwSme/kG1/edtyoHQH/hF0RB1cT8g+u9S9AK2rs3s2G+Ap0U5oyY8pqJalGdZSBudE0sU4mhOV8trtx0FrN9A7pNkTcGPH25nCtyIz6rzR+DP8Mtgw5385s5ivVlDb+z74Wbh6iu7ZkVAogNTpUYU/1BxDXWJqFMkFmfziNxQ5AQqm1vGlBzXifoQkUFX1riutNphmu0Hs+7KMmMLvtW2cXmQDpkHFKVheeN4w7pBCEZ8KhZ0VTOwRZcdvrNcpYfXM13/QdTHQmCqqwgS/VvlUFz7PDn0/OKo6moUic8W6b1iEvd3kfc7QkunxoOUoJr4RwJ+PqCzN6PxQivAFA2tmDPc8qEa1PAdxTeNFoR/6dNQRojouuJq3C1LrbmGf6lQPvKi3KeKHXyjmDr7Tve+al2tcWJVr+1qEM3/XuthoiZbuTDxYUjZ2nf2fhHrmNcfvrfNxSNHVdQPp2R9Rf3eGxlRJsmRpef66VbYhOpmiH4xmq45EWiyBZmYm+tZtjsP51EDMIvdFbVRSGO/hMqURrDSsJXJeot27Iup2s0P2n/6a9k0c4SVvf/WXNN5x9JNvjU97bQNDQRfonJmo9pRYYHl1tSqNIYBK7KsMH+qr1vmiJuhrXUuL/RtOKvE9KXQ8kGoC9oF5rFn21z40ElxG5XRTASg==</xenc:CipherValue>             </xenc:CipherData>         </xenc:EncryptedData>     </soap:Body> </soap:Envelope> 

First of all I have never worked with SOAP before so chances I do things wrong has pretty good odds :)

The body of the SOAP request is encrypted with AES-256-CBC, a 16 char random-generated iv and a 16 char random-generated key. My first problem is how the symmetric key is stored in the security header?

Have found something here, but I need more details https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html#aes256-cbc

How are the iv and the key stored in CipherValue in the header?

When sending the XML request to the webservice I get this error

23-08-2018 12:50:02   General exception:Padding is invalid and cannot be removed. 23-08-2018 12:50:02   Stack trace:    at System.Security.Cryptography.CapiSymmetricAlgorithm.DepadBlock(Byte[] block, Int32 offset, Int32 count)    at System.Security.Cryptography.CapiSymmetricAlgorithm.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)    at System.Security.Cryptography.Xml.EncryptedXml.DecryptData(EncryptedData encryptedData, SymmetricAlgorithm symmetricAlgorithm)    at SomeClassCore.XmlSecurity.Decryptor.DecryptData(Byte[] symmetricKey)    at SomeClassCore.SecurityServiceImpl.UnwrapRequest(ServiceRequest serviceRequest)    at BD.BCA.MessageHandler.MessageHandler.ProcessRequest(HttpContext context) 

The way I store the symmetric key is like this

$key_length = 16; $cipher     = 'AES-256-CBC';  $iv     = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher)); $key    = openssl_random_pseudo_bytes($key_length);  // Encryption of the body $encrypted_body = openssl_encrypt($body, $cipher, $key, 0, $iv);  // length 32 chars $symmetric_key = $iv.$key;  // The symmetric key is then encrypted via RSA and the public key.. $public_cert = openssl_pkey_get_public('the public cert'); openssl_public_encrypt($symmetric_key, $encrypted_key, $public_cert, OPENSSL_PKCS1_OAEP_PADDING);  // encrypted key is put in header EncryptedKey $encrypted_key;  // encrypted body is put in EncryptedData $encrypted_body; 

The part with the RSA encryption/decryption seems to work according to the error log right? It fails when decrypting the AES right?

This is how the symmetric key, and the iv and key alone looks like when dumping them with var_dump

symmetric key (not encrypted): string(32) "��J�    v��rb։�3�U���_$6p9�;p" iv: string(16) "��J�    v��rb։�" key: string(16) "3�U���_$6p9�;p" 

Symmetric key (base64 and not encrypted): 1/hKgBsfCXaUn3IaYtaJyzP0EFXK0uJfJDZwOR3AO3A=

update

Have searched a bit more.. Maybe the iv must be a part of the stored data. But it's still not working? Same error as above

class Encryption {     public function data_encrypt(string $data, string $cipher, int $key_length): Array{         $iv     = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));         $key    = openssl_random_pseudo_bytes($key_length);          return [             'data'  => base64_encode($iv.openssl_encrypt($data, $cipher, $key, OPENSSL_RAW_DATA, $iv)),             'key'   => $key         ];     }      public function key_encrypt(string $key): string{         $public_cert = openssl_pkey_get_public('contents of public cert');         openssl_public_encrypt($key, $data, $public_cert, OPENSSL_PKCS1_OAEP_PADDING);         openssl_free_key($public_cert);          return base64_encode($data);     } }  $Enc = new Encryption; $data_encrypted = $Env->data_encrypt('The message I want to encrypt', 'AES-256-CBC', 32);  //  This base64 encoded string goes to <EncryptedData> $data_encrypted['data'];  //  This base64 encoded string goes to <EncryptedKey> in the header $Enc->key_encrypt($data_encrypted['key']); 

0 Answers

Read More

Thursday, July 5, 2018

How to load Cipher encrypted image file in ImageView without saving to device

Leave a Comment

I am creating an application with content security for no one could ever copy the contents and files. I am using cipher to encrypt the image directly from the URL, with out downloading to the device. Please find my code below.

URL url = new URL(images.getImageurl()); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setDoOutput(true); File folder = new File(Environment.getExternalStorageDirectory(), "zerb"); boolean success = true;  if (!folder.exists()){ folder.mkdirs(); } InputStream fis = connection.getInputStream(); String path = folder.getAbsolutePath() + "images.getImageName + ".jpg"; encryptfile(fis, path, AppConstants.password + images.getContentid() + images.getTopicid()) fis.close(); 

And the cipher encryption Method code is

private static boolean encryptfile(InputStream inputStream, String path, String password) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {      FileOutputStream fos = new FileOutputStream(path.concat(".crypt"));     byte[] key = (AppConstants.salt + password).getBytes("UTF-8");     MessageDigest sha = MessageDigest.getInstance("SHA-1");     key = sha.digest(key);     key = Arrays.copyOf(key, 16);     SecretKeySpec sks = new SecretKeySpec(key, "AES");     Cipher cipher = Cipher.getInstance("AES");     cipher.init(Cipher.ENCRYPT_MODE, sks);     CipherOutputStream cos = new CipherOutputStream(fos, cipher);     int b;     byte[] d = new byte[8];     while ((b = inputStream.read(d)) != -1) {         cos.write(d, 0, b);     }     cos.flush();     cos.close();     inputStream.close();     File encryptedFile = new File(path.concat(".crypt"));     return (encryptedFile.exists()); } 

and the decryption code is

 public static void decrypt(String path, String password, String outPath) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {     FileInputStream fis = new FileInputStream(path);     FileOutputStream fos = new FileOutputStream(outPath);     byte[] key = (AppConstants.salt + password).getBytes("UTF-8");     MessageDigest sha = MessageDigest.getInstance("SHA-1");     key = sha.digest(key);     key = Arrays.copyOf(key, 16);     SecretKeySpec sks = new SecretKeySpec(key, "AES");     Cipher cipher = Cipher.getInstance("AES");     cipher.init(Cipher.DECRYPT_MODE, sks);     CipherInputStream cis = new CipherInputStream(fis, cipher);     int b;     byte[] d = new byte[8];     while ((b = cis.read(d)) != -1) {         fos.write(d, 0, b);     }     fos.flush();     fos.close();     cis.close(); } 

If I decrypt the image, it will be shown and can be copied from the device. All I need is to load the encrypted image to an ImageView without saving the decrypted image to the device, so that no one can copy. Please someone help me.

1 Answers

Answers 1

An ImageView can show an in-memory android.graphics.Bitmap which can be read in directly from an InputStream.

For example, the decrypt() method could be adapted to return a Bitmap :

public Bitmap decrypt(String path, String password) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {     FileInputStream fis = new FileInputStream(path);     byte[] key = (AppConstants.salt + password).getBytes("UTF-8");     MessageDigest sha = MessageDigest.getInstance("SHA-1");     key = sha.digest(key);     key = Arrays.copyOf(key, 16);     SecretKeySpec sks = new SecretKeySpec(key, "AES");     Cipher cipher = Cipher.getInstance("AES");     cipher.init(Cipher.DECRYPT_MODE, sks);     CipherInputStream cis = new CipherInputStream(fis, cipher);      Bitmap bitmap = BitmapFactory.decodeStream(cis);     cis.close();      return bitmap; } 

(Although it's called Bitmap, it's fine to decode a .jpg or .png).

Then this can be shown in the ImageView:

ImageView imageView = findViewById(R.id.imageView); Bitmap bitmap = decrypt(path + ".crypt", password); imageView.setImageBitmap(bitmap); 
Read More

Thursday, May 24, 2018

how to securely store encryption keys in android?

Leave a Comment

I want to know how to securely store encryption key in Android? What is the best scenario to protect encryption and secrete keys?

5 Answers

Answers 1

From your comments, you need to encrypt data using a local key for current Android versions and the old ones

Android Keystore is designed to generate and protect your keys. But it is not available for API level below 18 and it has some limitations until API level 23.

You will need a random symmetric encryption key, for example AES. The AES key is used to encrypt and decrypt you data. I'm going to summarize your options to generate and store it safely depending on Android API level.

  • API Level < 18: Android Keystore not present. Request a password to the user, derive an encryption key from the password, The drawback is that you need to prompt for the password when application starts. The encryption key it is not stored in the device. It is calculated each time when the application is started using the password

  • API Level >=18 <23: Android Keystore available without AES support. Generate a random AES key using the default cryptographic provider (not using AndroidKeystore). Generate a RSA key pair into Android Keystore, and encrypt the AES key using RSA public key. Store encrypted AES key into Android SharedPreferences. When application starts, decrypt the AES key using RSA private key

  • API Level >=23: Android Keystore available with AES support. Generate a random AES key using into Android Keystore. You can use it directly.

To encrypt to can use AES/CBC/PKCS7Padding algorithm. It requires also a random initialization vector (IV) to encrypt your data, but it can be public.

Alternatives:

  • API level >14: Android Key Chain: KeyChain is a system-wide credential storage. You can install certificates with private keys that can be used by applications. Use a preinstalled key to encrypt/decrypt your AES key as shown in the second case above.

  • External token: The protected keys are not stored in the device. You can use an external token containing a private/public key pair that allows you to encrypt the AES key. The token can be accesed using bluetooth or NFC

Answers 2

You cannot place the encryption key inside your apk file. You may want to keep it in a remote server and decrypt using server. Or you may make it difficult for others by encoding the key and keeping it in non-obvious places. But there's no bullet proof solution for this.

Answers 3

There is no way to securely save your private api keys into code. But you can use NDK to securely save private keys. It is not trivial to get key from NDK. Secue Key With NDK Example

Answers 4

You can use Android Keystore system to store and retrieve sensitive information. Read this 5 minute article to understand how it works. Using the Android Keystore system to store and retrieve sensitive information

Answers 5

You will need a random symmetric encryption key, for example AES. The AES key is used to encrypt and decrypt you data. I'm going to summarize your options to generate and store it safely depending on Android API level

Read More

Monday, May 7, 2018

litteral search with encrypted data with MYSQL database

Leave a Comment

I use these functions to encrypt / decrypt my data in MySQL Database:

'use strict';  const crypto = require('crypto');  const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY; // Must be 256 bytes (32 characters) const IV_LENGTH = 16; // For AES, this is always 16  function encrypt(text) {   let iv = crypto.randomBytes(IV_LENGTH);   let cipher = crypto.createCipheriv('aes-256-cbc', new Buffer(ENCRYPTION_KEY), iv);   let encrypted = cipher.update(text);    encrypted = Buffer.concat([encrypted, cipher.final()]);    return iv.toString('hex') + ':' + encrypted.toString('hex'); }  function decrypt(text) {   let textParts = text.split(':');   let iv = new Buffer(textParts.shift(), 'hex');   let encryptedText = new Buffer(textParts.join(':'), 'hex');   let decipher = crypto.createDecipheriv('aes-256-cbc', new Buffer(ENCRYPTION_KEY), iv);   let decrypted = decipher.update(encryptedText);    decrypted = Buffer.concat([decrypted, decipher.final()]);    return decrypted.toString(); }  module.exports = { decrypt, encrypt }; 

(from this link)

Now, I'd like to do a litteral search from my database. What is the best way to do it?

For example, I store encrypted names in my database but i'd like to be able to search lines with specific names.

0 Answers

Read More

Wednesday, March 7, 2018

ASPNET_REGIIS: Place AES key and IV into a KeyContainer

Leave a Comment

Is it possible to place an AES key and IV into a KeyContainer using ASPNET_REGIIS? If yes, how?

Context:

I have created AesProtectedConfigurationProvider to encrypt web.config data using AES as opposed to Triple DES (i.e., 3DES). I have also created a console application that uses the AesProtectedConfigurationProvider in order to generate both the AES key and initialization vector (IV). I can save the key to a text file and then reference the text file in the provider of the web.config. From there, I am able to encrypt the web.config file. But, I would like to protect the keys.txt file by moving them into a KeyContainer, if that is possible.

So, under the provider tag, the section for keyContainerName would be:

keyContainerName="AesKeyContainer"  

as opposed to

keyContainerName="C:\AesKey.txt" 

My understanding is the current encryption offering that is available out of the box in ASPNET_REGIIS uses 3DES to encrypt the contents of the web.config file while the RsaProtectedConfigurationProvider is used to encrypt the 3DES keys (please correct me if I am wrong on this). So, if it is possible to use the RsaProtectedConfigurationProvider to encrypt the AES keys into a KeyContainer then that would be helpful. I have reviewed the following sites and I am not sure if this is possible:

https://msdn.microsoft.com/en-us/library/33ws57y0.aspx

How to encrypt web.config with AES instead of 3DES

EDIT: Does anyone know why Microsoft took out the AesProtectedConfigurationProvider in subsequent releases of .NET? This seems like a step backwards as AES is the current standard while 3DES is no longer recommended. In speaking with a colleague, they mentioned that it may have been removed due to a security flaw, such as; elevation of privileges. Microsoft is known for making unannounced changes with respect to security. But, I would like to know if anyone knows for sure. If, indeed, a flaw was found in the AesProtectedConfigurationProvider, then I might be inclined to stay with 3DES.

1 Answers

Answers 1

RsaProtectedConfigurationProvider and AesProtectedConfigurationProvider, despite very similar names, are parts of different universes.

RsaProtectedConfigurationProvider resides in System.Configuration and is used (as other providers inheriting from abstract ProtectedConfigurationProvider) for encryption/decryption of configuration sections in web.config for ASP.NET applications.

AesProtectedConfigurationProvider in its turn resides in Microsoft.ApplicationHost and is used only for IIS configuration encryption. In configuration file of default application pool (DefaultAppPool.config) you will find following:

<configProtectedData>     <providers>         <!-- ... -->         <add name="AesProvider" type="Microsoft.ApplicationHost.AesProtectedConfigurationProvider" ... />         <add name="IISWASOnlyAesProvider" type="Microsoft.ApplicationHost.AesProtectedConfigurationProvider" ... />     </providers> </configProtectedData> 

You could read about AesProvider and IISWASOnlyAesProvider in IIS Securing Configuration article:

AesProvider - Encrypting IIS configuration sections read by the IIS worker process using AES encryption.

IISWASOnlyAesProvider - Encrypting IIS configuration sections read by WAS using AES encryption.

So answering your first question:

  1. Confirm whether using the AesProtectedConfigurationProvider is safe. It was removed by Microsoft in subsequent releases of .NET but I cannot seem to find a reason

Yes, using of your custom AES provider is safe if we assume that you have implemented it correctly without security flaws. Microsoft has not removed AesProtectedConfigurationProvider from .Net Framework, it was never a part of System.Configuration. If Microsoft has found security flaw in its implementation, they could just fix it instead of removing, correct?

  1. Provide steps to implement the AesProtectedConfigurationProvider and to create a KeyContainer in ASPNET_REGIIS

I believe you can have AES encryption without implementing custom AesProtectedConfigurationProvider.

I dig into source code of RsaProtectedConfigurationProvider and found that it has the following logic:

private SymmetricAlgorithm GetSymAlgorithmProvider() {     SymmetricAlgorithm symAlg;      if (UseFIPS) {         // AesCryptoServiceProvider implementation is FIPS certified         symAlg = new AesCryptoServiceProvider();     }     else {         // Use the 3DES. FIPS obsolated 3DES         symAlg = new TripleDESCryptoServiceProvider();          byte[] rgbKey1 = GetRandomKey();         symAlg.Key = rgbKey1;         symAlg.Mode = CipherMode.ECB;         symAlg.Padding = PaddingMode.PKCS7;     }      return symAlg; } 

As you see, default RSAProtectedConfigurationProvider supports switch from Triple DES to AES encryption by means of System.Security.Cryptography.AesCryptoServiceProvider.

UseFIPS flag is read from configuration section of RsaProtectedConfigurationProvider. You could set it on machine level (machine.config) so that it's applied to all encrypted configs or only for specific web.config.

For later case add following section to web.config (I have copied the section from machine.config and added useFIPS="true"):

<configuration>    <!-- ... -->    <configProtectedData>     <providers>       <remove name="RsaProtectedConfigurationProvider"/>       <add name="RsaProtectedConfigurationProvider"            type="System.Configuration.RsaProtectedConfigurationProvider,System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"            description="Uses RsaCryptoServiceProvider to encrypt and decrypt"            keyContainerName="NetFrameworkConfigurationKey"            cspProviderName=""            useMachineContainer="true"            useOAEP="false"            useFIPS="true"            />     </providers>   </configProtectedData>    <!-- ... -->  </configuration> 

Now if you run aspnet_regiis, you will see that data is encrypted with 256 bit AES:

<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" /> 

The AES symmetric key is stored in the same way as for Triple DES mode: the key is encrypted with RSA and is embedded into encrypted section while RSA key is stored in machine key container. See this blog post for more details.

I believe using of AES encryption that is already implemented in RsaProtectedConfigurationProvider is far better option than custom AES provider. You are using existing key storing method and you are protected from possible (highly probable) security flaws.

Read More

Tuesday, January 23, 2018

ruby open ssl api for encrypted key (without nodes option)

Leave a Comment

On a linux machine with openssl lib installed, when you perform “openssl pkcs12” with “-nodes” option then you get output with unencrypted private key but if you skip the –nodes option then output will have encrypted private key.

     e.g.              openssl pkcs12 -in test.pfx -out test.pem  

You should see private key encrypted like below

-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFDjBABgkqhkiGG7s=
-----END ENCRYPTED PRIVATE KEY-----

How do I achive the above using ruby's open ssl library?

This is how I am generating the private key with ruby:

    @private_key = OpenSSL::PKey::RSA.new 2048     @private_key.to_pem.to_s 

EDIT:

I guess my question how does this command encrypt the private key:

openssl pkcs12 -in test.pfx -out test.pem

whereas:

“openssl pkcs12 -nodes -in test.pfx -out test.pem"

does not. How do i get the same results using ruby?

1 Answers

Answers 1

There must be something more to this. Perhaps the question needs refined. From what I can tell, you want to print a private key in PEM format. I'm not a ruby programmer but I got this working in about 3 minutes by reading the on ruby-docs.org:

$ cat ssl.rb  require 'openssl' key = OpenSSL::PKey::RSA.new 2048 cipher = OpenSSL::Cipher.new 'AES-128-CBC' pass_phrase = 'my secure pass phrase goes here' key_secure = key.export cipher, pass_phrase puts key_secure 

Choose your cipher and your passphrase and voila, you have encrypted a key with a symmetric cipher:

$ ruby ssl.rb -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-128-CBC,D8062F93C8854E593602D503E1FAC309  UsNQt/Bq7QBldOBU7NW6miCDuC+ODpeplaWQ9BvJW4Wg7j0AbKgMZAn7juegAbjG JVkpdDNzhs37UVWmqwg64yYP6KEBGg4zCog2a993UHRvFTQb6tyugKHc+uFeyY+D ... -----END RSA PRIVATE KEY----- 

Is there something more you're trying to do?

--

EDIT: The Asker has clarified that the question is about which encryption is used by openssl pkcs12 when not using the -nodes option.

The -nodes option turns off DES encryption. This page shows that the default encryption for private key creation is triple DES, but I don't see the detail there about which DES option. This is also referred to in the manual for man pkcs12. In any case, it's not yet clear to me if you need to know exactly which cipher is used by openssl or if your question is now answered.

You can list ruby's available ciphers like this:

puts OpenSSL::Cipher.cipher  

(as specified in the docs)

On my system, quite a few DES ciphers are available with varying parameters:

des des-cbc des-cfb des-cfb1 des-cfb8 des-ecb des-ede des-ede-cbc des-ede-cfb des-ede-ofb des-ede3 des-ede3-cbc des-ede3-cfb des-ede3-cfb1 des-ede3-cfb8 des-ede3-ofb des-ofb des3 desx desx-cbc 

I'm not sure if the PKCS12 spec says which DES cipher should be used or if openssl just has a default. Are you trying to figure out which DES cipher is used by OpenSSL?

I looked a bit further through the openssl wiki and I found references to DES in CBC mode, so I'm guessing that the cipher you are looking for is DES-EDE3-CBC.

Sorry, I guess it's still not completely clear exactly what question you're trying to answer. If what you're trying to do is to find out exactly how openssl is encoding the private key when you don't use -nodes you can see that by cating the file into openssl asn1parse. Maybe this helps: you can start with your rsa private key in plain text as you got it from your ruby program:

$ cat key.pem -----BEGIN RSA PRIVATE KEY----- MIIEpA.... -----END RSA PRIVATE KEY $ cat key.pem | openssl asn1parse     0:d=0  hl=4 l=1188 cons: SEQUENCE           4:d=1  hl=2 l=   1 prim: INTEGER           :00 7:d=1  hl=4 l= 257 prim: INTEGER       ... 

You'll see that this is encoded as an ASN1 SEQUENCE of long integers.

But if you put your key through a roundtrip of pkcs12:

$ openssl pkcs12 -inkey key.pem -out key.pfx -export -nocerts -nodes (choose a password) $ openssl pkcs12 -in key.pfx -out outkey.pem -nodes (enter password) 

You'll find your key now wrapped in a new structure, which you can cat to asn1parse:

cat keyout.pem | openssl asn1parse     0:d=0  hl=4 l=1214 cons: SEQUENCE               4:d=1  hl=2 l=   1 prim: INTEGER           :00     7:d=1  hl=2 l=  13 cons: SEQUENCE               9:d=2  hl=2 l=   9 prim: OBJECT            :rsaEncryption 

You now have an ASN1 rsaEncryption Object with a payload of a large octet string.

Is this what you were trying to learn about? How these values are encapsulated in ASN1?

Read More

Saturday, January 6, 2018

Encode and decode pathname in nginx

Leave a Comment

Normally files can be accessed at:

http://example.com/cats/cat1.zip

I want to encode/encrypt the pathname (/cats/cat1.zip) so that the link is not normally accessible but accessible after the pathname is encrypted/encoded:

http://example.com/Y2F0cy9jYXQxLnppcAo=

I'm using base64 encoding above for simplicity but would prefer encryption. How do I do about doing this? Do I have to write a custom module?

4 Answers

Answers 1

If your only concern is limiting access to certain URLs you may take a look at this post on Securing URLs with the Secure Link Module in Nginx.

It provides fairly simple method for securing your files — the most basic and simple way to encrypt your URLs is by using the secure_link_secret directive:

server {      listen 80;     server_name example.com;      location /cats {         secure_link_secret yoursecretkey;         if ($secure_link = "") { return 403; }           rewrite ^ /secure/$secure_link;     }       location /secure {         internal;         root /path/to/secret/files;     } } 

The URL to access cat1.zip file will be http://example.com/cats/80e2dfecb5f54513ad4e2e6217d36fd4/cat1.zip where 80e2dfecb5f54513ad4e2e6217d36fd4 is the MD5 hash computed on a text string that concatenates two elements:

  1. The part of the URL that follows the hash, in our case cat1.zip
  2. The parameter to the secure_link_secret directive, in this case yoursecretkey

The above example also assumes the files accessible via the encrypted URLs are stored at /path/to/secret/files/secure directory.

Additionally, there is a more flexible, but also more complex method, for securing URLs with ngx_http_secure_link_module module by using the secure_link and secure_link_md5 directives, to limit the URL access by IP address, define expiration time of the URLs etc.

If you need to completely obscure your URLs (including the cat1.zip part), you'll need to make a decision between:

  1. Handling the decryption of the encrypted URL on the Nginx side — writing your own, or reusing a module written by someone else
  2. Handling the decryption of the encrypted URL somewhere in your application — basically using Nginx to proxy your encrypted URLs to your application where you decrypt them and act accordingly, as @cnst writes above.

Both approaches have pros and cons, but IMO the latter one is simpler and more flexible — once you set up your proxy you don't need to worry much about Nginx, nor need to compile it with some special prerequisites; no need to write or compile code in language other than what you already are writing in your application (unless your application includes code in C, Lua or Perl).

Here's an example of a simple Nginx/Express application where you'd handle the decryption within your application. The Nginx configuration might look like:

server {      listen 80;     server_name example.com;      location /cats {         proxy_set_header Host $http_host;         proxy_set_header X-Real-IP $remote_addr;         proxy_set_header X-Forwarded-For $remote_addr;         proxy_set_header X-NginX-Proxy true;         proxy_http_version 1.1;         proxy_set_header Upgrade $http_upgrade;         proxy_set_header Connection "upgrade";         proxy_pass http://127.0.0.1:8000;     }      location /path/to/secured/files {         internal;     } } 

and on the application (Node.js/Express) side you may have something like:

const express = require ('express'); const app = express();  app.get('/cats/:encrypted', function(req, res) {   const encrypted = req.params.encrypted;    //    // Your decryption logic here   //   const decryptedFileName = decryptionFunction(encrypted);    if (decryptedFileName) {     res.set('X-Accel-Redirect', `/path/to/secured/files/${decryptedFileName}`);   } else {     // return error   } });   app.listen(8000); 

The above example assumes that the secured files are located at /path/to/secured/files directory. Also it assumes that if the URL is accessible (properly encrypted) you are sending the files for download, but the same logic would apply if you need to do something else.

Answers 2

The easiest way would be to write a simple backend (with interfacing through proxy_pass, for example) that would decrypt the filename from the $uri, and provide the results within the X-Accel-Redirect response header (which is subject to proxy_ignore_headers in nginx), which would subsequently be subject to an internal redirect within nginx (to a location that cannot be accessed without going through the backend first), and served with all the optimisations that are already part of nginx.

location /sec/ {     proxy_pass http://decryptor/; } location /x-accel-redirect-here/ {     internal;     alias …; } 

The above approach follows the ‘microservices’ architecture, in that your decryptor service's only job is to perform decryption and access control, leaving it up to nginx to ensure files are served correctly and in the most efficient way possible through the use of the internal specially-treated X-Accel-Redirect HTTP response header.

Answers 3

Consider using something like OpenResty with Lua.

Lua can do almost everything you want in nginx.

https://openresty.org/

https://github.com/openresty/

Answers 4

You can use a Nginx rewrite rule rewrite the url (from encoded to unencoded). And, to apply your encoding logic you can use a custom function (I did it with the perl module).

Could be something like this:

 http {   ...     perl_modules perl/lib;     ...     perl_set $uri_decode 'sub {       my $r = shift;       my $uri = $r->uri;       $uri = perl_magic_to_decode_the_url;       return $uri;     }';     ...     server {     ...       location /your-protected-urls-regex {         rewrite ^(.*)$ $scheme://$host$uri_decode;       } 
Read More

Friday, January 5, 2018

Decrypting EnvelopedCms with non-default AlgorithmIdentifier

Leave a Comment

I'm trying to decrypt a EnvelopedCms that was encrypted using a non-default AlgorithmIdentifier like this:

ContentInfo contentInfo = new ContentInfo(data); EnvelopedCms envelopedCms = new EnvelopedCms(contentInfo, new AlgorithmIdentifier(new System.Security.Cryptography.Oid("2.16.840.1.101.3.4.1.42"))); CmsRecipientCollection recipients = new CmsRecipientCollection(SubjectIdentifierType.IssuerAndSerialNumber, certificates); envelopedCms.Encrypt(recipients); byte[] encryptedData = envelopedCms.Encode(); 

The encryption works as expected. Now when I try to decrypt the envelopedCms using something like this:

EnvelopedCms envelopedCms = new EnvelopedCms(); envelopedCms.Decode(encryptedData ); envelopedCms.Decrypt(certificates); byte[] decryptedData = envelopedCms.ContentInfo.Content; 

I notice that a.) the access to the certificate takes quite long (longer then when using the default AlgorithmIdentifier) and b.) I get this error message:

System.Security.Cryptography.CryptographicException: Access denied 

Which, looking at the source where this fails, is probably not the issue. Now I see that I haven't provided the encryption algorithm to use in the decrypt method anywhere and I believe that's the issue but I'm not sure how to specify the AlgorithmIdentifier for the decryption.

From the documenation I can see that the EnvelopedCms.Decrypt can take a RecipientInfo which can specify the algorithm but since we used multiple certificates for the encryption I'm not sure how to build such an object. Ideally I was thinking that the correct RecipientInfo, containing the actually used AlgorithmIdentifier, is already contained in the EnvelopedCms.RecipientInfos array. But it seems like this is not the case.

Can anyone get the decrypt code above working?

1 Answers

Answers 1

Although my answer may lead to some incomplete tangents, I believe that it will get you the same assertion that I have come to. The fact is that I use a X509Store allows me to locate the certificates that my machine has. I then pass the collection into the CmsReceipientCollection with a X509Certificate2Collection that is found from my store.Certificates. This method takes 128ms to execute. HTH!

 [TestMethod]     public void TestEnvelopedCMS()     {         X509Store store = new X509Store("MY", StoreLocation.CurrentUser);         store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);          X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;         X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);          byte[] data = new byte[256];         //lets change data before we encrypt         data[2] = 1;          ContentInfo contentInfo = new ContentInfo(data);         EnvelopedCms envelopedCms = new EnvelopedCms(contentInfo, new AlgorithmIdentifier(new System.Security.Cryptography.Oid("2.16.840.1.101.3.4.1.42")));         CmsRecipientCollection recipients = new CmsRecipientCollection(SubjectIdentifierType.IssuerAndSerialNumber, fcollection);         envelopedCms.Encrypt(recipients);         byte[] encryptedData = envelopedCms.Encode();          //lets decrypt now         envelopedCms.Decode(encryptedData);         envelopedCms.Decrypt(fcollection);         byte[] decryptedData = envelopedCms.ContentInfo.Content;           //grab index from byte[]         var item = decryptedData.Skip(2).Take(1).FirstOrDefault();         var item2 = data.Skip(2).Take(1).FirstOrDefault();          Assert.IsTrue(item == item2);     } 
Read More

Saturday, October 21, 2017

Decrypted String always returning Null

Leave a Comment

Data fetched from server using

var request = var request = URLRequest(url: URL(string: "http://www.example.com/test.php")!)            request.httpMethod = "POST"            let akey:String =  txt_key.stringValue;            let email:String = txt_email.stringValue            let VAL:String="test"               var data="blah"            let postString = data            request.httpBody = postString.data(using: .utf8)            let task = URLSession.shared.dataTask(with: request) { data, response, error in                guard let data = data, error == nil else {                                                 /                    print("error=\(error)")                    return                }                if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {           /                    print("statusCode should be 200, but is \(httpStatus.statusCode)")                    print("response = \(response)")                }                let responseString = String(data: data, encoding:  .utf8)                print(responseString)            }            task.resume()    

Decryption using

   func aesDecrypt(key:String, iv:String, options:Int = kCCOptionPKCS7Padding) -> String? {         if let keyData = key.data(using: String.Encoding.utf8),             let data = NSData(base64Encoded: self, options: .ignoreUnknownCharacters),             let cryptData    = NSMutableData(length: Int((data.length)) + kCCBlockSizeAES128) {              let keyLength              = size_t(kCCKeySizeAES128)             let operation: CCOperation = UInt32(kCCDecrypt)             let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)             let options:   CCOptions   = UInt32(options)              var numBytesEncrypted :size_t = 0              let cryptStatus = CCCrypt(operation,                                       algoritm,                                       options,                                       (keyData as NSData).bytes, keyLength,                                       iv,                                       data.bytes, data.length,                                       cryptData.mutableBytes, cryptData.length,                                       &numBytesEncrypted)              if UInt32(cryptStatus) == UInt32(kCCSuccess) {                 cryptData.length = Int(numBytesEncrypted)                 let unencryptedMessage = String(data: cryptData as Data, encoding:String.Encoding.utf8)                 return unencryptedMessage             }             else {                 return nil             }         }         return nil } 

Even when responseString is not null,The following code always returns optional("")

 let unencode = String(describing: responseString!).aesDecrypt(key: "123456789012asdsadasd", iv: "iv-salt-string--") 

Why is this happening ? Please advice.

1 Answers

Answers 1

Per MartinR's comment:

I have verified that "JDoNBXk21oJ9x15Bkv12uw==" is exactly the result of encrypting an empty string with your key and iv. The error is in your PHP script, not in the Swift code.

Therefore you need to resolve the error on the server.

Read More

Tuesday, September 26, 2017

Publish from Visual Studio and automatically encrypt appSettings using aspnet_regiis

Leave a Comment

I have web application that is deployed to my local IIS server under Sites\Default, it works fine and right now I want to make it more secure - I want to encrypt connection strings and appSettings.
Inside pubxml file I've added this line:

<MSDeployEnableWebConfigEncryptRule>true</MSDeployEnableWebConfigEncryptRule> 

but that only encrypts connections strings. I know that I can manually call:

C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis -pe "appSettings" -site Default -app "/" 

on my server after deploy to encrypt external file containing appSettings, but I must do that manually.

My question is how can I deploy website from Visual Studio (Build > Publish) and have that aspnet_regiis command execute automatically after publish succeeded.

I found information that I could use runcommand and other about bat files, but I'm not calling MSDeploy from command line.
I've also found information that I should build custom provider and call it from MSDeploy.

How should I edit my pubxml file to get this behaviour?

EDIT1:
I've managed to hook After Deploy Target using:

<Target Name="EncryptAppSettings" AfterTargets="MSDeployPublish" >   <Message Text="Encrypting appSettings" />   <Exec Command="aspnet_regiis -pe &quot;appSettings&quot; -site Default -app &quot;/&quot;" />   <Message Text="AppPath: $(DeployIisAppPath)" /> </Target> 

But now I get this error:

The command "aspnet_regiis -pe "appSettings" -site Default -app "/"" exited with code 9009.

EDIT2:
I've tried using runCommand like this:

<ItemGroup>   <MsDeploySourceManifest Include="runCommand">     <path>aspnet_regiis -pe &quot;appSettings&quot; -site Default -app &quot;/&quot;</path>     <waitInterval>10000</waitInterval>     <AdditionalProviderSettings>waitInterval</AdditionalProviderSettings>   </MsDeploySourceManifest> </ItemGroup> 

but I had no luck. I found blog about postSync:runCommand, but I'd like to call that directly from VS so I'd like to add that to publish profile.

EDIT3:
I'm adding my publish profile below:

<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">   <PropertyGroup>     <WebPublishMethod>MSDeploy</WebPublishMethod>     <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>     <LastUsedPlatform>Any CPU</LastUsedPlatform>     <SiteUrlToLaunchAfterPublish />     <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>     <ExcludeApp_Data>False</ExcludeApp_Data>     <MSDeployServiceURL>192.168.5.50</MSDeployServiceURL>     <DeployIisAppPath>Default</DeployIisAppPath>     <RemoteSitePhysicalPath />     <SkipExtraFilesOnServer>False</SkipExtraFilesOnServer>     <MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>     <EnableMSDeployBackup>True</EnableMSDeployBackup>     <MSDeployEnableWebConfigEncryptRule>True</MSDeployEnableWebConfigEncryptRule>     <UserName>LocalAdmin</UserName>     <_SavePWD>True</_SavePWD>     <PublishDatabaseSettings>       <Objects xmlns="">         <ObjectGroup Name="ApplicationDbContext" Order="1" Enabled="False">           <Destination Path="Data Source=192.168.5.51;Initial Catalog=GameBit;User ID=GUser;Password=MyRealPassword;Application Name=EntityFramework" Name="Data Source=192.168.5.51;Initial Catalog=GameBit;User ID=GUser;Password=MyRealPassword;MultipleActiveResultSets=True;Application Name=EntityFramework" />           <Object Type="DbCodeFirst">             <Source Path="DBContext" DbContext="Api.ApplicationDbContext, Api" Origin="Configuration" />           </Object>         </ObjectGroup>       </Objects>     </PublishDatabaseSettings>   </PropertyGroup>    <PropertyGroup>     <UseMsdeployExe>true</UseMsdeployExe>     <AllowUntrustedCertificate>True</AllowUntrustedCertificate>   </PropertyGroup>    <ItemGroup>     <MSDeployParameterValue Include="$(DeployParameterPrefix)ApplicationDbContext-Web.config Connection String">       <ParameterValue>metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlClient;provider connection string="Data Source=192.168.5.51;Initial Catalog=GameBit;User ID=GUser;Password=MyRealPassword;MultipleActiveResultSets=True;Application Name=EntityFramework"</ParameterValue>     </MSDeployParameterValue>   </ItemGroup>    <!--<ItemGroup>   <MsDeploySourceManifest Include="runCommand">     <Path>dir</Path>   </MsDeploySourceManifest> </ItemGroup>-->     <!--<Target Name="EncryptImportantSettings" AfterTargets="MSDeployPublish" >     <Message Text="Encrypting appSettings" />     --><!--<Exec Command="aspnet_regiis -pe &quot;appSettings&quot; -site Default -app &quot;/&quot;" />--><!--   <ItemGroup>     <MsDeploySourceManifest Include="runCommand">       <path>dir/b >> C:\temp\log.txt</path>       --><!--<waitInterval>10000</waitInterval>--><!--       --><!--<AdditionalProviderSettings>waitInterval</AdditionalProviderSettings>--><!--     </MsDeploySourceManifest>   </ItemGroup>   <Message Text="AppPath: $(DeployIisAppPath)" />   </Target>--> </Project> 

I've noticed that when I use MSDeploy I can see command that is executed during publish:

"C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe" -source:manifest='D:\GameBit\API\obj\Release\Package\API.SourceManifest.xml' -dest:auto,ComputerName="https://192.168.5.50:8172/msdeploy.axd?site=Default",UserName='LocalAdmin',Password="MyRealPassword",IncludeAcls='False',AuthType='Basic' -verb:sync -enableRule:EncryptWebConfig -enableRule:EncryptWebConfig -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -setParamFile:"D:\GameBit\API\obj\Release\Package\API.Publish.Parameters.xml" -allowUntrusted -retryAttempts=2 -userAgent="VS12.0:PublishDialog:WTE12.5.60612.0"

Can I add -postSync:runCommand="" to that command from publish profile? As I found on MS site this parameter allows to execute command on destination machine.

EDIT4:
I found information about Web Deploy Operation Settings and postSync setting, but I don't know where to set it, I don't want to edit Microsoft.Web.Publishing.targets from MSBuild folder

I need to execute command on remote machine after publish succeeded.

1 Answers

Answers 1

After going through your all edits and a bit of research from me , you want to execute the following command after the publish from the Visual Studio

C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis -pe "appSettings" -site Default -app "/"  

If i understood right , You can try wrapping the ItemGroup in a Target with AfterTargets set to AddIisSettingAndFileContentsToSourceManifest

<Target Name="executeinHosts" AfterTargets="AddIisSettingAndFileContentsToSourceManifest">     <ItemGroup>       <MsDeploySourceManifest Include="runCommand">          //here would be your path that need to run after the publish       </MsDeploySourceManifest>     </ItemGroup>   </Target> 

So in Your case this is how that part should look:

<Target Name="executeinHosts" AfterTargets="AddIisSettingAndFileContentsToSourceManifest">     <ItemGroup>       <MsDeploySourceManifest Include="runCommand">          <path>C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis -pe "appSettings" -site $(DeployIisAppPath) -app "/"</path>       </MsDeploySourceManifest>     </ItemGroup>   </Target> 
Read More

Sunday, July 16, 2017

Reverse engineer a string masking/obfuscating function

Leave a Comment

I would like to unmask or unobfuscate a piece of base64 encoded string containing some characters.

For instance, I know VovL5SaV_iSBx6oAFGypsTCO matches 00000000. Is there a way, knowing more data (the input and the output) to find out the algorithm that obfuscate the string? I couldn't find anything.

Some example:

  • The result of f(A) will vary in length if A is longer f(A) will be longer, if A is shorter f(A) will be shorter.
  • If we have A and B with a similar ending (e.g. aaaaa@gmail.com and bbbbb@gmail.com), then f(A) and f(B) will also have the same ending.

3 Answers

Answers 1

Given what you said and the comments published, I would say that yes, knowing more data couples [obfuscated/cleartext] can help you understand the underlying algorithm.

You will have to analyze various properties, such as string lengths, and characters redundancy, and byte values shift and variations to small changes (eg: changing 000000 to 000001) Also, control if the obfuscated strings are predictable or repeatable.

Ultimately, try to see if you can forge your own obfuscated texts and see the results. This can be achieved by fuzzing and can help you predict the algorithm in a more aggressive way.

From what you said, I would say this is not a secured block-cypher of the data, but rather a substitution. It could be ROT13 at byte-level or an XOR, both with some padding (metadata?).

Note that the _ seems to act as a separator: the string as is cannot be decoded, but both parts around the _ can be.

Answers 2

based on what you mentionned, it seems like the encryption uses a block cipher: One nive property about you block cipher is that the encryption of a block doesn't depend on the one before, this is not allways true, (and is very bad practice=very good for the you the attacker), one of block ciphers that does that is: ECB: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29 , as you can see the same key is used for each encryption, and the block cipher can be equivalent to a XOR allowing to guess decryopt anything. To check that trying f(00000000000000) where the length is more than one block and see if a pattern repeats it self. If this not the case, you might be useing something like CTR: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29 here a "key(i)"=blockcipher(key,nonce(i)) is used to encrypted each block using XOR, if you manage to calculate again f(000000000000) and find key(1), key(2)... you can use them latter to decrypt anything you want. if you can provide me with the tool used for the obfuscation I might be able to decrypt what you want

Answers 3

I would like to help you with this, but it is illegal under the United States Digital Millennium copyright act section 1201. Perhaps somebody in a country with more freedom will respond with a better answer.

1201. (1) A. No person shall circumvent a technological measure that effectively controls access to a work protected under this title. 
Read More

Wednesday, February 8, 2017

Server-side Google Sign-In, way to encrypt/decrypt data with Google-managed secrets?

Leave a Comment

It's rather straightforward to use the Google Sign-In library on the server side and attain a GoogleIdToken to validate a user's identity. However, I'd like to encrypt per-user data in my database with a secret that's unique to every user. Is there an easy way to do this? If not using Google Sign-in, you can derive keys from a user's password, but that's obviously not possible here.

3 Answers

Answers 1

Well, first of all, you're drawing a parallel to using the user's password to derive an encryption key, but since you're talking about that as an alternative if you weren't using Google Sign-On, that implies your talking about using the password that users would authenticate with. That's a bad idea.

Users need to be able to change their authentication password, and that will be a major hassle for you if you're encrypting with it. It will require you to decrypt everything with the old password and then re-encrypt it with the new one.

So what you need to find is something that you can pull out of the GoogleIdToken that will never change. Email addresses change, so I wouldn't use that. Perhaps the user id, which you can get with GoogleIdToken.getPayload().getSubject() is what you want. Then what you would want to do is derive a key from that. I would look for ways to combine it with other information that the user gives you that really is secret, though.

Answers 2

I really don't know anything about this, so I'm kinda just thinking out loud.

To get some idea as to the general process for using Google Sign In I've been looking over the following guides found at the Google Sign-in for Websites page

  • Integrate Google Sign-In
  • Authenticate with a Backend Server
  • Google Sign-In for Server-Side Apps

Anyway, it seemes like it should be possible to use some combination of immutable characteristics of the users GoogleIdToken like their email, user id, etc in combination with some immutable characteristics of your app or web site, such as the app id or some other secret and create a hash value from that. Use that as your encryption key, or maybe send that thru OpenSSH or something for a stronger encryption hash.

Seems like in interesting problem, wish I had some insight into solving it. Perhaps I will research this more as I have a few interesting side projects I'm tinkering on.

Answers 3

The information you receive during a Google sign on is intended for authentication purposes. The id token is encoded as a Json Web Token. There is nothing secret in a JWT.

The information is cryptographically signed by the authentication provider, so you can verify the information. This is of no help for deriving secrets, though.

Looks like you'll have to find another way.

Read More

Thursday, January 26, 2017

Decrypt OpenSSL binary through NGINX as it is received ( on the fly )

Leave a Comment

I have a small embedded Linux device that has 128 MB flash storage available to work with as a scratchpad. This device runs an NGINX web server. In order to do a firmware update - the system receives an encrypted binary file as an HTTPS POST through NGINX to the scratchpad. The system then decrypts the file and flashes a different QSPI flash device in order to complete the update.

The firmware binary is encrypted outside the device like this:

openssl smime -encrypt -binary -aes-256-cbc -in plainfile.zip -out encrypted.zip.enc -outform DER yourSslCertificate.pem 

The firmware binary is decrypted, after being received through NGINX, on the device like this:

openssl smime -decrypt -binary -in encrypted.zip.enc -inform DER -out decrypted.zip -inkey private.key -passin pass:your_password 

I'd really like to decrypt the binary as it is received ( on the fly ) through NGINX, so that it appears on the flash scratchpad in it's decrypted form.

I've been unable to find any existing NGINX modules on Google that would do this. How might I accomplish this? Thanks.

2 Answers

Answers 1

First of all, you need to understand one thing. While nginx will decrypt file - all other request will be blocked. That's why nginx does not support CGI, only FastCGI.

If it ok for you (for example, nginx used only for update purposes), you can use perl or lua extension: http://nginx.org/en/docs/http/ngx_http_perl_module.html, https://github.com/openresty/lua-nginx-module

Using this modules you can exec shell. For access uploaded file need to set client_body_in_file_only directive - https://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_in_file_only

Example for perl module (untested):

location /upload {   client_body_in_file_only clean;   perl 'sub {     my $r = shift;     if ($r->request_body_file) {        system("openssl smime -decrypt -binary -in ".$r->request_body_file." -inform DER -out /tmp/decrypted.zip -inkey private.key -passin pass:your_password");     }   }'; } 

But much better to use fastcgi. You can use light fastcgi wraper for it, for example, fcgiwrap https://www.nginx.com/resources/wiki/start/topics/examples/fcgiwrap/

Answers 2

There's a widely-known attack against non-event-based servers like Apache known as Slowloris, where the client initiates a number of HTTP requests without sending them in full (where each request is instead being sent in part for a prolonged interval, which is a very cheap operation, both to send and receive, for an event-based architecture like that of nginx, but at the same time is very expensive for some other servers).

As a proxy server, nginx protects its backends from such an attack. Indeed, protection may come at a cost in certain circumstances, and can be turned off with the …_request_buffering directives:

What you would do is disable the request buffering, and then pipe the incoming file directly to openssl as it is received.

Note that you can always use /dev/fd/0 in place of the filename to specify stdin (depending on the tool, using - in place of the filename may also be an option).

Read More

Thursday, June 23, 2016

PHP: Warning mcrypt_generic_init(): Iv size is incorrect; supplied length: 12, needed: 8

Leave a Comment

Basic Facts:

$algorithm  = MCRYPT_BLOWFISH; $mode       = MCRYPT_MODE_CBC; $randSource = MCRYPT_DEV_URANDOM; 

Note This is not a strict coding question.

Context:

CentOS 7, Apache 2.4.12, & PHP 5.6.20.

I am making an HTML email with a "verify your email address" link that allows a registration process to complete. Everything on my virtual private server is UTF-8, and all form and query string input is processed with multi-byte (mb) funcions.

Background

As an experiment (I know about the age and state of the mcrypt library), I am attempting to decrypt Blowfish encrypted query string parameters. Assume that on the way up, the encryption sequence works perfectly and I am receiving email with the link.

On the way down, the hmac_hash() signing (SHA-512, just for this experiment) is working and I am able to separate each independent message (32 characters) from its hash checksum (128 characters). Base64 decoding of the separated message portion is working. For each parameter, I am left with the composite cipher text, where composite cipher text equals the IV + base cipher text. Assume I use a version of substr() to obtain the IV and the base cipher text independently (which is par for the course).

Problem

PHP: Warning  mcrypt_generic_init(): Iv size is incorrect; supplied length: 12, needed: 8 

Assume I have combed the PHP manual and Stackoverflow. Assume I have looked at other questions similar, but not exactly like this one. Assume I have searched the Internet to no avail. Assume I have enough experience to setup mb_string properly. Assume that I will take care of mcrypt padding when I get past this current problem.

Could multi-byte issues be interfering with decryption?

Could base64 encoding the IV + base cipher text corrupt the IV?

Could base64 padding be an issue?

Should I be specifying a more specific MCRYPT_BLOWFISH_*?

Why does the blowfish IV size report 8 bytes, but rarely produces an 8 byte IV?

Which substr() should I use, substr() or mb_substr(), for a setup that leans towards making everything UTF-8 and processes all other input as multi-byte UTF-8. I know that is an odd question, but all of the PHP Manual mycrypt decryption sequence examples use substr(), and none use mb_substr(). Everything on my site works with mb_functions when possible, and I would not mind using substr() if it solved my problem, but it does not solve it. When I use mb_substr(), I get the following warning.

PHP: Warning  mcrypt_generic_init(): Iv size is incorrect; supplied length: 11, needed: 8 

Does anyone have any experience with this exact issue? Constructive answers will be rewarded!

Latest

Goal: To recreate a hash, like this, from a Blowfish encrypted query string.

Above is an example Blowfish hash that I am trying to reconstruct from an array, received via a SHA512 HMACed, symmetricly Blowfish encrypted (CBC), url safe Base64 encoded, urlencoded, query string (phew!).

Below, this is what the strings for the query string look like before being urlencoded (don't mind the indexes right now).

enter image description here

Decrypted Array

Above is the Base64 decoded and Blowfish decrypted array derived from the query string (Obviously, there are security steps in between this result, but I am just trying to show the latest state of things.) Something is not right. Encryption appears to work without any errors. Decryption does not produce and errors either. The plain text is just wrong. If I join/implode these elements, they will not be like the blowfish hash above.

1 Answers

Answers 1

Preliminary answer.

Both mycrpt_create_iv() and mcrypt_generic() output an ISO-8859-1 string. If you have a pure UTF-8 stack, and you intend on using Mcrypt's Blowfish to encrypt query string values, you must ensure that the composite cipher text (IV + Cipher Text) arrives at the decryption sequence as ISO-8859-1 (so, to be safe, you better make sure it arrives at the encryption sequences as ISO-8859-1). You can use substr() to recover the IV and true ciper text just fine, but you must first use utf8_decode() to change your UTF-8 composite cipher text to single byte ISO-8859-1 (testing in progress).

In effect, it seems that you want to be using ISO-8859-1 with mcrypt. Shape your inputs accordingly. But, still, now that the IV error is gone the data does not decrypt.

Read More

Thursday, April 14, 2016

Create RSA Token in nodejs

Leave a Comment

I'm trying to authenticate to a REST API using encryption.

First I need to call the API to get an encryptionKey and a timestamp. The encryptionKey is in Base 64 format.

Then I need to create a RSAToken using the key and finaly encrypt the password using password + "|" + timestamp.

This is some sample code using python to authenticate to the API

key, timestamp = get_encryption_key() decoded_key = key.decode('base64') rsa_key = RSA.importKey(decoded_key) encrypted = rsa_key.encrypt(password + '|' + str(timestamp), 'x') encrypted_password = encrypted[0] 

and

import base64 from Crypto.PublicKey import RSA r = requests.get(my_url, headers=headers)  myData = r.json() decoded = base64.b64decode(myData['encryptionKey']) key = RSA.importKey(decoded) enc = key.encrypt(password + '|' + str(myData['timeStamp']), 'x') encryptedPassword = enc[0]  session = "/session" my_url = url + session  payload = {"identifier": identifier, "password": encryptedPassword, "encryptedPassword": "True" } 

Any hints to achieve this under Node?

1 Answers

Answers 1

You can use crypto.publicEncrypt to encrypt your password. Notice the padding, you might want to use the right padding that is being used in your Python script.

const crypto = require('crypto'); const constants = require('constants'); const decodedKey = Buffer(encriptionKey, 'base64').toString();  const encryptedPassword = crypto.publicEncrypt({     key: decodedKey,     padding : constants.RSA_PKCS1_OAEP_PADDING } , Buffer(`${password}|${timestamp}`)); 

Check out this node.js test to find out more examples for different padding.

Read More

Friday, April 1, 2016

How to encrypt data in core data (sqllite) in OS-X application

Leave a Comment

I have found that if I use transformable type of attributes and NSXMLStoreType my data is encrypted, that is attributes that has been of transformable type, are not readable. There is no need of doing anything else, no code is required. Please note that I am working on OS-X application that uses core data.

However, if I change my store type to NSSQLiteStoreType, that is not the case.

I can open the database with sqllitebrowser, select the transformable field, and If I click on export button, in the generated text file, I can read the value normally, that is the value (data) is not encrypted.

I have asked the same question about 4 months ago and I get no answer.

Also, I have found this post here on stackoverflow.

You can encrypt individual properties in your Core Data model entities by making them transformable properties, then creating an NSValueTransformer subclass which will encrypt and decrypt the data for that property.

Unlucky for me, the author of the answer, @Brad Larson, didn't provide an simple example of how this can be done.

Can anyone provide any sample code of how I can encrypt transformable properties so that It' cant be readable in any way?

1 Answers

Answers 1

you could do something like shown here Cross-platform-AES-encryption

add a new Objective-C file to project select category and NSData class call it Additions

NSData+Additions.h

#import <Foundation/Foundation.h> #import <CommonCrypto/CommonDigest.h> #import <CommonCrypto/CommonCryptor.h>  @interface NSData (Additions)  #pragma mark - data encryption  + (NSData *)encrypt:(NSData *)plainText key:(NSData *)key iv:(NSData *)iv; + (NSData *)decrypt:(NSData *)encryptedText key:(NSData *)key iv:(NSData *)iv;  + (NSData *)dataFromHexString:(NSString *)string; + (NSData *)sha256forData:(id)input;  + (NSData *)generateRandomIV:(size_t)length;  @end 

NSData+Additions.m

#import "NSData+Additions.h"  @implementation NSData (Additions)  + (NSData *)encrypt:(NSData *)dataToEncrypt key:(NSData *)key iv:(NSData *)iv {      NSUInteger dataLength = [dataToEncrypt length];      size_t buffSize = dataLength + kCCBlockSizeAES128;     void *buff = malloc(buffSize);      size_t numBytesEncrypted = 0;      CCCryptorStatus status = CCCrypt(kCCEncrypt,                                      kCCAlgorithmAES128,                                      kCCOptionPKCS7Padding,                                      [key bytes], kCCKeySizeAES256,                                      [iv bytes],                                      [dataToEncrypt bytes], [dataToEncrypt length],                                      buff, buffSize,                                      &numBytesEncrypted);      if (status == kCCSuccess) {         return [NSData dataWithBytesNoCopy:buff length:numBytesEncrypted];     }      free(buff);     return nil; }  + (NSData *)decrypt:(NSData *)encryptedData key:(NSData *)key iv:(NSData *)iv {      NSUInteger dataLength = [encryptedData length];      size_t buffSize = dataLength + kCCBlockSizeAES128;      void *buff = malloc(buffSize);      size_t numBytesEncrypted = 0;     CCCryptorStatus status = CCCrypt(kCCDecrypt,                                      kCCAlgorithmAES128,                                      kCCOptionPKCS7Padding,                                      [key bytes], kCCKeySizeAES256,                                      [iv bytes],                                      [encryptedData bytes], [encryptedData length],                                      buff, buffSize,                                      &numBytesEncrypted);     if (status == kCCSuccess) {         return [NSData dataWithBytesNoCopy:buff length:numBytesEncrypted];     }      free(buff);     return nil; }  + (NSData *)dataFromHexString:(NSString *)string {      NSMutableData *stringData = [[NSMutableData alloc] init];     unsigned char whole_byte;     char byte_chars[3] = {'\0','\0','\0'};      for (int counter = 0; counter < [string length] / 2; counter++) {         byte_chars[0] = [string characterAtIndex:counter * 2];         byte_chars[1] = [string characterAtIndex:counter * 2 + 1];         whole_byte = strtol(byte_chars, NULL, 16);         [stringData appendBytes:&whole_byte length:1];     }      return stringData; }  + (NSData *)sha256forData:(id)input {     NSData *dataIn;      if ([input isKindOfClass:[NSString class]]) {         dataIn = [input dataUsingEncoding:NSUTF8StringEncoding];     } else if ([input isKindOfClass:[NSData class]]) {          NSUInteger dataLength = [input length];         NSMutableString *string = [NSMutableString stringWithCapacity:dataLength * 2];         const unsigned char *dataBytes = [input bytes];          for (NSInteger idx = 0; idx < dataLength; ++idx)             [string appendFormat:@"%02x", dataBytes[idx]];          dataIn = [string dataUsingEncoding:NSUTF8StringEncoding];     }      NSMutableData *macOut = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];      CC_SHA256(dataIn.bytes, (CC_LONG)[dataIn length],  [macOut mutableBytes]);      return macOut; }  + (NSData *)generateRandomIV:(size_t)length {     NSMutableData *data = [NSMutableData dataWithLength:length];      SecRandomCopyBytes(kSecRandomDefault, length, [data mutableBytes]);      return data; }  @end 

then you are going to need username, password, iVector and some random salt. in the salt i have used replace # with random characters but make sure that if u use % do not forget to double it %% otherwise it will be incomplete format specifier warning.

in your class use it like this

#define CC_USERNAME         @"secretName" #define CC_PASSWORD         @"secretPassword" #define CC_SALTED_STRING    [NSString stringWithFormat:@"####################%@#####################", CC_PASSWORD] 

then create NSData representation of your salted string ran through SHA256

NSData *hash = [NSData sha256forData:CC_SALTED_STRING]; 

next step is to generate 16 bytes of random generated iVector data

NSData *iVector = [NSData generateRandomIV:16]; 

and use these objects to encrypt your string. create a NSMutableData object with first 16 bytes of iVector data (make sure u use iVector object and do not generate new random or you will be unable to decrypt).

NSString *message = @"my secret message to the world"; NSData *messageData = [message dataUsingEncoding:NSUTF8StringEncoding];  NSMutableData *encryptedData = [[NSMutableData alloc] initWithData:iVector];  NSData *payLoad = [NSData encrypt:messageData key:hash iv:iVector];  [encryptedData appendData:payLoad]; 

to decrypt, separate first 16 bytes and the rest of data and use it with the hash.

NSData *pureData = [encryptedData subdataWithRange:NSMakeRange(16, [encryptedData length] - 16)]; NSData *extractedVector = [encryptedData subdataWithRange:NSMakeRange(0, 16)];  NSData *decryptedData = [NSData decrypt:pureData key:hash iv:extractedVector];  NSString *decryptedMessage = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding]; 

you can do some extra md5 on the hash or even pack encrypted data with zlib before storing it.

enjoy your custom made crypto.

Read More

Unique IVs producing identical ciphertext using attr_encrypted

Leave a Comment

Using the most basic setup:

class User < ActiveRecord::Base   attr_encrypted :name,                   key: 'This is a key that is 256 bits!!',                   encode: true,                   encode_iv: true,                   encode_salt: true end 

The results look like this in the database when supplying an identical name:

╔════╦══════════════════════════════╦═══════════════════╗ ║ id ║ encrypted_name               ║ encrypted_name_iv ║ ╠════╬══════════════════════════════╬═══════════════════╣ ║ 1  ║ aVXZb1b317nroumXVBdV9pGxA2o= ║ JyE7wHups+3upY5e  ║ ║ 2  ║ aVXZb1b317nroumXVBdV9pGxA2o= ║ uz/ktrtbUAksg5Vp  ║ ╚════╩══════════════════════════════╩═══════════════════╝ 

Why is the ciphertext identical? Isn't that the part of the point of iv, which the gem is using by default?

2 Answers

Answers 1

Update: the following is the original post explaining the whole problem, the issue is fixed now, see the bottom of this answer for a solution.

I am quite sure you noticed a rather nasty security issue in the encryptor gem (the gem that is used by attr_encrypted to do the actual encryptions).

The problem is that when using the aes-256-gcm algorithm (or any of the AES GCM algorithms), the initialization vector (IV) is currently indeed not taken into account when encrypting. The issue does not affect other algorithms but unfortunately the aes-256-gcm is the default algorithm in attr_encrypted.

As it turns out, it is the order of setting the IV vs. the encryption key what causes the issue. When IV is set before the key (as is in the gem), the IV is not taken into account but it is if set after the key.

Some tests to prove the problem:

While taking parts of the encryptor gem code, I created the simplest test case to prove the problem (tested under ruby 2.3.0 compiled against OpenSSL version "1.0.1f 6 Jan 2014"):

def base64_enc(bytes)   [bytes].pack("m") end  def test_aes_encr(n, cipher, data, key, iv, iv_before_key = true)   cipher = OpenSSL::Cipher.new(cipher)   cipher.encrypt    # THIS IS THE KEY PART OF THE ISSUE   if iv_before_key     # this is how it's currently present in the encryptor gem code     cipher.iv = iv     cipher.key = key   else     # this is the version that actually works     cipher.key = key     cipher.iv = iv   end    if cipher.name.downcase.end_with?("gcm")     cipher.auth_data = ""   end    result = cipher.update(data)   result << cipher.final    puts "#{n} #{cipher.name}, iv #{iv_before_key ? "BEFORE" : "AFTER "} key: " +            "iv=#{iv}, result=#{base64_enc(result)}" end  def test_encryption   data = "something private"   key = "This is a key that is 256 bits!!"    # control tests using AES-256-CBC   test_aes_encr(1, "aes-256-cbc", data, key, "aaaabbbbccccdddd", true)   test_aes_encr(2, "aes-256-cbc", data, key, "eeeeffffgggghhhh", true)   test_aes_encr(3, "aes-256-cbc", data, key, "aaaabbbbccccdddd", false)   test_aes_encr(4, "aes-256-cbc", data, key, "eeeeffffgggghhhh", false)    # failing tests using AES-256-GCM   test_aes_encr(5, "aes-256-gcm", data, key, "aaaabbbbcccc", true)   test_aes_encr(6, "aes-256-gcm", data, key, "eeeeffffgggg", true)   test_aes_encr(7, "aes-256-gcm", data, key, "aaaabbbbcccc", false)   test_aes_encr(8, "aes-256-gcm", data, key, "eeeeffffgggg", false) end 

Running test_encryption which encrypts a text using AES-256-CBC and then using AES-256-GCM, each time with two different IVs in two regimes (IV set before/after key), gets us the following results:

# control tests with CBC: 1 AES-256-CBC, iv BEFORE key: iv=aaaabbbbccccdddd, result=4IAGcazRmEUIRDE3ZpEgoS0Nmm1/+nrd5VT2/Xab0WM= 2 AES-256-CBC, iv BEFORE key: iv=eeeeffffgggghhhh, result=T7um2Wgb2vw1r4uryF3xnBeq+KozuetjKGItfNKurGE= 3 AES-256-CBC, iv AFTER  key: iv=aaaabbbbccccdddd, result=4IAGcazRmEUIRDE3ZpEgoS0Nmm1/+nrd5VT2/Xab0WM= 4 AES-256-CBC, iv AFTER  key: iv=eeeeffffgggghhhh, result=T7um2Wgb2vw1r4uryF3xnBeq+KozuetjKGItfNKurGE=  # the problematic tests with GCM: 5 id-aes256-GCM, iv BEFORE key: iv=aaaabbbbcccc, result=Tl/HfkWpwoByeYRz6Mz4yIo= 6 id-aes256-GCM, iv BEFORE key: iv=eeeeffffgggg, result=Tl/HfkWpwoByeYRz6Mz4yIo= 7 id-aes256-GCM, iv AFTER  key: iv=aaaabbbbcccc, result=+4Iyn7RSDKimTQi0S3gn58E= 8 id-aes256-GCM, iv AFTER  key: iv=eeeeffffgggg, result=3m9uEDyb9eh1RD3CuOCmc50= 

These tests show that while the order of setting IV vs. key is not relevant for CBC, it is for GCM. More importantly, the encrypted result in CBC is different for two different IVs, whereas it is not for GCM if IV set before the key.

I just created a pull request to fix this issue in the encryptor gem. Practically, you have a few options now:

  • Wait till a new version of the encryptor gem is released.

  • Use also salt with attr_encrypted. You should use salt anyway to further secure the encrypted data.

The very unfortunate thing is that all already encrypted data will become undecipherable after the fix as suddenly the IVs will be taken into account.

Update: encryptor 3.0.0 available

You can now upgrade the encryptor gem to version 3.0 in which the bug is fixed. Now, if this is the first time you use the encryptor or attr_encrypted gems you are all set and everything should work correctly.

If you have data that is already encrypted using encryptor 2.0.0, then you must manually re-encrypt the data after the gem upgrade, otherwise it will fail to decrypt correctly! You will be warned about this during the gem upgrade. The schematic procedure is as follows:

  • You have to decrypt all your encrypted data using the Encryptor class (see the README for examples), using the :v2_gcm_iv => true option. This should correctly decrypt your data.
  • Then you must encrypt the same data back again, now without this option (i.e. :v2_gcm_iv => false) but including the proper IV from your database.
  • If you have production data, you will need to do this upgrade offline and immediately after the gem update to ensure no data corruption.

Answers 2

If the plain text, key and IV are the same the encrypted text will be the same.

It looks like you need to use:

attr_encrypted :email, key: 'some secret key', encode: true, encode_iv: true, encode_salt: true 

Note: encode_iv: true

Or perhaps set the default option encode_iv: true

Documentation: attr_encrypted

Read More

Tuesday, March 15, 2016

How to encrypt a pouchdb database

Leave a Comment

Background:

I am trying to encrypt a pouchdb database by using crypto-pouch library. I had a look at the example shown at https://github.com/calvinmetcalf/crypto-pouch But it doesn't seem to do anything for me.

My code:

<!DOCTYPE html>  <html ng-app="pouchdbApp">   <head>     <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>     <script src="pouchdbDemo.js"></script>     <script src="http://cdn.jsdelivr.net/pouchdb/5.2.1/pouchdb.min.js"></script>     <!-- <script src="crypto-pouch-master/bundle.js"></script> -->     <script src="http://wzrd.in/standalone/crypto-pouch"></script>       <script>         var db = new PouchDB('kittens2');           var password = "mypassword";          db.crypto(password).then(function (publicKey) {              console.log("publicKey");     	    console.log(publicKey);         });              /* db.removeCrypto();  */           var doc = {  		  "_id": "mittens",  		  "name": "Mittens",  		  "occupation": "kitten",  		  "age": 3,  		  "hobbies": [  		    "playing with balls of yarn",  		    "chasing laser pointers",  		    "lookin' hella cute"   		   ]  		};                db.put(doc);          db.get('mittens').then(function (doc) {           console.log(doc);        });       </script>     </head>   <body>     </body>    </html>

But my code doesn't see to do any encryption of the data entered, or i couldn't see any public key generated.

Any clue how i should be using the crypto-pouch library with pouchdb.

1 Answers

Answers 1

so the documentation is a bit confusing (which I just cleaned up) but when you call db.crypto it wraps the database so that documents are transparently encrypted and decrypted

db.crypto(password).then(function () {    // <-- encryption set up }) 

and it will transparently encrypt documents you create and decrypt ones you read until you call

db.removeCrypto(); 

so if you want to test do something like

db.crypto(password).then(function () {    return db.put({_id: 'foo', bar: 'baz'}); }).then(function () {     return db.get('foo'); }).then(function (doc) {     console.log('decrypted', doc);     return db.removeCrypto(); }).then(function () {     return db.get('foo'); }).then(function (doc) {     console.log('encrypted', doc); }) 
Read More