Seize the day

POST : Android Dev Study

Android AES encryption + Cpp AES decryption

Android에서 AES 암호화하기

    private fun encrypt(jwt: String, name: String): String {
        val data = JSONObject()
            .put("jwt", jwt)
            .put("name", name)
            .toString()

        try {
            val key = EncryptUtils.generateSHA256("super-secret-key")
            val iv = ByteArray(16) { 0 }
            val skeySpec = SecretKeySpec(key, "AES")
            val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, IvParameterSpec(iv))

            val clearText = data.toByteArray()
            val data = ByteArray(((clearText.size / 16) + 1) * 16)
            System.arraycopy(clearText, 0, data, 0, clearText.size)
            val encrypted = cipher.doFinal(data)
            return Base64.encodeToString(encrypted, Base64.NO_WRAP)
        } catch (ex: Exception) {
            DUtils.notReached(ex)
            throw ex;
        }
    }
    
    @WorkerThread
    @Throws(Exception::class)
    fun qrcodeAccept(code: String) {
        val data = encrypt(getJwtForDesktop(), getDbName())
        val parameter = JSONObject()
                .put("encrypted", true)
                .put("data", data)

        request("/v1/qrcode-accept/$code", parameter, true)
    }

 

Cpp 클라이언트에서 복호화하기

간단히 AES를 풀수있는 라이브러리 SergeyBel/AES: C++ AES implementation (github.com) 를 사용.
간단히 json을 다룰수 있는 라이브러리 nlohmann/json: JSON for Modern C++ (github.com) 사용

int CDlgQrcode::handleTokenData(std::string& data) {

	dfx::ByteArray encrypted;
	if (!dfx::Base64_DecodeA(data.c_str(), encrypted))
		return 1;

	const char* key = "super-secret-key";
	BYTE aesKey[32];
	dsMakeSHA256((BYTE*)key, strlen(key), aesKey);

	BYTE ivKey[16] = { 0 };
	ZeroMemory(ivKey, 16);

	AES aes(256);
	unsigned char* result = aes.DecryptCBC(encrypted.GetBuffer(), encrypted.GetSize(), aesKey, ivKey);

	std::string jsonString = (char*)result;
	delete[] result;
    
        try {
		json jsonObject = json::parse(jsonString.c_str());
		std::string jwt = jsonObject.at("jwt").get<std::string>();
		std::string name = jsonObject.at("name").get<std::string>();
                CString dbName = dfx::Utf8toUnicode(name.data());
		CDialog::OnOK();
	}
	catch (const std::exception& e) {
		::AfxMessageBox(L"json parsing failed:\n" + dfx::Utf8toUnicode(jsonString.c_str()));
	}    
        return 0;
}

 

 

보안 강화를 위해서 JWT 토큰 만들기

h5p9sl/hmac_sha256: Minimal HMAC-SHA256 implementation in C / C++ (github.com) 를 코드를 이용한다. 간단히 소스파일 2개만 추가해서 구현..

std::string base64url_encode(BYTE* data, int size) {
	CAtlStringA base64 = dfx::Base64_EncodeA(data, size);
	base64.Replace("=", "");
	base64.Replace("+", "-");
	base64.Replace("/", "_");
	return base64;
}

std::string getJwtAuth(std::string code) {

	const std::string header = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}";

	CTime ct = CTime::GetCurrentTime();
	__time64_t currentTimeSeconds = ct.GetTime();
	__time64_t exp = currentTimeSeconds + 60; // 60 seconds 유효
	std::string payload = string_format("{\"exp\":%ld, \"code\":\"", exp);
	payload += code;
	payload += "\"}";
	

	const std::string header_encoded = base64url_encode((BYTE*)header.data(), header.size());
	const std::string payload_encoded = base64url_encode((BYTE*)payload.data(), payload.size());
	const std::string data = header_encoded + "." + payload_encoded;

	const std::string key = "super-secret-key";

	BYTE hmac[32];
	hmac_sha256(key.data(), key.size(), data.data(), data.size(), hmac, 32);


	const std::string signiture = base64url_encode(hmac, 32);
	return header_encoded + "." + payload_encoded + "." + signiture;
}


deno/ oak에서 jwt-auth 헤더 확인

exp로 유효시간 확인, jwt의 payload안의 code와 일치하는지 확인

/**
 * verifyJwtAuth
 */
 import { hmac as createHmac} from "https://deno.land/x/crypto@v0.10.0/hmac.ts"
 import {encode as base64urlEncode, decode as base64urlDecode} from "https://deno.land/std@0.103.0/encoding/base64url.ts"
 
const PRIVATE_SECRET = new TextEncoder().encode("super-secret-key")

export function verifyJwtAuth(token: string, code: string) {
    if (token == null) {
        throwJwtError("empty token")
    }
    const [header, payload, signiture] = token.split('.')
    const value = new TextEncoder().encode(header + "." + payload)
    const hmacValue = createHmac('sha256', PRIVATE_SECRET, value)
    const signiture2 = base64urlEncode(hmacValue)
    if (signiture !== signiture2) {
        throwJwtError("invalid signiture")
    }
    const payloadJson: string = new TextDecoder().decode(base64urlDecode(payload))
    const payloadObj = JSON.parse(payloadJson)
    
    if (code !== payloadObj.code) {
        throwJwtError("invalid code")
    }

    const exp = payloadObj.exp || null
    if (typeof exp != "number") {
        throwJwtError("invalid exp")
    }
                
    if (exp <= Math.floor(Date.now()/1000)) {
        throw new ErrorCode("jwt auth error: expired token", kErrorJwtExpire)
    }
}
top

posted at

2021. 8. 29. 00:59


CONTENTS

Seize the day
BLOG main image
김대정의 앱 개발 노트와 사는 이야기
RSS 2.0Tattertools
공지
아카이브
최근 글 최근 댓글
카테고리 태그 구름사이트 링크