Samma lokala databasmotor används för alla Adobe AIR-program. Det betyder att alla AIR-program kan ansluta till, läsa från och skriva till en okrypterad databasfil. Från och med Adobe AIR 1.5 inkluderar AIR funktioner för att skapa och ansluta till krypterade databasfiler. När du använder en krypterad databas måste programmet som vill ansluta till databasen tillhandahålla korrekt krypteringsnyckel. Om fel krypteringsnyckel (eller ingen nyckel) anges kan programmet inte ansluta till databasen. Det innebär att programmet inte kan läsa data från databasen eller skriva till eller ändra data i databasen.
Om du vill använda en krypterad databas måste du skapa databasen som en krypterad databas. Om du har en befintlig krypterad databas kan du upprätta en anslutning till databasen. Du kan också ändra krypteringsnyckeln för en krypterad databas. Förutom när det gäller att skapa och ansluta till krypterade databaser, arbetar du på samma sätt med krypterade och okrypterade databaser. Exempelvis kör du SQL-uttryck på samma sätt oavsett om databasen är krypterad eller inte.
Användningsområden för en krypterad databas
Kryptering är användbart om du vill begränsa åtkomsten till informationen som lagras i en databas. Databaskrypteringsfunktionen i Adobe AIR kan användas för många ändamål. Här följer några exempel på situationer då det kan vara praktiskt att använda en krypterad databas:
-
En skrivskyddad cache med privata programdata som hämtas från en server.
-
Ett lokalt programarkiv för privata data som synkroniseras med en server (data skickas till och läses in från servern).
-
Krypterade filer som används som filformatet för dokument som skapas och redigeras av programmet. Filerna kan vara privata för en användare, eller delas av alla som använder programmet.
-
Annan användning av en lokal datalagringsplats, t.ex. de som beskrivs i avsnittet
Användningsområden för lokala SQL-databaser
, där informationen måste skyddas från användare som har åtkomst till datorn eller databasfilerna.
Om du förstår när och varför det kan vara praktiskt att använda en krypterad databas blir det lättare att bestämma hur programmet ska struktureras. Detta kan exempelvis påverka hur krypteringsnyckeln för databasen skapas, hämtas eller lagras av programmet. Mer information finns i avsnittet
Att tänka på när du använder kryptering med en databas
.
I stället för att använda en krypterad databas kan du skydda känsliga data med hjälp av den
krypterade, lokala lagringsplatsen
. Med den krypterade, lokala lagringsplatsen lagrar du ett enskilt
ByteArray
-värde med hjälp av en String-nyckel. Värdet är bara tillgängligt för det AIR-program som skapat värdet, och endast på den dator som det lagras på. Med den krypterade lokala lagringsplatsen behöver du inte skapa din egen krypteringsnyckel. Dessa anledningar gör att den krypterade lokala lagringsplatsen passar särskilt bra för att enkelt lagra ett enskilt värde eller en uppsättning värden som lätt kan kodas i en bytearray. En krypterad databas passar bäst för större datauppsättningar där strukturerad datalagring och datafrågor används. Mer information om hur du använder den krypterade lokala lagringsplatsen finns i avsnittet
Krypterad lokal lagringsplats
.
Skapa en krypterad databas
Du kan bara använda en krypterad databas om databasen krypterades när den skapades. En okrypterad databas kan inte krypteras senare. På motsvarande sätt kan en krypterad databas inte dekrypteras senare. Om det behövs kan du ändra krypteringsnyckeln för en krypterad databas. Mer information finns i avsnittet
Ändra krypteringsnyckeln för en databas
. Om du har en befintlig databas som inte är krypterad och du vill använda databaskryptering kan du skapa en ny krypterad databas och kopiera den befintliga tabellstrukturen och data till den nya databasen.
Du skapar en krypterad databas på praktiskt taget samma sätt som en okrypterad databas. Mer information finns i avsnittet
Skapa en databas
. Först skapar du en
SQLConnection
-instans som representerar anslutningen till databasen. Du skapar databasen genom att anropa SQLConnection-objektets
open()
-metod eller
openAsync()
-metod och anger en fil som inte finns än för databasplatsen. Den enda skillnaden när du skapar en krypterad databas är att du anger ett värde för
encryptionKey
-parametern (
open()
-metodens femte parameter och
openAsync()
-metodens sjätte parameter).
Ett giltigt
encryptionKey
-parametervärde är ett
ByteArray
-objekt som innehåller exakt 16 byte.
I följande exempel visas hur en krypterad databas skapas. För enkelhetens skull är krypteringsnyckeln hårdkodad i programkoden i de här exemplen. Den här tekniken rekommenderas emellertid inte eftersom den inte är säker.
var conn:SQLConnection = new SQLConnection();
var encryptionKey:ByteArray = new ByteArray();
encryptionKey.writeUTFBytes("Some16ByteString"); // This technique is not secure!
// Create an encrypted database in asynchronous mode
conn.openAsync(dbFile, SQLMode.CREATE, null, false, 1024, encryptionKey);
// Create an encrypted database in synchronous mode
conn.open(dbFile, SQLMode.CREATE, false, 1024, encryptionKey);
Ett exempel som illustrerar en rekommenderad metod för genereringen av en krypteringsnyckel finns i avsnittet
Exempel: Generera och använda en krypteringsnyckel.
.
Ansluta till en krypterad databas
Precis som när du skapar en krypterad databas, ansluter du till krypterade och okrypterade databaser på samma sätt. Den här proceduren beskrivs i detalj i avsnittet
Ansluta till en databas
. Du använder
open()
-metoden om du vill
öppna en anslutning i synkront körningsläge
och
openAsync()
-metoden om du vill
öppna en anslutning i asynkront körningsläge
. Den enda skillnaden när du öppnar en krypterad databas är att du anger korrekt värde för
encryptionKey
-parametern (
open()
-metodens femte parameter och
openAsync()
-metodens sjätte parameter).
Om en felaktig krypteringsnyckel anges returneras ett fel. Om du använder
open()
-metoden genereras ett
SQLError
-undantag. Om du använder
openAsync()
-metoden skickar
SQLConnection
-objektet ett
SQLErrorEvent
-objekt, vars
error
-egenskap innehåller ett
SQLError
-objekt. I båda fallen har SQLError-objektet som genereras
errorID
-egenskapsvärdet 3138. Fel-ID:t hör till felmeddelandet "Filen som öppnats är inte en databasfil".
Följande exempel illustrerar hur du öppnar en krypterad databas i asynkront körningsläge. För enkelhetens skull är krypteringsnyckeln hårdkodad i programkoden i det här exemplet. Den här tekniken rekommenderas emellertid inte eftersom den inte är säker.
import flash.data.SQLConnection;
import flash.data.SQLMode;
import flash.events.SQLErrorEvent;
import flash.events.SQLEvent;
import flash.filesystem.File;
var conn:SQLConnection = new SQLConnection();
conn.addEventListener(SQLEvent.OPEN, openHandler);
conn.addEventListener(SQLErrorEvent.ERROR, errorHandler);
var dbFile:File = File.applicationStorageDirectory.resolvePath("DBSample.db");
var encryptionKey:ByteArray = new ByteArray();
encryptionKey.writeUTFBytes("Some16ByteString"); // This technique is not secure!
conn.openAsync(dbFile, SQLMode.UPDATE, null, false, 1024, encryptionKey);
function openHandler(event:SQLEvent):void
{
trace("the database opened successfully");
}
function errorHandler(event:SQLErrorEvent):void
{
if (event.error.errorID == 3138)
{
trace("Incorrect encryption key");
}
else
{
trace("Error message:", event.error.message);
trace("Details:", event.error.details);
}
}
Följande exempel illustrerar hur du öppnar en krypterad databas i synkront körningsläge. För enkelhetens skull är krypteringsnyckeln hårdkodad i programkoden i det här exemplet. Den här tekniken rekommenderas emellertid inte eftersom den inte är säker.
import flash.data.SQLConnection;
import flash.data.SQLMode;
import flash.filesystem.File;
var conn:SQLConnection = new SQLConnection();
var dbFile:File = File.applicationStorageDirectory.resolvePath("DBSample.db");
var encryptionKey:ByteArray = new ByteArray();
encryptionKey.writeUTFBytes("Some16ByteString"); // This technique is not secure!
try
{
conn.open(dbFile, SQLMode.UPDATE, false, 1024, encryptionKey);
trace("the database was created successfully");
}
catch (error:SQLError)
{
if (error.errorID == 3138)
{
trace("Incorrect encryption key");
}
else
{
trace("Error message:", error.message);
trace("Details:", error.details);
}
}
Ett exempel som illustrerar en rekommenderad metod för genereringen av en krypteringsnyckel finns i avsnittet
Exempel: Generera och använda en krypteringsnyckel.
.
Ändra krypteringsnyckeln för en databas
Om en databas är krypterad kan du ändra krypteringsnyckeln för databasen vid ett senare tillfälle. Om du vill ändra databasens krypteringsnyckel öppna du först en anslutning till databasen genom att skapa en
SQLConnection
-instans och anropa dess
open()
- eller
openAsync()
-metod. När databasanslutningen har upprättats anropar du
reencrypt()
-metoden och anger den nya krypteringsnyckeln som ett argument.
Som med de flesta databasåtgärder varierar
reencrypt()
-metodens beteende beroende på om synkront eller asynkront körningsläge används för databasanslutningen. Om du använder
open()
-metoden för att ansluta till databasen körs
reencrypt()
-åtgärden synkront. När åtgärden har slutförts exekveras nästa rad i koden:
var newKey:ByteArray = new ByteArray();
// ... generate the new key and store it in newKey
conn.reencrypt(newKey);
Om du i stället upprättar databasanslutningen med
openAsync()
-metoden körs
reencrypt()
-åtgärden asynkront. Omkrypteringen startar när
reencrypt()
anropas. När åtgärden slutförts skickar SQLConnection-objektet en
reencrypt
-händelse. Du använder en händelseavlyssnare för att kontrollera när omkrypteringen är färdig:
var newKey:ByteArray = new ByteArray();
// ... generate the new key and store it in newKey
conn.addEventListener(SQLEvent.REENCRYPT, reencryptHandler);
conn.reencrypt(newKey);
function reencryptHandler(event:SQLEvent):void
{
// save the fact that the key changed
}
reencrypt()
-åtgärden körs i en egen transaktion. Om åtgärden avbryts eller misslyckas (till exempel om programmet stängs innan åtgärden slutförts) återställs transaktionen. I så fall används den ursprungliga krypteringsnyckeln för databasen.
reencrypt()
-metoden kan inte användas för att ta bort krypteringen från en databas. Om ett
null
-värde eller en krypteringsnyckel som inte är en bytearray på 16 byte skickas till
reencrypt()
-metoden returneras ett fel.
Att tänka på när du använder kryptering med en databas
I avsnittet
Användningsområden för en krypterad databas
illustreras olika situationer då du bör använda en krypterad databas. Självklart har användningsscenarierna för olika program (även dessa och andra scenarier) olika sekretesskrav. Hur du strukturerar användningen av eventuell kryptering i programmet avgör i hög grad hur privat informationen i en databas är. Om du till exempel använder en krypterad databas för att skydda personliga data, även från andra användare på samma dator, behöver varje användares databas en egen krypteringsnyckel. Högst säkerhet erhålls om programmet genererar nyckeln från ett lösenord som anges av användaren. Om krypteringsnyckeln baseras på ett lösenord är informationen inte åtkomlig även om någon lyckas personifiera användarens konto på datorn. Andra gånger kanske du tvärtom vill att en databasfil ska kunna läsas av alla som använder programmet, men inte av andra program. I så fall behöver alla installerade kopior av programmet åtkomst till en delad krypteringsnyckel.
Du kan utforma programmet, och teknikerna som används för att generera krypteringsnyckeln, i enlighet med den sekretessnivå som krävs för programinformationen. Följande lista innehåller designförslag för olika sekretessnivåer:
-
Om du vill att en databas ska vara tillgänglig för alla användare som har åtkomst till programmet på alla datorer, använder du en enda nyckel som är tillgänglig för alla programinstanser. Första gången ett program körs kan den delade krypteringsnyckeln hämtas från en server med hjälp av ett säkert protokoll, t.ex. SSL. Därefter kan nyckeln sparas på den krypterade lokala lagringsplatsen för framtida användning. Ett annat alternativ är att kryptera informationen för de enskilda användarna på datorn och synkronisera informationen med ett dataarkiv på en fjärrdator, t.ex. en server, så att informationen blir portabel.
-
Om du vill att databasen ska vara tillgänglig för en enskild användare på alla datorer genererar du krypteringsnyckeln från en användarhemlighet, t.ex. ett lösenord. Generera inte nyckeln baserat på ett värde som är kopplat till en särskild dator (t.ex. ett värde som lagras på den krypterade lokala lagringsplatsen). Ett annat alternativ är att kryptera informationen för de enskilda användarna på datorn och synkronisera informationen med ett dataarkiv på en fjärrdator, t.ex. en server, så att informationen blir portabel.
-
Om du vill att en databas bara ska vara tillgänglig för en särskild användare på en enskild dator genererar du nyckeln från ett lösenord och ett genererat salt-värde. Ett exempel på den här tekniken finns i avsnittet
Exempel: Generera och använda en krypteringsnyckel.
.
Nedan följer ytterligare säkerhetsaspekter som är viktiga att ha i åtanke när du utformar ett program som ska använda en krypterad databas:
-
Ett system är bara så säkert som dess svagaste länk. Om krypteringsnyckeln genereras baserat på ett användardefinierat lösenord bör du definiera krav på komplexitet och minsta tillåtna längd för lösenorden. Ett kort lösenord som bara innehåller vanliga tecken är inte svårt att lista ut.
-
Källkoden för ett AIR-program lagras på en användares dator som oformaterad text (för HTML-innehåll) eller som en binärfil som enkelt kan dekompileras (för SWF-innehåll). Eftersom källkoden är åtkomlig bör du tänka på följande två saker:
-
Hårdkoda aldrig en krypteringsnyckel i källkoden.
-
Förutsätt alltid att tekniken som används för att generera en krypteringsnyckel (t.ex. program som genererar slumpmässiga tecken eller en särskild hash-algoritm) inte innebär några större svårigheter för en angripare.
-
Databaskrypteringen i AIR baseras på AES (Advanced Encryption Standard) med CCM-läge (Counter with CBC-MAC). För att det här krypteringschiffret ska vara säkert krävs en användardefinierad nyckel som kombineras med ett salt-värde. Ett exempel finns i avsnittet
Exempel: Generera och använda en krypteringsnyckel.
.
-
När du väljer att kryptera en databas krypteras alla hårddiskfiler som används av databasmotorn i kombination med den databasen. För att förbättra läs- och skrivtiderna i transaktioner sparas emellertid en del data tillfälligt i ett cacheminne. Informationen i cacheminnet är inte krypterad. Om en angripare lyckas få åtkomst till minnet som används av ett AIR-program, t.ex. med hjälp av en felsökare, kan han eller hon komma åt informationen i en öppen databas som inte är krypterad.
Exempel: Generera och använda en krypteringsnyckel.
Det här exempelprogrammet demonstrerar en teknik som du kan använda för att generera en krypteringsnyckel. Med det här programmet används en hög sekretess- och säkerhetsnivå för att skydda användarnas data. En viktig aspekt vid säkring av privat information är kravet att användaren måste ange ett lösenord varje gång programmet ansluter till databasen. Därför, vilket visas i det här exemplet, ska ett program med den här sekretessnivån aldrig direkt lagra krypteringsnyckeln för databasen.
Programmet består av två delar: en ActionScript-klass som genererar en krypteringsnyckel (klassen EncryptionKeyGenerator), och ett grundläggande användargränssnitt som visar hur den klassen används. Den fullständiga källkoden finns i
Fullständig exempelkod för generering och användning av en krypteringsnyckel
.
Använda klassen EncryptionKeyGenerator för att få en säker krypteringsnyckel
Det är inte nödvändigt att förstå exakt hur EncryptionKeyGenerator-klassen fungerar för att kunna använda den i programmet. Mer detaljerad information om hur klassen genererar en krypteringsnyckel för databasen finns i
EncryptionKeyGenerator-klassen
.
Följ de här stegen om du vill använda EncryptionKeyGenerator-klassen i ditt program:
-
Hämta EncryptionKeyGenerator-klassen som en källkod eller en kompilerad SWC. Klassen EncryptionKeyGenerator ingår i AS3CoreLib-projektet med öppen källkod. Du kan hämta
tas3corelib-paketet inklusive källkod och dokumentation
. Du kan även hämta SWC-filerna och källkodsfilerna från projektsidan.
-
Lägg källkoden för EncryptionKeyGenerator-klassen (eller as3corelib-SWC) på en plats som programkällkoden kan hitta.
-
Lägg till en
import
-sats i programkällkoden för EncryptionKeyGenerator-klassen.
import com.adobe.air.crypto.EncryptionKeyGenerator;
-
Före koden som skapar databasen eller en anslutning till den lägger du till kod för att skapa en EncryptionKeyGenerator-instans genom att anropa konstruktorn
EncryptionKeyGenerator()
.
var keyGenerator:EncryptionKeyGenerator = new EncryptionKeyGenerator();
-
Hämta ett lösenord från användaren:
var password:String = passwordInput.text;
if (!keyGenerator.validateStrongPassword(password))
{
// display an error message
return;
}
EncryptionKeyGenerator-instansen använder det här lösenordet som grund för krypteringsnyckeln (visas i nästa steg). EncryptionKeyGenerator-instansen testar lösenordet mot vissa valideringskrav för starka lösenord. Om valideringen misslyckas inträffar ett fel. Som exempelkoden visar kan du kontrollera lösenordet i förväg genom att anropa EncryptionKeyGenerator-objektets
validateStrongPassword()
-metod. På det sättet kan du avgöra om lösenordet motsvarar minimikraven för starka lösenord och undvika ett fel.
-
Generera krypteringsnyckeln från lösenordet:
var encryptionKey:ByteArray = keyGenerator.getEncryptionKey(password);
Metoden
getEncryptionKey()
genererar och returnerar krypteringsnyckeln (en 16-byte ByteArray). Sedan kan du använda krypteringsnyckeln för att skapa en ny krypterad databas eller öppna en befintlig.
Lösenordet som hämtas i steg 5 är en obligatorisk parameter i metoden
getEncryptionKey()
.
Obs!
För att få högsta säkerhets- och sekretessnivå för data måste ett program begära att användaren anger ett lösenord varje gång som programmet ansluter till databasen. Lagra inte användarens lösenord eller databasens krypteringsnyckel direkt. Det medför säkerhetsrisker att göra så. I stället, vilket visas i det här exemplet, bör ett program använda samma teknik för att skapa krypteringsnyckeln från lösenordet både när den krypterade databasen skapas och vid senare anslutningar till den.
Metoden
getEncryptionKey()
accepterar även en andra (valfri) parameter, parametern
overrideSaltELSKey
. EncryptionKeyGenerator skapar ett slumpvärde (kallat
salt
) som används som en del av krypteringsnyckeln. För att kunna återskapa krypteringsnyckeln lagras salt-värdet i ELS-lagret (Encrypted Local Store) i ditt AIR-program. Som standard använder EncryptionKeyGenerator-klassen en speciellt String-värde som ELS-nyckel. Även om det är osannolikt finns en risk för att nyckeln råkar i konflikt med en annan nyckel som används i ditt program. I stället för att använda standardnyckeln kan du ange en egen ELS-nyckel. Om du gör så anger du en egen nyckel genom att skicka den som den andra
getEncryptionKey()
-parametern, vilket visas här:
var customKey:String = "My custom ELS salt key";
var encryptionKey:ByteArray = keyGenerator.getEncryptionKey(password, customKey);
-
Skapa eller öppna databasen
Med en krypteringsnyckel som returneras av metoden
getEncryptionKey()
kan din kod skapa en ny krypterad databas eller försöka att öppna den befintliga krypterade databasen. Oavsett vilket kan du använda SQLConnection-klassens
open()
- eller
openAsync()
-metod, vilket beskrivs i
Skapa en krypterad databas
och
Ansluta till en krypterad databas
.
I det här exemplet har programmet utformats att öppna databasen i asynkront körningsläge. Koden aktiverar lämpliga händelseavlyssnare, anropar
openAsync()
-metoden och anger krypteringsnyckeln som det sista argumentet:
conn.addEventListener(SQLEvent.OPEN, openHandler);
conn.addEventListener(SQLErrorEvent.ERROR, openError);
conn.openAsync(dbFile, SQLMode.CREATE, null, false, 1024, encryptionKey);
Händelseavlyssnarregistreringarna tas bort i avlyssnarmetoderna. Därefter visas ett statusmeddelande som anger om databasen har skapats, öppnats eller om ett fel har inträffat. Den mest intressanta delen i dessa händelsehanterare finns i
openError()
-metoden. I den metoden kontrollerar en
if
-sats om databasen finns (d.v.s. att koden försöker ansluta till en befintlig databas) och om fel-id:t matchar konstanten
EncryptionKeyGenerator.ENCRYPTED_DB_PASSWORD_ERROR_ID
. Om båda dessa villkor har värdet true betyder det sannolikt att lösenordet som användaren angav är felaktigt. (Det kan även betyda att den angivna filen inte är en databasfil.) Följande kod kontrollerar fel-id:t:
if (!createNewDB && event.error.errorID == EncryptionKeyGenerator.ENCRYPTED_DB_PASSWORD_ERROR_ID)
{
statusMsg.text = "Incorrect password!";
}
else
{
statusMsg.text = "Error creating or opening database.";
}
Den fullständiga koden för exemplets händelseavlyssnare finns i
Fullständig exempelkod för generering och användning av en krypteringsnyckel
.
Fullständig exempelkod för generering och användning av en krypteringsnyckel
Här nedan är den fullständiga koden för exempelprogrammet "Generera och använda en krypteringsnyckel." Koden består av två delar.
I det här exemplet används klassen EncryptionKeyGenerator för att skapa en krypteringsnyckel från ett lösenord. Klassen EncryptionKeyGenerator ingår i AS3CoreLib-projektet med öppen källkod. Du kan hämta
tas3corelib-paketet inklusive källkod och dokumentation
. Du kan även hämta SWC-filerna och källkodsfilerna från projektsidan.
Flex-exempel
MXML-filen i programmet innehåller källkoden för ett enkelt program som skapar eller öppnar en anslutning till en krypterad databas:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="init();">
<mx:Script>
<![CDATA[
import com.adobe.air.crypto.EncryptionKeyGenerator;
private const dbFileName:String = "encryptedDatabase.db";
private var dbFile:File;
private var createNewDB:Boolean = true;
private var conn:SQLConnection;
// ------- Event handling -------
private function init():void
{
conn = new SQLConnection();
dbFile = File.applicationStorageDirectory.resolvePath(dbFileName);
if (dbFile.exists)
{
createNewDB = false;
instructions.text = "Enter your database password to open the encrypted database.";
openButton.label = "Open Database";
}
}
private function openConnection():void
{
var password:String = passwordInput.text;
var keyGenerator:EncryptionKeyGenerator = new EncryptionKeyGenerator();
if (password == null || password.length <= 0)
{
statusMsg.text = "Please specify a password.";
return;
}
if (!keyGenerator.validateStrongPassword(password))
{
statusMsg.text = "The password must be 8-32 characters long. It must contain at least one lowercase letter, at least one uppercase letter, and at least one number or symbol.";
return;
}
passwordInput.text = "";
passwordInput.enabled = false;
openButton.enabled = false;
var encryptionKey:ByteArray = keyGenerator.getEncryptionKey(password);
conn.addEventListener(SQLEvent.OPEN, openHandler);
conn.addEventListener(SQLErrorEvent.ERROR, openError);
conn.openAsync(dbFile, SQLMode.CREATE, null, false, 1024, encryptionKey);
}
private function openHandler(event:SQLEvent):void
{
conn.removeEventListener(SQLEvent.OPEN, openHandler);
conn.removeEventListener(SQLErrorEvent.ERROR, openError);
statusMsg.setStyle("color", 0x009900);
if (createNewDB)
{
statusMsg.text = "The encrypted database was created successfully.";
}
else
{
statusMsg.text = "The encrypted database was opened successfully.";
}
}
private function openError(event:SQLErrorEvent):void
{
conn.removeEventListener(SQLEvent.OPEN, openHandler);
conn.removeEventListener(SQLErrorEvent.ERROR, openError);
if (!createNewDB && event.error.errorID == EncryptionKeyGenerator.ENCRYPTED_DB_PASSWORD_ERROR_ID)
{
statusMsg.text = "Incorrect password!";
}
else
{
statusMsg.text = "Error creating or opening database.";
}
}
]]>
</mx:Script>
<mx:Text id="instructions" text="Enter a password to create an encrypted database. The next time you open the application, you will need to re-enter the password to open the database again." width="75%" height="65"/>
<mx:HBox>
<mx:TextInput id="passwordInput" displayAsPassword="true"/>
<mx:Button id="openButton" label="Create Database" click="openConnection();"/>
</mx:HBox>
<mx:Text id="statusMsg" color="#990000" width="75%"/>
</mx:WindowedApplication>
Flash Professional-exempel
FLA-filen i programmet innehåller källkoden för ett enkelt program som skapar eller öppnar en anslutning till en krypterad databas. FLA-filen har fyra komponenter som är placerade på scenen:
Instansnamn
|
Komponenttyp
|
Beskrivning
|
instruktioner
|
Label
|
Innehåller de instruktioner som lämnats till användaren
|
passwordInput
|
TextInput
|
Inmatningsfält där användaren anger lösenordet
|
openButton
|
Button
|
Knapp som användaren klickar på efter att lösenordet har angetts
|
statusMsg
|
Label
|
Visar statusmeddelanden (fel eller godkänt)
|
Programmets kod är angiven på en nyckelbildruta på ruta 1 på huvudtidslinjen. Programmets kod följer nedan:
import com.adobe.air.crypto.EncryptionKeyGenerator;
const dbFileName:String = "encryptedDatabase.db";
var dbFile:File;
var createNewDB:Boolean = true;
var conn:SQLConnection;
init();
// ------- Event handling -------
function init():void
{
passwordInput.displayAsPassword = true;
openButton.addEventListener(MouseEvent.CLICK, openConnection);
statusMsg.setStyle("textFormat", new TextFormat(null, null, 0x990000));
conn = new SQLConnection();
dbFile = File.applicationStorageDirectory.resolvePath(dbFileName);
if (dbFile.exists)
{
createNewDB = false;
instructions.text = "Enter your database password to open the encrypted database.";
openButton.label = "Open Database";
}
else
{
instructions.text = "Enter a password to create an encrypted database. The next time you open the application, you will need to re-enter the password to open the database again.";
openButton.label = "Create Database";
}
}
function openConnection(event:MouseEvent):void
{
var keyGenerator:EncryptionKeyGenerator = new EncryptionKeyGenerator();
var password:String = passwordInput.text;
if (password == null || password.length <= 0)
{
statusMsg.text = "Please specify a password.";
return;
}
if (!keyGenerator.validateStrongPassword(password))
{
statusMsg.text = "The password must be 8-32 characters long. It must contain at least one lowercase letter, at least one uppercase letter, and at least one number or symbol.";
return;
}
passwordInput.text = "";
passwordInput.enabled = false;
openButton.enabled = false;
var encryptionKey:ByteArray = keyGenerator.getEncryptionKey(password);
conn.addEventListener(SQLEvent.OPEN, openHandler);
conn.addEventListener(SQLErrorEvent.ERROR, openError);
conn.openAsync(dbFile, SQLMode.CREATE, null, false, 1024, encryptionKey);
}
function openHandler(event:SQLEvent):void
{
conn.removeEventListener(SQLEvent.OPEN, openHandler);
conn.removeEventListener(SQLErrorEvent.ERROR, openError);
statusMsg.setStyle("textFormat", new TextFormat(null, null, 0x009900));
if (createNewDB)
{
statusMsg.text = "The encrypted database was created successfully.";
}
else
{
statusMsg.text = "The encrypted database was opened successfully.";
}
}
function openError(event:SQLErrorEvent):void
{
conn.removeEventListener(SQLEvent.OPEN, openHandler);
conn.removeEventListener(SQLErrorEvent.ERROR, openError);
if (!createNewDB && event.error.errorID == EncryptionKeyGenerator.ENCRYPTED_DB_PASSWORD_ERROR_ID)
{
statusMsg.text = "Incorrect password!";
}
else
{
statusMsg.text = "Error creating or opening database.";
}
}
EncryptionKeyGenerator-klassen
Det är inte nödvändigt att förstå EncryptionKeyGenerator-klassen i detalj för att använda den till att skapa en säker krypteringsnyckel till din programdatabas. Sättet du använder klassen på förklaras i
Använda klassen EncryptionKeyGenerator för att få en säker krypteringsnyckel
. Det kan dock vara värdefullt att förstå teknikerna som klassen använder. Du kanske vill anpassa klassen eller införliva vissa av dess tekniker för tillfällen då en annan sekretessnivå är nödvändig.
Klassen EncryptionKeyGenerator ingår i AS3CoreLib-projektet med öppen källkod. Du kan hämta
as3corelib-paketet inklusive källkod och dokumentation
.Du kan även se källkoden på projektwebbplatsen eller ladda ned den för att arbeta med vägledning av förklaringarna.
När koden skapar en EncryptionKeyGenerator-instans och anropar dess
getEncryptionKey()
-metod vidtas flera steg för att säkerställa att bara rätt användare kan komma åt data. Processen är samma vid generering av en krypteringsnyckel från ett användarangivet lösenord innan databasen skapas, samt vid återskapande av krypteringsnyckeln för att öppna databasen.
Begära och validera ett starkt lösenord
När koden anropar
getEncryptionKey()
-metoden skickas ett lösenord som en parameter. Lösenordet används som bas för krypteringsnyckeln. Genom att använda information som bara användaren känner till försäkrar den här designen att endast användare som har tillgång till lösenordet kan komma åt data i databasen. Även om en angripare lyckas komma åt användarens konto på datorn måste han eller hon känna till lösenordet för att få åtkomst till databasen. Som en extra säkerhetsåtgärd lagras lösenordet aldrig av programmet.
Koden för programmet skapar en EncryptionKeyGenerator-instans och anropar metoden
getEncryptionKey()
, vilket skickar det användarangivna lösenordet som ett argument (variabeln
password
i det här exemplet):
var keyGenerator:EncryptionKeyGenerator = new EncryptionKeyGenerator();
var encryptionKey:ByteArray = keyGenerator.getEncryptionKey(password);
Första steget som EncryptionKeyGenerator-klassen tar när
getEncryptionKey()
-metoden anropas är att kontrollera det användarangivna lösenordet för att se till att det motsvarar kraven på starka lösenord. EncryptionKeyGenerator-klassen kräver att ett lösenord är 8–32 tecken långt. Lösenordet måste innehålla både versaler och gemener och minst en siffra eller symbol.
Det reguljära uttrycket som kontrollerar det här mönstret är definierat som en konstant som heter
STRONG_PASSWORD_PATTERN
:
private static const STRONG_PASSWORD_PATTERN:RegExp = /(?=^.{8,32}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/;
Koden som kontrollerar lösenordet finns i EncryptionKeyGenerator-klassens
validateStrongPassword()
-metod. Koden är som följer:
public function vaidateStrongPassword(password:String):Boolean
{
if (password == null || password.length <= 0)
{
return false;
}
return STRONG_PASSWORD_PATTERN.test(password))
}
Metoden
getEncryptionKey()
anropar internt EncryptionKeyGenerator-klassens
validateStrongPassword()
-metod och genererar ett undantag om lösenordet är ogiltigt. Metoden
validateStrongPassword()
är en offentlig metod så att programkod kan kontrollera ett lösenord utan att anropa
getEncryptionKey()
-metoden för att undvika att ett fel inträffar.
Utöka lösenordet till 256 bitar
Längre fram i processen finns instruktioner som anger att lösenordet måste vara 256 bitar långt. I stället för att tvinga varje användare att ange ett lösenord som är exakt 256 bitar (32 tecken) skapar koden ett längre lösenord genom att upprepa tecknen i lösenordet.
Metoden
getEncryptionKey()
anropar
concatenatePassword()
-metoden för att den ska skapa det långa lösenordet.
var concatenatedPassword:String = concatenatePassword(password);
Följande kod gäller för
concatenatePassword()
-metoden:
private function concatenatePassword(pwd:String):String
{
var len:int = pwd.length;
var targetLength:int = 32;
if (len == targetLength)
{
return pwd;
}
var repetitions:int = Math.floor(targetLength / len);
var excess:int = targetLength % len;
var result:String = "";
for (var i:uint = 0; i < repetitions; i++)
{
result += pwd;
}
result += pwd.substr(0, excess);
return result;
}
Om lösenordet är kortare än 256 bitar drar koden samman lösenordet (konkatenerar) så att det blir 256 bitar. Om längden inte blir exakt 256 bitar kortas den sista konkateneringen av så att lösenordet blir exakt 256 bitar.
Generera eller hämta ett 256-bitars salt-värde
Nästa steg är att få fram ett 256-bitars salt-värde som i ett senare steg kombineras med lösenordet. Ett
salt
-värde är ett slumpmässigt värde som läggs till eller kombineras med ett användardefinierat värde för att generera ett lösenord. Även om en användare väljer ett vanligt ord som lösenord garanterar "lösenord och salt-värde"-kombinationen att ett slumpmässigt värde används av systemet. Denna slumpmässighet hjälper till att skydda mot en ordlisteattack, där en angripare använder en lista med ord för att hitta ett lösenord. Genom att generera salt-värdet och lagra det på den krypterade lokala lagringsplatsen kopplas det dessutom till användarens konto på datorn där databasfilen finns.
Om programmet anropar
getEncryptionKey()
-metoden för första gången skapar koden ett slumpmässigt 256-bitars salt-värde. Annars läser koden in salt-värdet från den krypterade lokala lagringsplatsen.
Salt-värdet lagras i en variabel som heter
salt
. Koden avgör om ett salt-värde redan har skapats genom att försöka att läsa in salt-värdet från den krypterade lokala lagringsplatsen:
var salt:ByteArray = EncryptedLocalStore.getItem(saltKey);
if (salt == null)
{
salt = makeSalt();
EncryptedLocalStore.setItem(saltKey, salt);
}
Om koden skapar ett nytt salt-värde genererar
makeSalt()
-metoden ett slumpmässigt 256-bitars värde. Eftersom värdet lagras på den krypterade lokala lagringsplatsen genereras det som ett ByteArray-objekt.
makeSalt()
-metoden använder
Math.random()
-metoden för att generera det slumpmässiga värdet.
Math.random()
-metoden kan inte generera 256 bitar på en gång. I stället används en slinga som anropar
Math.random()
åtta gånger. Varje gång genereras ett slumpmässigt uint-värde mellan 0 och 4294967295 (det högsta uint-värdet). Ett uint-värde används av praktiska skäl eftersom det innehåller exakt 32 bitar. Ett 256-bitars värde genereras genom att åtta uint-värden skrivs till bytearrayen. Här följer koden för
makeSalt()
-metoden:
private function makeSalt():ByteArray
{
var result:ByteArray = new ByteArray;
for (var i:uint = 0; i < 8; i++)
{
result.writeUnsignedInt(Math.round(Math.random() * uint.MAX_VALUE));
}
return result;
}
När koden sparar salt-värdet till den krypterade lokala lagringsplatsen (ELS) eller hämtar salt-värdet från ELS, behövs en String-nyckel som salt-värdet sparas under. Salt-värdet kan inte hämtas om nyckeln är okänd. Om det är fallet kan inte krypteringsnyckeln återskapas varje gång för att öppna om databasen. Som standard använder EncryptionKeyGenerator en fördefinierad ELS-nyckel som definieras i konstanten
SALT_ELS_KEY
. I stället för att använda standardnyckeln kan programkoden ange en ELS-nyckel som används i anropet till
getEncryptionKey()
-metoden. Standardnyckeln eller den programanpassade salt-ELS-nyckeln lagras i en variabel som heter
saltKey
. Variabeln används i anropen till
EncryptedLocalStore.setItem()
och
EncryptedLocalStore.getItem()
, som visades tidigare.
Kombinera 256-bitars lösenordet och salt-värdet med hjälp av XOR-operatorn
Koden har nu ett 256-bitars lösenord och ett 256-bitars salt-värde. Sedan använder det en XOR-åtgärd i bitform för att kombinera salt-värdet och det konkatenerade lösenordet till ett enda värde. Den här tekniken skapar ett 256-bitars lösenord som består av tecken från hela spektrat av möjliga tecken. Den här regeln har värdet true även om det faktiska lösenordet mest sannolikt består av alfanumeriska tecken till största delen. Den här ökade slumpmässigheten har fördelen att uppsättningen möjliga lösenord blir stor utan att användaren måste ange ett långt och komplext lösenord.
Resultatet av XOR-åtgärden lagras i variabeln
unhashedKey
. Själva processen med att kombinera de båda värdena med hjälp av en XOR-åtgärd sker i
xorBytes()
-metoden:
var unhashedKey:ByteArray = xorBytes(concatenatedPassword, salt);
Den bitvisa XOR-operatorn (
^
) tar två uint-värden och returnerar ett uint-värde. (Ett uint-värde består av 32 bitar.) Indatavärdena som skickas som argument till
xorBytes()
-metoden är en sträng (lösenordet) och en bytearray (salt-värdet). En slinga används för att extrahera 32 bitar från varje indatavärde som ska kombineras med hjälp av XOR-operatorn.
private function xorBytes(passwordString:String, salt:ByteArray):ByteArray
{
var result:ByteArray = new ByteArray();
for (var i:uint = 0; i < 32; i += 4)
{
// ...
}
return result;
}
Inom loopen extraheras de första 32 bitarna (4 byte) från
passwordString
-parametern. Dessa bitar extraheras och konverteras till ett uint-värde (
o1
) i en tvåstegsprocess. Först hämtar
charCodeAt()
-metoden varje teckens numeriska värde. Därefter flyttas värdet till rätt position i uint med hjälp av operatorn för flyttning till vänster (
<<
) och det flyttade värdet läggs till
o1
. Det första tecknet (
i
) blir exempelvis de första 8 bitarna genom att bitarna flyttas 24 bitar åt vänster med hjälp av operatorn för flyttning till vänster (
<<
) och genom att värdet sedan associeras med
o1
. Det andra tecknet
(i + 1
) blir de efterföljande 8 bitarna genom att tillhörande värde flyttas 16 bitar åt vänster och genom att resultatet sedan läggs till
o1
. Värdena för det tredje och fjärde tecknet läggs till på samma sätt.
// ...
// Extract 4 bytes from the password string and convert to a uint
var o1:uint = passwordString.charCodeAt(i) << 24;
o1 += passwordString.charCodeAt(i + 1) << 16;
o1 += passwordString.charCodeAt(i + 2) << 8;
o1 += passwordString.charCodeAt(i + 3);
// ...
Variabeln
o1
innehåller nu 32 bitar från parametern
passwordString
. Därefter extraheras 32 bitar från parametern
salt
genom att dess
readUnsignedInt()
-metod anropas. De 32 bitarna lagras i uint-variabeln
o2
.
// ...
salt.position = i;
var o2:uint = salt.readUnsignedInt();
// ...
Slutligen kombineras de två 32-bitarsvärdena (uint) med XOR-operatorn och resultatet skrivs till en ByteArray som heter
result
.
// ...
var xor:uint = o1 ^ o2;
result.writeUnsignedInt(xor);
// ...
När slingan slutförts returneras den bytearray som innehåller XOR-resultatet.
// ...
}
return result;
}
Hash-koda nyckeln
När det konkatenerade lösenordet och salt-värdet har kombinerats är nästa steg att ytterligare säkra det här värdet genom att hash-koda det med hash-algoritmen SHA-256. Hash-kodningen försvårar reverse engineering av en potentiell angripare.
I det här skedet har koden en ByteArray som heter
unhashedKey
och innehåller det konkatenerade lösenordet kombinerat med salt-värdet. Core-biblioteksprojektet för ActionScript 3.0 (as3corelib) innehåller en SHA256-klass i paketet com.adobe.crypto. Metoden
SHA256.hashBytes()
utför en SHA-256-hash-kodning på en ByteArray och returnerar ett String-värde med hash-kodningsresultatet på 256 bitar som ett hexadecimalt tal. Klassen EncryptionKeyGenerator använder SHA256-klassen för att hash-koda nyckeln:
var hashedKey:String = SHA256.hashBytes(unhashedKey);
Extrahera krypteringsnyckeln från hashen
Krypteringsnyckeln måste vara en bytearray som är exakt 16 byte (128 bitar) lång. Resultatet av SHA-256-hash-algoritmen är alltid 256 bitar långt. Följaktligen är det sista steget att välja 128 bitar från det hash-kodade resultatet som ska användas som den faktiska krypteringsnyckeln.
I EncryptionKeyGenerator-klassen reducerar koden nyckeln till 128 bitar genom att anropa
generateEncryptionKey()
-metoden. Sedan returneras metodens resultat som resultatet för
getEncryptionKey()
-metoden:
var encryptionKey:ByteArray = generateEncryptionKey(hashedKey);
return encryptionKey;
Du måste inte använda de första 128 bitarna som krypteringsnyckeln. Du kan välja från ett urval bitar med godtycklig startplats, du kan välja alla bitar eller använda ett annat sätt att välja bitar. Det viktiga är att 128 olika bitar väljs, och att samma 128 bitar används varje gång.
I det här fallet använder
generateEncryptionKey()
-metoden urvalet av bitar med start vid 18:e byte som krypteringsnyckeln. Som vi nämnt tidigare returnerar SHA256-klassen en sträng som innehåller en 256-bitars hash i form av ett hexadecimalt tal. Ett enskilt block med 128 bitar har för många byte för att alla ska kunna läggas till i en bytearray på en gång. Av den anledningen används en
for
-slinga för att extrahera tecken från den hexadecimala strängen, konvertera dem till numeriska värden och lägga till dem i bytearrayen. Det resulterande String-värdet för SHA-256 är 64 tecken långt. Ett intervall med 128 bitar är lika med 32 tecken i String-värdet och varje tecken representerar 4 bitar. Den minsta dataökningen som du kan lägga till i en bytearray är en byte (8 bitar), vilket motsvarar två tecken i
hash
-strängen. Slingan räknar följaktligen från 0 till 31 (32 tecken) i steg om 2 tecken.
I slingan börjar koden med att fastställa startpunkten för det aktuella teckenparet. Eftersom vi vill börja intervallet vid tecknet på indexposition 17 (den 18:e byten) tilldelas
position
-variabeln det aktuella iteratorvärdet (
i
) plus 17. De två tecknen på den aktuella positionen extraheras med hjälp av strängobjektets
substr()
-metod. Tecknen lagras i variabeln
hex
. Därefter används
parseInt()
-metoden för att konvertera
hex
-strängen till ett decimalt heltal. Det lagrar det värdet i int-variabeln
byte
. Till sist lägger koden till värdet i
byte
till ByteArrayen
result
genom att använda
writeByte()
-metoden. När slingan slutförts innehåller bytearrayen
result
16 byte och kan användas som krypteringsnyckel för en databas.
private function generateEncryptionKey(hash:String):ByteArray
{
var result:ByteArray = new ByteArray();
for (var i:uint = 0; i < 32; i += 2)
{
var position:uint = i + 17;
var hex:String = hash.substr(position, 2);
var byte:int = parseInt(hex, 16);
result.writeByte(byte);
}
return result;
}
|
|
|