SQL 데이터베이스에서 암호화 사용

Adobe AIR 1.5 이상

모든 Adobe AIR 응용 프로그램은 동일한 로컬 데이터베이스 엔진을 공유합니다. 따라서 모든 AIR 응용 프로그램에서 암호화되지 않은 데이터베이스 파일에 연결하고, 읽고, 쓸 수 있습니다. Adobe AIR 1.5부터 AIR에는 암호화된 데이터베이스 파일을 만들고 연결하는 기능이 포함되어 있습니다. 암호화된 데이터베이스를 사용하는 경우 데이터베이스에 연결하려면 응용 프로그램에서 올바른 암호화 키를 제공해야 합니다. 잘못된 암호화 키를 제공하거나 키를 제공하지 않으면 응용 프로그램이 데이터베이스에 연결할 수 없습니다. 따라서 응용 프로그램은 데이터베이스에서 데이터를 읽어오거나 데이터베이스에 데이터를 쓰거나 데이터를 변경할 수 없습니다.

암호화된 데이터베이스를 사용하려면 데이터베이스를 암호화된 데이터베이스로 만들어야 합니다. 기존의 암호화된 데이터베이스를 사용하여 데이터베이스에 대한 연결을 열 수 있습니다. 또한 암호화된 데이터베이스의 암호화 키를 변경할 수도 있습니다. 암호화된 데이터베이스를 만들고 연결한다는 점을 제외하고는 암호화된 데이터베이스로 작업하기 위한 기술은 암호화되지 않은 데이터베이스로 작업할 때와 동일합니다. 특히, 데이터베이스의 암호화 여부와 관계없이 동일하게 SQL 문을 실행합니다.

암호화된 데이터베이스 사용

데이터베이스에 저장된 정보에 대한 액세스를 제한하려는 경우 언제든지 암호화를 사용할 수 있습니다. Adobe AIR의 데이터베이스 암호화 기능은 여러 목적으로 사용할 수 있습니다. 다음은 암호화된 데이터베이스를 사용할 수 있는 경우의 몇 가지 예입니다.

  • 서버에서 다운로드한 개인 응용 프로그램 데이터의 읽기 전용 캐시

  • 서버와 동기화되는 개인 데이터의 로컬 응용 프로그램 저장소(데이터가 서버로 전송되고 서버에서 로드됨)

  • 응용 프로그램에서 만들고 편집한 문서의 파일 형식으로 사용되는 암호화된 파일. 이 파일은 한 사용자만 사용하거나 응용 프로그램의 모든 사용자가 공유하도록 설계할 수 있습니다.

  • 로컬 데이터 저장소의 다른 사용 방법(예: 로컬 SQL 데이터베이스의 용도에서 설명한 것과 같이 시스템이나 데이터베이스 파일에 액세스할 수 있는 사용자로부터 데이터를 비공개로 유지해야 하는 경우)

암호화된 데이터베이스를 사용하려는 이유를 알고 있으면 응용 프로그램을 구축하는 방법을 결정하는 데 도움이 됩니다. 특히 이러한 결정은 응용 프로그램에서 데이터베이스의 암호화 키를 만들거나 가져오거나 저장하는 방법에 영향을 줄 수 있습니다. 이러한 고려 사항에 대한 자세한 내용은 데이터베이스에서 암호화를 사용할 때 고려할 사항을 참조하십시오.

암호화된 데이터베이스 외에 중요한 데이터를 비공개로 유지하는 다른 메커니즘은 암호화된 로컬 저장소입니다. 암호화된 로컬 저장소에서는 단일 ByteArray 값을 String 키를 사용하여 저장합니다. 암호화된 로컬 저장소에 값을 저장하는 AIR 응용 프로그램 및 해당 저장소가 있는 컴퓨터에서만 값에 액세스할 수 있습니다. 따라서 암호화된 로컬 저장소에서는 고유 암호화 키를 만들 필요가 없습니다. 이러한 이유 때문에 암호화된 로컬 저장소는 ByteArray로 쉽게 인코딩할 수 있는 값 집합이나 단일 값을 쉽게 저장하는 데 가장 적합합니다. 암호화된 데이터베이스는 구조적 데이터 저장소 및 쿼리가 필요한 큰 데이터 집합에 가장 적합합니다. 암호화된 로컬 저장소 사용에 대한 자세한 내용은 암호화된 로컬 저장소를 참조하십시오.

암호화된 데이터베이스 만들기

암호화된 데이터베이스를 사용하려면 데이터베이스 파일을 만들 때 암호화해야 합니다. 데이터베이스를 암호화되지 않은 상태로 만들면 나중에 암호화할 수 없습니다. 마찬가지로 암호화된 데이터베이스를 나중에 암호화되지 않게 만들 수 없습니다. 필요한 경우 암호화된 데이터베이스의 암호화 키는 변경할 수 있습니다. 자세한 내용은 데이터베이스의 암호화 키 변경을 참조하십시오. 암호화되지 않은 기존 데이터베이스가 있을 경우 데이터베이스 암호화를 사용하려면 암호화된 데이터베이스를 새로 만들고 기존 테이블 구조 및 데이터를 새 데이터베이스로 복사할 수 있습니다.

