Proses Pembangkitan kunci

Alisa dan Bobi masing-masing membangkitkan pasangan kunci privat dan kunci publik X25519, dengan cara membuka tautan Android Instant App di dalam ponsel pintar Android milik mereka, https://slamessaging.000webhostapp.com/pembangkitan-ecdh. Kunci privat dan kunci publik tersebut dibangkitkan dengan menggunakan prosedur yang pengembang adaptasi dari kode sumber berkas X25519Test.java dan BaseJavaCurve25519Provider.java berikut:

Pembangkitan kunci privat

import java.security.SecureRandom;
import org.spongycastle.util.encoders.UrlBase64;

byte[] privateKey = new byte[32];
new SecureRandom().nextBytes(privateKey);
privateKey[0] &= 248;
privateKey[31] &= 127;
privateKey[31] |= 64;
String privString = new BigInteger(privateKey).toString(36);

Pembangkitan kunci publik

import org.spongycastle.util.encoders.UrlBase64;

byte[] publicKey = new byte[32];
X25519.scalarMultBase(privateKey, 0, publicKey, 0);
String pubString = new BigInteger(publicKey).toString(36);        

Lalu, Bobi ingin mengirimkan pesan rahasia kepada Alisa, maka Alisa harus memberikan kunci publik miliknya kepada Bobi dengan cara menekan tombol Bagikan Kunci Publik, setelah itu akan muncul pilihan aplikasi perpesanan yang bisa dipilih Alisa seperti surel, pesan instan, atau sms untuk memberikan kunci publiknya kepada Bobi.

Proses Pembuatan Pesan

Bobi menerima kunci publik milik Alisa berupa tautan Android Instant App yang memiliki pola seperti milik pengembang sendiri https://slamessaging.000webhostapp.com/pembuatan-ecdh?kunciPublikTeman=2fh92juhve84g4biidolwrug551dvljrujin0jygiz544vdydr.

Setelah itu, aplikasi Android Instant App Slamessaging akan menampilkan isian untuk di isi pesan oleh Bobi. Setelah selesai membuat pesan, Bobi menekan tombol Bagikan Pesan, lalu akan terjadi proses tanda tangan digital dan enkripsi pesan sebagai berikut:

import java.math.BigInteger;
import java.security.SecureRandom;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.json.JSONObject;
import org.spongycastle.crypto.CipherKeyGenerator;
import org.spongycastle.crypto.CipherParameters;
import org.spongycastle.crypto.KeyGenerationParameters;
import org.spongycastle.crypto.digests.SHA384Digest;
import org.spongycastle.crypto.engines.AESEngine;
import org.spongycastle.crypto.engines.AESWrapEngine;
import org.spongycastle.crypto.generators.HKDFBytesGenerator;
import org.spongycastle.crypto.modes.CBCBlockCipher;
import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.spongycastle.crypto.params.HKDFParameters;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.crypto.params.ParametersWithIV;
import org.spongycastle.crypto.params.ParametersWithRandom;
import org.spongycastle.math.ec.rfc7748.X25519;
import org.spongycastle.util.encoders.Hex;
import org.spongycastle.util.encoders.UrlBase64;
import org.whispersystems.curve25519.java.curve_sigs;

/**
 *
 * @author dawud_tan
 */
public class PembuatanPesan {