암호화된 데이터베이스를 만드는 방법은 데이터베이스 생성에 설명된 암호화되지 않은 데이터베이스를 만드는 방법과 거의 동일합니다. 우선 데이터베이스에 대한 연결을 나타내는 SQLConnection 인스턴스를 만듭니다. 그리고 SQLConnection 객체의 open() 메서드나 openAsync() 메서드를 호출하여 데이터베이스를 만듭니다. 이때 아직 존재하지 않는 파일을 데이터베이스 위치로 지정합니다. 암호화된 데이터베이스를 만들 때는 encryptionKey 매개 변수(open() 메서드의 다섯 번째 매개 변수 및 openAsync() 메서드의 여섯 번째 매개 변수)에 값을 지정한다는 점만 다릅니다.

유효한 encryptionKey 매개 변수 값은 정확히 16바이트를 포함하는 ByteArray 객체입니다.

다음 예제에서는 암호화된 데이터베이스를 만드는 방법을 보여 줍니다. 간단하게 이 예제에서는 암호화 키를 응용 프로그램 코드에 하드 코딩합니다. 그러나 이 방법은 안전하지 않으므로 사용하지 않는 것이 좋습니다.

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);

암호화 키를 생성하는 좋은 방법을 보여 주는 예제는 예: 암호화 키 생성 및 사용을 참조하십시오.

암호화된 데이터베이스 연결

암호화된 데이터베이스를 만드는 것과 마찬가지로 암호화된 데이터베이스에 대한 연결을 여는 절차는 암호화되지 않은 데이터베이스에 연결할 때와 동일합니다. 이 절차에 대해서는 데이터베이스 연결에서 자세히 설명합니다. open() 메서드를 사용하여 동기 실행 모드에서 이 연결을 열거나openAsync() 메서드를 사용하여 비동기 실행 모드에서 이 연결을 엽니다. 암호화된 데이터베이스를 열려면 encryptionKey 매개 변수(open() 메서드의 다섯 번째 매개 변수 및 openAsync() 메서드의 여섯 번째 매개 변수)에 올바른 값을 지정한다는 점만 다릅니다.

제공한 암호화 키가 올바르지 않으면 오류가 발생합니다. open() 메서드의 경우 SQLError 예외가 발생되고, openAsync() 메서드의 경우 SQLConnection객체가 SQLErrorEvent를 전달하여 이 이벤트의 error 속성에 SQLError 객체가 포함됩니다. 두 경우 모두 예외에서 생성되는 SQLError 객체의 errorID 속성 값은 3138입니다. 이 오류 ID는 오류 메시지 "연 파일은 데이터베이스 파일이 아닙니다"에 해당합니다.

다음 예제에서는 암호화된 데이터베이스를 비동기 실행 모드로 여는 방법을 보여 줍니다. 간단하게 이 예제에서는 암호화 키를 응용 프로그램 코드에 하드 코딩합니다. 그러나 이 방법은 안전하지 않으므로 사용하지 않는 것이 좋습니다.

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); 
    } 
} 

다음 예제에서는 암호화된 데이터베이스를 동기 실행 모드로 여는 방법을 보여 줍니다. 간단하게 이 예제에서는 암호화 키를 응용 프로그램 코드에 하드 코딩합니다. 그러나 이 방법은 안전하지 않으므로 사용하지 않는 것이 좋습니다.

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); 
    } 
} 

암호화 키를 생성하는 좋은 방법을 보여 주는 예제는 예: 암호화 키 생성 및 사용을 참조하십시오.

데이터베이스의 암호화 키 변경

데이터베이스를 암호화하는 경우 나중에 데이터베이스의 암호화 키를 변경할 수 있습니다. 데이터베이스의 암호화 키를 변경하려면 먼저 SQLConnection인스턴스를 만들고 이 인스턴스의 open() 또는 openAsync() 메서드를 호출하여 데이터베이스에 대한 연결을 엽니다. 데이터베이스에 연결되면 새 암호화 키를 인수로 전달하여 reencrypt() 메서드를 호출합니다.

대부분의 다른 데이터베이스 작업처럼 reencrypt() 메서드의 비헤이비어는 데이터베이스 연결에서 동기 실행 모드를 사용하는지 비동기 실행 모드를 사용하는지에 따라 달라집니다. open() 메서드를 사용하여 데이터베이스에 연결하는 경우 reencrypt() 작업이 동기적으로 실행됩니다. 작업이 완료되면 코드의 다음 줄에서 실행이 계속됩니다.

var newKey:ByteArray = new ByteArray(); 
// ... generate the new key and store it in newKey 
conn.reencrypt(newKey);

반면 openAsync() 메서드를 사용하여 데이터베이스 연결을 여는 경우 reencrypt() 작업은 비동기적입니다. 즉, reencrypt()를 호출하면 재암호화 프로세스가 시작되고, 작업이 완료되면 SQLConnection 객체가 reencrypt 이벤트를 전달합니다. 재암호화가 완료되는 때를 확인하려면 이벤트 리스너를 사용합니다.

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() 작업은 자체의 트랜잭션에서 실행됩니다. 작업이 중단되거나 실패하면(예: 작업이 완료되기 전에 응용 프로그램이 닫히는 경우) 트랜잭션이 롤백됩니다. 이 경우 원래 암호화 키가 계속 데이터베이스의 암호화 키입니다.

reencrypt() 메서드를 사용하여 데이터베이스에서 암호화를 제거할 수는 없습니다. reencrypt() 메서드에 16바이트 ByteArray가 아닌 암호화 키나 null 값을 전달하면 오류가 발생합니다.

데이터베이스에서 암호화를 사용할 때 고려할 사항

암호화된 데이터베이스 사용 단원에서는 암호화된 데이터베이스를 사용해야 하는 여러 경우를 제시합니다. 분명한 점은 이러한 시나리오 및 다른 시나리오를 포함한 서로 다른 응용 프로그램에 대한 사용 시나리오마다 개인 정보 보호 요구 사항이 다르다는 것입니다. 응용 프로그램에서 암호화 사용을 어떻게 구축하는지에 따라 데이터베이스 데이터의 비공개 정도를 제어하는 데 상당한 영향을 미칩니다. 예를 들어 암호화된 데이터베이스를 사용하여 개인 데이터를 같은 시스템의 다른 사용자를 비롯한 다른 사용자에게 비공개로 유지하려면 각 사용자의 데이터베이스에 자체의 암호화 키가 있어야 합니다. 보안을 강화하기 위해 응용 프로그램에서 사용자가 입력한 암호를 사용하여 키를 생성할 수 있습니다. 암호화 키가 암호를 기반으로 하는 경우 다른 사용자가 시스템에서 사용자의 계정을 가장할 수 있더라도 여전히 데이터에 액세스할 수 없습니다. 개인 정보 보호 범위의 다른 일면으로 데이터베이스 파일을 응용 프로그램의 모든 사용자가 읽을 수 있게 하지만 다른 응용 프로그램에서는 읽을 수 없게 하려는 경우를 가정합니다. 이 경우 설치된 각 응용 프로그램 사본에서 공유 암호화 키에 액세스할 수 있어야 합니다.

응용 프로그램, 그리고 특히 암호화 키를 생성하는 데 사용하는 기술을 설계할 때 응용 프로그램 데이터에 필요한 개인 정보 보호 수준을 기준으로 할 수 있습니다. 다음 목록에서는 여러 데이터 개인 정보 보호 수준에 대한 디자인 제안을 제공합니다.

  • 모든 시스템에서 응용 프로그램에 액세스할 수 있는 모든 사용자가 데이터베이스에 액세스할 수 있게 하려면 응용 프로그램의 모든 인스턴스에 대해 사용할 수 있는 단일 키를 사용합니다. 예를 들어 처음으로 실행될 때 응용 프로그램은 SSL과 같은 보안 프로토콜을 사용하여 서버에서 공유 암호화 키를 다운로드할 수 있습니다. 그런 다음 키를 나중에 사용하기 위해 암호화된 로컬 저장소에 저장할 수 있습니다. 또는 데이터를 시스템의 사용자별로 암호화하고 서버와 같은 원격 데이터 저장소와 데이터를 동기화하여 데이터를 이식 가능하게 만듭니다.

  • 모든 시스템의 단일 사용자가 데이터베이스에 액세스할 수 있게 만들려면 사용자 비밀(예: 암호)을 사용하여 암호화 키를 생성합니다. 특히 특정 컴퓨터에 연결된 값(예: 암호화된 로컬 저장소에 저장된 값)을 사용하여 키를 생성하지 않습니다. 또는 데이터를 시스템의 사용자별로 암호화하고 서버와 같은 원격 데이터 저장소와 데이터를 동기화하여 데이터를 이식 가능하게 만듭니다.

  • 단일 시스템의 단일 사용자가 데이터베이스에 액세스할 수 있게 하려면 암호화 생성된 솔트를 사용하여 키를 생성합니다. 이 방법에 대한 예는 예: 암호화 키 생성 및 사용을 참조하십시오.

다음은 암호화된 데이터베이스를 사용할 응용 프로그램을 설계할 때 유념해야 할 추가 보안 고려 사항입니다.

  • 시스템 보안은 가장 약한 링크의 보안 수준으로만 유지됩니다. 사용자 입력 암호를 사용하여 암호화 키를 생성하는 경우 암호에 최소 길이 및 복잡도 제한 사항을 적용하는 것이 좋습니다. 기본 문자만 사용하는 짧은 암호는 쉽게 추측할 수 있습니다.

  • AIR 응용 프로그램의 소스 코드는 사용자 컴퓨터에 일반 텍스트(HTML 내용의 경우) 또는 쉽게 디컴파일 할 수 있는 이진 형식(SWF 내용의 경우)으로 저장됩니다. 소스 코드에 액세스할 수 있으므로 다음 두 가지 사항에 유념해야 합니다.

    • 암호화 키를 소스 코드에 하드 코딩하지 마십시오.

    • 암호화 키를 생성하는 데 사용하는 기술(예: 임의의 문자 생성기 또는 특정 해시 알고리즘)은 공격자가 쉽게 파악할 수 있는 것으로 항상 간주하십시오.

  • AIR 데이터베이스 암호화에서는 CCM(Counter with CBC-MAC) 모드와 함께 AES(Advanced Encryption Standard)를 사용합니다. 이 암호화 암호에서는 보안을 위해 사용자가 입력한 키를 솔트 값과 결합해야 합니다. 이 방법에 대한 예는 예: 암호화 키 생성 및 사용을 참조하십시오.

  • 데이터베이스를 암호화하는 경우 데이터베이스 엔진에서 사용하는 모든 디스크 파일이 데이터베이스와 함께 암호화됩니다. 그러나 데이터베이스 엔진에서는 트랜잭션의 읽기 및 쓰기 시간 성능을 개선하기 위해 일부 데이터를 메모리 내 캐시에 보관합니다. 메모리에 상주하는 모든 데이터는 암호화되지 않습니다. 공격자가 디버거를 사용하는 등의 방법으로 AIR 응용 프로그램에서 사용하는 메모리에 액세스할 수 있으면 현재 열려 있고 암호화되지 않은 데이터베이스의 데이터를 사용할 수 있게 됩니다.