    public static void main(String[] args) {
        String kunciPublikTeman = "lydd4t7ia2xmb29elnf926acvst5d0t9scukk3sthcz3t9r84";
        String kunciPribadiSaya = "10gs3alm530rbwd6oeb7rovzs8928k4wguqec6w42hk7x16ezk";

        byte[] recipientPublicKey = new BigInteger(kunciPublikTeman, 36).toByteArray();

        SecureRandom secureRandom = new SecureRandom();

        CipherKeyGenerator engine = new CipherKeyGenerator();
        engine.init(new KeyGenerationParameters(secureRandom, 256));
        SecretKey contentEncryptionKey = new SecretKeySpec(engine.generateKey(), "AES");
        byte[] encodedContentEncryptionKey = contentEncryptionKey.getEncoded();
        byte[] subadya = new byte[16];
        secureRandom.nextBytes(subadya);

        IvParameterSpec paramSpec = new IvParameterSpec(subadya);
        ParametersWithIV contentEncKeyParam
                = new ParametersWithIV(new KeyParameter(contentEncryptionKey.getEncoded()), paramSpec.getIV());

        byte[] privateKey = new BigInteger(kunciPribadiSaya, 36).toByteArray();
        String pesannya = "Halo Dunia";
        byte[] result = new byte[64];
        byte[] acak = new byte[64];
        secureRandom.nextBytes(acak);
        byte[] msg = pesannya.getBytes();
        int msg_len = msg.length;
        curve_sigs.curve25519_sign(result, privateKey, msg, msg_len, acak);

        byte[] plainTextBytes = new JSONObject()
                .put("eContent", pesannya)
                .put("signature", Hex.toHexString(result))
                .toString()
                .getBytes();
        PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
        cipher.init(true, contentEncKeyParam);
        byte[] cipherText = new byte[cipher.getOutputSize(plainTextBytes.length)];
        int len = cipher.processBytes(plainTextBytes, 0, plainTextBytes.length, cipherText, 0);
        cipher.doFinal(cipherText, len);

        byte[] agreedWrapKey = new byte[32];
        X25519.scalarMult(privateKey, 0, recipientPublicKey, 0, agreedWrapKey, 0);

        int keySize = 256;
        byte[] ukmParameters = Hex.decode("3015"
                + "300B" + "0609" + "60864801650304012D"
                + //aes256-wrap
                "A206" + "0404" + "00000100");//key size 256 bits
        HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA384Digest());
        hkdf.init(new HKDFParameters(agreedWrapKey, null, ukmParameters));
        byte[] secret = new byte[keySize / 8];//SizeInOctets
        hkdf.generateBytes(secret, 0, secret.length);
        SecretKey keyEncryptionKey = new SecretKeySpec(secret, "AES");

        CipherParameters keyEncParam = new KeyParameter(keyEncryptionKey.getEncoded());
        keyEncParam = new ParametersWithRandom(keyEncParam, secureRandom);
        keyEncParam = ((ParametersWithRandom) keyEncParam).getParameters();
        AESWrapEngine wrapEngine = new AESWrapEngine();
        wrapEngine.init(true, keyEncParam);
        byte[] recipientEncryptedKey = wrapEngine.wrap(encodedContentEncryptionKey, 0, encodedContentEncryptionKey.length);
        String encryptedKey = new String(UrlBase64.encode(recipientEncryptedKey));
        String Nonce = new String(UrlBase64.encode(subadya));
        String encryptedContent = new String(UrlBase64.encode(cipherText));
        System.out.println("encryptedKey: " + encryptedKey);
        System.out.println("nonce: " + Nonce);
        System.out.println("encryptedContent: " + encryptedContent);
    }
}

Setelah itu pesan rahasia bisa diberikan ke Alisa melalui surel, pesan instant, atau sms. Misalkan bila pembaca ingin mengirimkan pesan rahasia kepada pengembang, pembaca bisa menggunakan kunci publik pengembang di atas (2fh92juhve84g4biidolwrug551dvljrujin0jygiz544vdydr), lalu membagikan pesan rahasia tersebut kepada pengembang ke alamat surel berikut dawud_tan@merahputih.id

Proses Pembacaan Pesan

Alisa menerima tautan Instant App yang berisi pesan rahasia dari Bobi, lalu Alisa tinggal mengklik tautan tersebut, dan Alisa bisa membaca pesan rahasia tersebut bila Bobi benar-benar menggunakan kunci publik milik Alisa. Selain itu, tanda tangan digital yang disertakan di dalam pesan juga ikut diverifikasi menggunakan kunci publik milik Bobi

import java.math.BigInteger;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.math.Curve;
import net.i2p.crypto.eddsa.math.GroupElement;
import org.json.JSONObject;
import org.spongycastle.crypto.CipherParameters;
import org.spongycastle.crypto.digests.SHA384Digest;
import org.spongycastle.crypto.digests.SHA512Digest;
import org.spongycastle.crypto.engines.AESEngine;
import org.spongycastle.crypto.engines.AESWrapEngine;
import org.spongycastle.crypto.generators.HKDFBytesGenerator;
import org.spongycastle.crypto.modes.CBCBlockCipher;
import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.spongycastle.crypto.params.HKDFParameters;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.crypto.params.ParametersWithIV;
import org.spongycastle.crypto.params.ParametersWithRandom;
import org.spongycastle.math.ec.rfc7748.X25519;
import org.spongycastle.util.encoders.Hex;
import org.spongycastle.util.encoders.UrlBase64;
import org.whispersystems.curve25519.java.curve_sigs;
import org.whispersystems.curve25519.java.fe_1;
import org.whispersystems.curve25519.java.fe_add;
import org.whispersystems.curve25519.java.fe_frombytes;
import org.whispersystems.curve25519.java.fe_invert;
import org.whispersystems.curve25519.java.fe_mul;
import org.whispersystems.curve25519.java.fe_sub;
import org.whispersystems.curve25519.java.fe_tobytes;

/**
 *
 * @author dawud_tan
 */
public class PembacaanPesan {

    public static void main(String[] args) throws Exception {

        String kunciPribadiSaya = "eucz9tr6jixw7w7ry8yssnjjf6918irgxrsf6px380uictdmt";
        String kunciPublikTeman = "wbme6l9autfl4f78nluy1i4gfpm88jtjoxz22lvkn08j2kkk4";
        String encryptedKey = "RDmTEWfaXjQWnHu6G8JoDHUtAMM_dd2v-jT_qQMV105GJq5HDsbzLw..";
        String Nonce = "70tbXNUTsPBw0gAa";
        String encryptedContent = "qeCA0DpF-52R8ovJw3Id9px7deSCvHrWLLwe1X2YxRH8lDwxQ5H1f-Z13-wp-yGEB6uUj3T9P-1hDCvhKlPV_xub91kq0OHT03yRzdB52BZh907TXZyAFSyhqMzJhBlVrm84QBDfi7Top5ts422VXWAKZ6JxZwSayZQFTnWWtyr7ZFsbJ7z2TdvgNDNumiZlND3ETs0uHlIiKayxOxN7zDN9u8gYMtgJ";

        byte[] privateKey = new BigInteger(kunciPribadiSaya, 36).toByteArray();
        //https://tools.ietf.org/html/rfc4130#section-8
        //The exchange of public keys
        //MUST be handled as part of the process of establishing a
        //trading partnership.
        byte[] senderPublicKey = new BigInteger(kunciPublikTeman, 36).toByteArray();
        byte[] agreedWrapKey = new byte[32];
        X25519.scalarMult(privateKey, 0, senderPublicKey, 0, agreedWrapKey, 0);

        int keySize = 256;
        byte[] ukmParameters = Hex.decode("3015"
                + "300B" + "0609" + "60864801650304012D"
                + //aes256-wrap
                "A206" + "0404" + "00000100");//key size 256 bits
        HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA384Digest());
        hkdf.init(new HKDFParameters(agreedWrapKey, null, ukmParameters));
        byte[] secret = new byte[keySize / 8];//SizeInOctets
        hkdf.generateBytes(secret, 0, secret.length);

        SecretKey recoveredKeyEncryptionKey = new SecretKeySpec(secret, "AES");

        byte[] recipientEncryptedKey = UrlBase64.decode(encryptedKey);