예: 암호화 키 생성 및 사용

다음 예제 응용 프로그램에서는 암호화 키를 생성하는 한 가지 방법을 보여 줍니다. 이 응용 프로그램은 사용자 데이터에 대한 가장 높은 수준의 개인 정보 보호 및 보안을 제공하도록 설계됩니다. 개인 데이터를 보호하기 위한 중요한 방법 한 가지는 응용 프로그램에서 데이터베이스에 연결할 때마다 사용자에게 암호를 입력하도록 요구하는 것입니다. 이러한 맥락에서 높은 수준의 개인 정보 보호가 필요한 응용 프로그램에서는 다음 예제와 같이 데이터베이스 암호화 키를 직접 저장하지 않아야 합니다.

다음 응용 프로그램은 암호화 키를 생성하는 ActionScript 클래스(EncryptionKeyGenerator 클래스)와 이 클래스를 사용하는 방법을 보여주는 기본 사용자 인터페이스의 두 부분으로 구성되어 있습니다. 전체 소스 코드는 암호화 키를 생성 및 사용하기 위한 전체 예제 코드를 참조하십시오.

EncryptionKeyGenerator 클래스를 사용하여 보안 암호화 키 생성

응용 프로그램에서 사용하기 위해 EncryptionKeyGenerator 클래스의 자세한 작동 방식을 이해할 필요는 없습니다. 이 클래스가 데이터베이스의 암호화 키를 생성하는 방식에 대한 자세한 내용은 EncryptionKeyGenerator 클래스 이해를 참조하십시오.