        CipherParameters param1 = new KeyParameter(recoveredKeyEncryptionKey.getEncoded());
        param1 = new ParametersWithRandom(param1, new SecureRandom());
        param1 = ((ParametersWithRandom) param1).getParameters();
        AESWrapEngine wrapEngine1 = new AESWrapEngine();
        wrapEngine1.init(false, param1);
        byte[] contentEncryptionKey = wrapEngine1.unwrap(recipientEncryptedKey, 0, recipientEncryptedKey.length);
        SecretKey key = new SecretKeySpec(contentEncryptionKey, "AES");
        byte[] subadya = UrlBase64.decode(Nonce);
        IvParameterSpec paramSpec = new IvParameterSpec(subadya);
        ParametersWithIV param
                = new ParametersWithIV(new KeyParameter(key.getEncoded()), paramSpec.getIV());
        PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
        byte[] cipherText = UrlBase64.decode(encryptedContent);
        cipher.init(false, param);
        byte[] tmp = new byte[cipher.getOutputSize(cipherText.length)];
        int len = cipher.processBytes(cipherText, 0, cipherText.length, tmp, 0);
        len += cipher.doFinal(tmp, len);
        byte[] plainText = new byte[len];
        System.arraycopy(tmp, 0, plainText, 0, len);
        JSONObject jsonObject = new JSONObject(new String(plainText));
        String content = jsonObject.getString("eContent");
        byte[] message = content.getBytes();
        String signature = jsonObject.getString("signature");
        byte[] sigBytes = Hex.decode(signature);
        boolean one = curve_sigs.curve25519_verify(sigBytes, senderPublicKey, message, message.length) == 0;
        boolean two = verify(sigBytes, senderPublicKey, message, message.length);
        System.out.println("konten: " + content);
        System.out.println("TTD: " + signature);
        System.out.println("one: " + one);
        System.out.println("two: " + two);
    }

    private static boolean verify(byte[] sigBytes, byte[] senderPublicKey, byte[] message, int messageLength) throws Exception {
        int[] mont_x = new int[10];
        fe_frombytes.fe_frombytes(mont_x, senderPublicKey);
        int[] one = new int[10];
        fe_1.fe_1(one);
        int[] mont_x_minus_one = new int[10];
        fe_sub.fe_sub(mont_x_minus_one, mont_x, one);
        int[] mont_x_plus_one = new int[10];
        fe_add.fe_add(mont_x_plus_one, mont_x, one);
        int[] inv_mont_x_plus_one = new int[10];
        fe_invert.fe_invert(inv_mont_x_plus_one, mont_x_plus_one);
        int[] ed_y = new int[10];
        fe_mul.fe_mul(ed_y, mont_x_minus_one, inv_mont_x_plus_one);
        byte[] ed_pubkey = new byte[32];
        fe_tobytes.fe_tobytes(ed_pubkey, ed_y);
        ed_pubkey[31] &= 0x7F;
        ed_pubkey[31] |= (sigBytes[63] & 0x80);
        EdDSAPublicKey pub = new EdDSAPublicKey(
                new X509EncodedKeySpec(
                        getEncoded(ed_pubkey)));
        Curve curve = pub.getParams().getCurve();
        int b = curve.getField().getb();
        if (sigBytes.length != b / 4) {
            throw new SignatureException("signature length is wrong");
        }
        SHA512Digest digest = new SHA512Digest();
        digest.reset();
// R is first b/8 bytes of sigBytes, S is second b/8 bytes
        digest.update(sigBytes, 0, b / 8);
        byte[] Abyte = pub.getAbyte();
        digest.update(Abyte, 0, Abyte.length);
// h = H(Rbar,Abar,M)
        digest.update(message, 0, messageLength);
        byte[] h = new byte[digest.getDigestSize()];
        digest.doFinal(h, 0);
// h mod l
        h = pub.getParams().getScalarOps().reduce(h);
        byte[] Sbyte = Arrays.copyOfRange(sigBytes, b / 8, b / 4);
// R = SB - H(Rbar,Abar,M)A
        GroupElement R = pub.getParams().getB().doubleScalarMultiplyVariableTime(
                pub.getNegativeA(), h, Sbyte);
// Variable time. This should be okay, because there are no secret
// values used anywhere in verification.
        byte[] Rcalc = R.toByteArray();
        for (int i = 0; i < Rcalc.length; i++) {
            if (Rcalc[i] != sigBytes[i]) {
                return false;
            }
        }
        return true;
    }

    private static byte[] getEncoded(byte[] Abyte) {
        int totlen = 12 + Abyte.length;
        byte[] rv = new byte[totlen];
        int idx = 0;
        // sequence
        rv[idx++] = 0x30;
        rv[idx++] = (byte) (totlen - 2);
        // Algorithm Identifier
        // sequence
        rv[idx++] = 0x30;
        rv[idx++] = 5;
        // OID
        // https://msdn.microsoft.com/en-us/library/windows/desktop/bb540809%28v=vs.85%29.aspx
        rv[idx++] = 0x06;
        rv[idx++] = 3;
        rv[idx++] = 43;
        rv[idx++] = 101;
        rv[idx++] = 112;
        // params - absent
        // the key
        rv[idx++] = 0x03; // bit string
        rv[idx++] = (byte) (1 + Abyte.length);
        rv[idx++] = 0; // number of trailing unused bits
        System.arraycopy(Abyte, 0, rv, idx, Abyte.length);
        return rv;
    }
}

powered by Curve25519

Reactions:

You Might Also Like:

Berikan Komentar Sembunyikan Komentar

Hello, how may we help you? Just send us a message now to get assistance.

Facebook Messenger ×