응용 프로그램에 EncryptionKeyGenerator 클래스를 사용하려면 다음 단계를 수행하십시오.

  1. EncryptionKeyGenerator 클래스를 소스 코드 또는 컴파일된 SWC로 다운로드합니다. EncryptionKeyGenerator 클래스는 오픈 소스 ActionScript 3.0 기본 라이브러리(as3corelib) 프로젝트에 포함되어 있습니다. 소스 코드와 설명서를 포함한 as3corelib 패키지를 다운로드할 수 있습니다. 또한 이 프로젝트 페이지에서 SWC 또는 소스 코드 파일도 다운로드할 수 있습니다.

  2. EncryptionKeyGenerator 클래스의 소스 코드(as3corelib SWC)를 응용 프로그램 소스 코드에서 찾을 수 있는 위치에 배치합니다.

  3. 응용 프로그램 소스 코드에 EncryptionKeyGenerator 클래스를 가져오기 위한 import 문을 추가합니다.

    import com.adobe.air.crypto.EncryptionKeyGenerator;
  4. 코드에서 데이터베이스를 만들거나 데이터베이스에 대한 연결을 여는 코드 이전에 EncryptionKeyGenerator() 생성자를 호출하여 EncryptionKeyGenerator 인스턴스를 만드는 코드를 추가합니다.

    var keyGenerator:EncryptionKeyGenerator = new EncryptionKeyGenerator();
  5. 사용자에게서 암호를 입력받습니다.

    var password:String = passwordInput.text; 
     
    if (!keyGenerator.validateStrongPassword(password)) 
    { 
        // display an error message 
        return; 
    }

    이 암호는 EncryptionKeyGenerator 인스턴스가 다음 단계에서 암호화 키를 생성하는 데 기반으로 사용됩니다. EncryptionKeyGenerator 인스턴스는 특정 강력한 암호 유효성 검사 요구 사항을 기준으로 암호를 테스트합니다. 유효성 검사에 실패하면 오류가 발생합니다. 예제 코드와 같이 EncryptionKeyGenerator 객체의 validateStrongPassword() 메서드를 호출하여 암호를 미리 검사할 수 있습니다. 이러한 방식으로 암호가 강력한 암호를 위한 최소 요구 사항을 충족하는지 확인하고 오류를 방지할 수 있습니다.

  6. 암호를 기반으로 암호화 키를 생성합니다.

    var encryptionKey:ByteArray = keyGenerator.getEncryptionKey(password);

    getEncryptionKey() 메서드는 암호화 키(16바이트 ByteArray)를 생성하여 반환합니다. 이제 이 암호화 키를 사용하여 암호화된 데이터베이스를 새로 만들거나 기존 암호화된 데이터베이스를 열 수 있습니다.

    getEncryptionKey() 메서드에는 필수 매개 변수가 하나 있는데 이는 5단계에서 가져온 암호입니다.

    참고: 최고 수준의 개인 정보 보호 및 보안을 유지하려면 응용 프로그램에서 데이터베이스에 연결할 때마다 사용자에게 암호를 입력하도록 요구해야 합니다. 사용자 암호 또는 데이터베이스 암호화 키를 직접 저장하지 마십시오. 저장할 경우 보안 위험을 유발할 수 있습니다. 대신 이 예제와 같이, 응용 프로그램에서 암호화된 데이터베이스를 만들 때와 만든 데이터베이스에 연결할 때 모두 암호를 기반으로 암호를 생성하는 방법을 사용해야 합니다.

    getEncryptionKey() 메서드는 선택적인 두 번째 매개 변수인 overrideSaltELSKey도 받습니다. EncryptionKeyGenerator는 암호화 키의 일부로 사용되는 솔트라고 하는 임의의 값을 만듭니다. 솔트 값은 암호화 키를 다시 만들 수 있도록 AIR 응용 프로그램의 암호화된 로컬 저장소(ELS)에 저장됩니다. 기본적으로 EncryptionKeyGenerator 클래스는 특정 String을 ELS 키로 사용하며 그럴 가능성은 적지만 이 기본 ELS 키가 응용 프로그램에서 사용하는 다른 키와 충돌할 수 있습니다. 따라서 기본 키를 사용하는 대신 고유 ELS 키를 지정하는 것도 좋습니다. 이렇게 하려면 다음과 같이 두 번째 getEncryptionKey() 매개 변수로 전달하여 사용자 정의 키를 지정하십시오.

    var customKey:String = "My custom ELS salt key"; 
    var encryptionKey:ByteArray = keyGenerator.getEncryptionKey(password, customKey);
  7. 데이터베이스 만들기 또는 열기

    getEncryptionKey() 메서드가 반환한 암호화 키를 사용하여 코드에서 암호화된 데이터베이스를 새로 만들거나 기존 암호화된 데이터베이스를 열 수 있습니다. 두 경우 모두 암호화된 데이터베이스 만들기암호화된 데이터베이스 연결에서 설명한 대로 SQLConnection 클래스의 open() 또는 openAsync() 메서드를 사용합니다.

    이 예제의 응용 프로그램은 데이터베이스를 비동기 실행 모드로 열도록 설계되었습니다. 다음 코드에서는 적절한 이벤트 리스너를 설정하고 암호화 키를 최종 인수로 전달하여 openAsync() 메서드를 호출합니다.

    conn.addEventListener(SQLEvent.OPEN, openHandler); 
    conn.addEventListener(SQLErrorEvent.ERROR, openError); 
     
    conn.openAsync(dbFile, SQLMode.CREATE, null, false, 1024, encryptionKey);

    리스너 메서드의 코드에서는 이벤트 리스너 등록을 제거합니다. 그런 다음 데이터베이스가 생성되었는지, 열렸는지 또는 오류가 발생했는지를 나타내는 상태 메시지를 표시합니다. 이러한 이벤트 핸들러에서 가장 주목할 부분은 openError() 메서드입니다. 이 메서드에서는 if 문이 데이터베이스가 존재하는지 확인하고(기존 데이터베이스에 연결 시도) 오류 ID가 상수 EncryptionKeyGenerator.ENCRYPTED_DB_PASSWORD_ERROR_ID와 일치하는지 확인합니다. 이러한 조건이 모두 충족되는 경우 사용자가 입력한 암호가 올바르지 않을 수 있습니다. 또한 지정한 파일이 아예 데이터베이스 파일이 아닐 수도 있습니다. 다음은 오류 ID를 확인하는 코드입니다.

    if (!createNewDB && event.error.errorID == EncryptionKeyGenerator.ENCRYPTED_DB_PASSWORD_ERROR_ID) 
    { 
        statusMsg.text = "Incorrect password!"; 
    } 
    else 
    { 
        statusMsg.text = "Error creating or opening database."; 
    }

    예제 이벤트 리스너의 전체 코드는 암호화 키를 생성 및 사용하기 위한 전체 예제 코드를 참조하십시오.

암호화 키를 생성 및 사용하기 위한 전체 예제 코드

다음은 예제 응용 프로그램 “암호화 키 생성 및 사용”의 전체 코드입니다. 이 코드는 두 부분으로 구성되어 있습니다.

이 예제에서는 EncryptionKeyGenerator 클래스를 사용하여 암호를 기반으로 암호화 키를 만듭니다. EncryptionKeyGenerator 클래스는 오픈 소스 ActionScript 3.0 기본 라이브러리(as3corelib) 프로젝트에 포함되어 있습니다. 소스 코드와 설명서를 포함한 as3corelib 패키지를 다운로드할 수 있습니다. 또한 이 프로젝트 페이지에서 SWC 또는 소스 코드 파일도 다운로드할 수 있습니다.

Flex 예제

응용 프로그램 MXML 파일에는 암호화된 데이터베이스를 만들거나 암호화된 데이터베이스에 대한 연결을 열기 위한 간단한 응용 프로그램의 소스 코드가 들어 있습니다.

<?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 예제

응용 프로그램 FLA 파일에는 암호화된 데이터베이스를 만들거나 암호화된 데이터베이스에 대한 연결을 열기 위한 간단한 응용 프로그램의 소스 코드가 들어 있습니다. FLA 파일에는 스테이지에 배치되는 네 가지 구성 요소가 있습니다.

인스턴스 이름

구성 요소 유형

설명

지침

레이블

사용자에게 제공되는 지침이 들어 있습니다.

passwordInput

TextInput

사용자가 암호를 입력하는 입력 필드입니다.

openButton

버튼

사용자가 암호를 입력한 후 클릭하는 버튼입니다.

statusMsg

레이블

상태(성공 또는 실패) 메시지를 표시합니다.

응용 프로그램의 코드가 기본 타임라인에서 프레임 1의 키프레임에 정의됩니다. 다음은 응용 프로그램의 코드입니다.

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 클래스 이해

EncryptionKeyGenerator 클래스를 사용하여 응용 프로그램 데이터베이스의 보안 암호화 키를 만들기 위해 이 클래스가 내부적으로 작동하는 방식을 이해할 필요는 없습니다. EncryptionKeyGenerator 클래스를 사용하는 과정은 EncryptionKeyGenerator 클래스를 사용하여 보안 암호화 키 생성에 설명되어 있습니다. 그러나 EncryptionKeyGenerator 클래스의 내부 작동 방식을 이해하면 도움이 될 수도 있습니다. 예를 들어 다른 수준의 개인 정보 보호가 필요한 경우 EncryptionKeyGenerator 클래스를 그에 맞게 조정하거나 이 클래스의 일부 작동 방식을 통합할 수 있습니다.

EncryptionKeyGenerator 클래스는 오픈 소스 ActionScript 3.0 기본 라이브러리(as3corelib) 프로젝트에 포함되어 있습니다. 소스 코드와 설명서를 포함한 as3corelib 패키지를 다운로드할 수 있습니다. 또한 프로젝트 사이트에서 소스 코드를 보거나 다운로드하여 설명과 함께 따라할 수도 있습니다.

코드에서 EncryptionKeyGenerator 인스턴스를 만들어 getEncryptionKey() 메서드를 호출하면 여러 단계를 거쳐 올바른 사용자만 데이터에 액세스할 수 있게 합니다. 이 프로세스는 데이터베이스를 새로 만들기 위해 사용자가 입력한 암호에서 암호화 키를 생성하고 데이터베이스를 열기 위해 암호화 키를 다시 생성할 때 동일하게 적용됩니다.

강력한 암호 가져오기 및 유효성 검사

코드에서 getEncryptionKey() 메서드를 호출하면 매개 변수로 암호를 전달합니다. 암호는 암호화 키의 기준으로 사용됩니다. 이 디자인에서는 사용자만 아는 정보를 사용하여 암호를 아는 사용자만 데이터베이스의 데이터에 액세스할 수 있게 합니다. 공격자가 컴퓨터에서 사용자의 계정에 액세스하더라도 암호를 모르면 데이터베이스에 침투할 수 없습니다. 보안을 극대화하기 위해 응용 프로그램에서 암호를 저장하지 않습니다.

응용 프로그램의 코드는 EncryptionKeyGenerator 인스턴스를 만들고 해당 getEncryptionKey() 메서드를 호출하여 사용자가 입력한 암호를 인수(이 예제에서는 password 변수)로 전달합니다.

var keyGenerator:EncryptionKeyGenerator = new EncryptionKeyGenerator(); 
var encryptionKey:ByteArray = keyGenerator.getEncryptionKey(password);

getEncryptionKey() 메서드가 호출될 때 EncryptionKeyGenerator 클래스가 수행하는 첫 번째 단계는 사용자가 입력한 암호가 강력한 암호 요구 사항을 충족하는지 검사하는 것입니다. EncryptionKeyGenerator 클래스는 8 - 32자 길이의 문자를 요구합니다. 암호에는 대문자와 소문자가 섞여 있고 하나 이상의 숫자 또는 기호 문자가 포함되어 있어야 합니다.

이 패턴을 확인하는 일반 표현식은 STRONG_PASSWORD_PATTERN이라는 상수로 정의됩니다.

private static const STRONG_PASSWORD_PATTERN:RegExp = /(?=^.{8,32}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/;

암호를 검사하는 코드는 EncryptionKeyGenerator 클래스의 validateStrongPassword() 메서드에 있습니다. 코드는 다음과 같습니다.

public function vaidateStrongPassword(password:String):Boolean 
{ 
    if (password == null || password.length <= 0) 
    { 
        return false; 
    } 
     
    return STRONG_PASSWORD_PATTERN.test(password)) 
}

내부적으로 getEncryptionKey() 메서드는 EncryptionKeyGenerator 클래스의 validateStrongPassword() 메서드를 호출하고 암호가 유효하지 않을 경우 예외를 발생시킵니다. validateStrongPassword() 메서드는 공용 메서드이므로 코드에서 응용 프로그램 코드가 getEncryptionKey() 메서드를 호출하지 않고 암호를 검사해도 오류가 발생하지 않습니다.

암호를 256비트로 확대

이 프로세스의 뒷부분에서는 암호의 길이가 256비트여야 합니다. 각 사용자에게 정확히 256비트(32자)인 암호를 입력하게 하는 대신 이 코드에서는 암호 문자를 반복하여 더 긴 암호를 만듭니다.

getEncryptionKey() 메서드는 concatenatePassword() 메서드를 호출하여 긴 암호를 만드는 작업을 수행합니다.

var concatenatedPassword:String = concatenatePassword(password);

다음은 concatenatePassword() 메서드의 코드입니다.

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; 
}

암호가 256비트 미만이면 코드는 암호를 암호 자체와 연결하여 256비트로 만듭니다. 길이가 정확히 처리되지 않으면 마지막 반복이 짧아져 정확히 256비트에 맞춥니다.

256비트 솔트 값 생성 또는 가져오기

다음 단계는 이후의 단계에서 암호와 결합되는 256비트 솔트 값을 가져오는 것입니다. 솔트는 사용자가 입력한 값에 추가하거나 결합하여 암호를 형성하는 임의의 값입니다. 솔트와 암호를 함께 사용하면 사용자가 실제 단어나 일반 용어를 암호로 선택하더라도 시스템에서 사용하는 암호와 솔트 조합은 임의의 값이 됩니다. 이렇게 하면 공격자가 단어 목록을 사용하여 암호를 추측하려고 하는 사전 공격(Dictionary Attack)을 방지할 수 있습니다. 또한 솔트 값을 생성하여 암호화된 로컬 저장소에 저장하면 이 값이 데이터베이스 파일이 있는 시스템의 사용자 계정에 연결됩니다.

응용 프로그램이 getEncryptionKey() 메서드를 처음으로 호출하면 코드에서 임의의 256비트 솔트 값을 생성합니다. 그렇지 않은 경우에는 코드에서 암호화된 로컬 저장소의 솔트 값을 로드합니다.

솔트는 salt라는 변수에 저장됩니다. 코드는 암호화된 로컬 저장소에서 솔트를 로드하려고 시도하여 이미 솔트를 생성했는지 확인합니다.

var salt:ByteArray = EncryptedLocalStore.getItem(saltKey); 
if (salt == null) 
{ 
    salt = makeSalt(); 
    EncryptedLocalStore.setItem(saltKey, salt); 
}

코드에서 새 솔트 값을 만들 경우 makeSalt() 메서드가 임의의 256비트 값을 생성합니다. 이 값은 결과적으로 암호화된 로컬 저장소에 저장되므로 ByteArray 객체로 생성됩니다. makeSalt() 메서드는 Math.random() 메서드를 사용하여 값을 임의로 생성합니다. Math.random() 메서드는 256비트를 한 번에 생성할 수 없습니다. 대신 루프를 사용하여 Math.random()을 여덟 번 호출합니다. 호출할 때마다 0에서 4294967295(최대 단위 값) 사이의 임의의 단위 값이 생성됩니다. 한 단위에서 정확히 32비트를 사용하므로 단위 값은 편의를 위해 사용됩니다. 여덟 개의 단위 값을 ByteArray에 쓰면 256비트 값이 생성됩디다. 다음은 makeSalt() 메서드의 코드입니다.

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; 
}

코드에서 솔트를 암호화된 로컬 저장소(ELS)에 저장하거나 ELS에서 솔트를 검색하려면 솔트가 저장된 String 키가 필요합니다. 이 키를 모르면 솔트 값을 검색할 수 없습니다. 즉, 이 키를 모를 경우 데이터베이스를 다시 열 때마다 암호화 키를 다시 생성할 수 없습니다. 기본적으로 EncryptionKeyGenerator는 상수 SALT_ELS_KEY에 사전 정의된 ELS 키를 사용합니다. 기본 키를 사용하는 대신 응용 프로그램 코드에서 getEncryptionKey() 메서드 호출에 사용할 ELS 키를 지정할 수도 있습니다. 기본 솔트 ELS 키 또는 응용 프로그램에서 지정한 솔트 ELS 키는 saltKey라는 변수로 저장됩니다. 앞에 나온 것처럼 해당 변수가 EncryptedLocalStore.setItem()EncryptedLocalStore.getItem()에 대한 호출에 사용됩니다.

XOR 연산자를 사용하여 256비트 암호와 솔트 결합

이제 코드에는 256비트 암호와 256비트 솔트 값이 있습니다. 그 다음에 코드에서는 비트 XOR 작업을 사용하여 연결된 암호와 솔트를 하나의 값으로 결합합니다. 그런 다음 사용 가능한 전체 문자 범위의 문자로 구성된 256비트 암호를 만듭니다. 이 원칙이 적용되기는 하지만 실제 암호 입력은 주로 영숫자로 구성됩니다. 이렇게 높은 임의성으로 사용 가능한 암호 집합이 커지기 때문에 사용자가 길고 복잡한 암호를 입력하지 않아도 됩니다.

XOR 작업의 결과는 unhashedKey 변수에 저장됩니다. 두 값에 대한 비트 XOR을 수행하는 실제 프로세스는 xorBytes() 메서드에서 일어납니다:

var unhashedKey:ByteArray = xorBytes(concatenatedPassword, salt);

비트 XOR 연산자(^)는 두 단위 값을 가져와서 하나의 단위 값을 반환합니다. 하나의 단위 값은 32비트로 구성됩니다. xorBytes() 메서드에 인수로 전달되는 입력 값은 String(암호) 및 ByteArray(솔트)입니다. 따라서 코드에서는 루프를 사용하여 각 입력에서 한 번에 32비트를 추출하고 XOR 연산자를 사용하여 결합합니다.

private function xorBytes(passwordString:String, salt:ByteArray):ByteArray 
{ 
    var result:ByteArray = new ByteArray(); 
     
    for (var i:uint = 0; i < 32; i += 4) 
    { 
        // ... 
    } 
     
    return result; 
}

루프 내에서 먼저 32비트(4바이트)가 passwordString 매개 변수에서 추출됩니다. 이러한 비트는 추출된 후 두 부분으로 이루어진 프로세스에서 단위(o1)로 변환됩니다. 먼저 charCodeAt() 메서드는 각 문자의 숫자 값을 가져옵니다. 그런 다음 비트 왼쪽 시프트 연산자(<<)를 사용하여 해당 값을 단위의 적절한 위치로 이동하고 이동된 값을 o1에 추가합니다. 예를 들어 첫 번째 문자(i)는 비트 왼쪽 시프트 연산자(<<)를 사용하여 24비트 왼쪽으로 이동하고 o1에 할당하여 첫 번째 8비트가 됩니다. 두 번째 문자(i + 1)는 왼쪽으로 16비트 이동하고 결과를 o1에 추가하여 두 번째 8비트가 됩니다. 세 번째 및 네 번째 문자 값도 같은 방식으로 추가됩니다.

        // ... 
         
        // 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); 
         
        // ...

이제 o1 변수에는 passwordString 매개 변수의 32비트가 들어 있습니다. 다음으로 readUnsignedInt() 메서드를 호출하여 salt 매개 변수에서 32비트를 추출합니다. 32비트는 uint 변수 o2에 저장됩니다.

        // ... 
         
        salt.position = i; 
        var o2:uint = salt.readUnsignedInt(); 
         
        // ...

마지막으로 XOR 연산자를 사용하여 이 두 32비트(uint) 값을 결합하고 그 결과를 result라는 ByteArray에 씁니다.

        // ... 
         
        var xor:uint = o1 ^ o2; 
        result.writeUnsignedInt(xor); 
        // ...

루프가 완료되면 XOR 결과를 포함하는 ByteArray가 반환됩니다.

        // ... 
    } 
     
    return result; 
}

키 해시

연결된 암호와 솔트가 결합되면 다음 단계로 이 값을 SHA-256 해시 알고리즘으로 해시하여 보안을 강화합니다. 값을 해시하면 공격자가 값을 리버스 엔지니어링하기 더 어렵게 만듭니다.

이제 코드에는 솔트가 결합된 연결된 암호를 포함하는 unhashedKey라는 ByteArray가 있습니다. ActionScript 3.0 기본 라이브러리(as3corelib) 프로젝트에는 com.adobe.crypto 패키지에 SHA256 클래스가 포함되어 있습니다. 이 클래스에는 ByteArray에 대한 SHA-256 해시를 수행하고 256비트 해시 결과를 포함하는 String을 16진수로 반환하는 SHA256.hashBytes() 메서드가 있습니다. EncryptionKeyGenerator 클래스는 SHA256 클래스를 사용하여 키를 해시합니다.

var hashedKey:String = SHA256.hashBytes(unhashedKey);

해시에서 암호화 키 추출

암호화 키는 길이가 정확히 16바이트(128비트)인 ByteArray여야 합니다. SHA-256 해시 알고리즘의 결과는 항상 길이가 256비트입니다. 따라서 마지막 단계에서는 해시된 결과에서 128비트를 선택하여 실제 암호화 키로 사용합니다.

EncryptionKeyGenerator 클래스에서는 generateEncryptionKey() 메서드를 호출하여 키를 128비트로 줄입니다. 그런 다음 이 메서드 결과를 getEncryptionKey() 메서드의 결과로 반환합니다.

var encryptionKey:ByteArray = generateEncryptionKey(hashedKey); 
return encryptionKey;

첫 번째 128비트를 암호화 키로 사용할 필요는 없습니다. 임의이 지점에서 시작하는 비트 범위를 선택할 수도 있고 하나씩 걸러서 비트를 선택할 수도 있고 그 밖에 다양한 방법으로 비트를 선택할 수 있습니다. 중요한 점은 코드에서 128개의 고유한 비트를 선택하고 이 동일한 128비트가 매번 사용된다는 것입니다.

generateEncryptionKey() 메서드에서는 18번째 바이트에서 시작되는 비트 범위를 암호화 키로 사용합니다. 앞에서 설명한 대로 SHA256 클래스는 256비트 해시가 포함된 String을 16진수로 반환합니다. 128비트의 단일 블록은 너무 많은 바이트여서 ByteArray에 한 번에 추가할 수 없습니다. 따라서 코드에서는 for 루프를 사용하여 16진수 String에서 문자를 추출하고 이를 실제 숫자 값으로 변환한 다음 ByteArray에 추가합니다. SHA-256 결과 String은 길이가 64자입니다. 128비트 범위는 String의 32자와 동일하므로 각 문자는 4비트를 나타냅니다. ByteArray에 추가할 수 있는 가장 작은 데이터 단위는 1바이트(8비트)이며, 이는 hash String에 있는 두 문자에 해당합니다. 따라서 루프는 0에서 31(32자)까지 2자씩 증가하여 계산합니다.

루프 내에서 코드는 먼저 현재 문자 쌍의 시작 위치를 결정합니다. 원하는 범위가 인덱스 위치 17(18번째 바이트)의 문자에서 시작되므로 position 변수는 현재 반복기 값(i)에 17을 더한 값으로 할당됩니다. 코드에서는 String 객체의 substr() 메서드를 사용하여 현재 위치에서 두 문자를 추출합니다. 이러한 문자는 hex 변수에 저장됩니다. 그런 다음 코드에서는 parseInt() 메서드를 사용하여 hex String을 10진수 정수 값으로 변환합니다. 그리고 이 값을 int 변수 byte에 저장합니다. 마지막으로 코드에서는 byte의 값을 writeByte() 메서드를 사용하여 result ByteArray에 추가합니다. 루프가 완료되면 result ByteArray는 16바이트를 포함하게 되며 데이터베이스 암호화 키로 사용할 수 있습니다.

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; 
}