Proses Pembangkitan kunci

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

Pembangkitan Pasangan Kunci

import java.math.BigInteger;
import java.security.SecureRandom;
import java.security.spec.RSAPrivateCrtKeySpec;

public class PembangkitanKunci {

    static int iterations;

    public static void main(String[] args) {
        int kekuatanKunci = 3072;
        int certainty = kekuatanKunci <= 1024 ? 80 : (96 + 16 * ((kekuatanKunci - 1) / 1024));
        iterations = getNumberOfIterations(kekuatanKunci, certainty);
        RSAPrivateCrtKeySpec kunciku = bangkitkanKunciPribadi(3072);
        System.out.println("PrimaP: " + kunciku.getPrimeP().toString());
        System.out.println("PrimaQ: " + kunciku.getPrimeQ().toString());
        System.out.println("Modulus: " + kunciku.getPrimeP().multiply(kunciku.getPrimeQ()).toString());
    }

    static RSAPrivateCrtKeySpec bangkitkanKunciPribadi(int strength) {
        RSAPrivateCrtKeySpec result = null;
        boolean done = false;
        //
        // p and q values should have a length of half the strength in bits
        //
        int pbitlength = (strength + 1) / 2;
        int qbitlength = strength - pbitlength;
        int mindiffbits = (strength / 2) - 100;
        if (mindiffbits < strength / 3) {
            mindiffbits = strength / 3;
        }
        int minWeight = strength >> 2;

        // d lower bound is 2^(strength / 2)
        BigInteger dLowerBound = BigInteger.valueOf(2).pow(strength / 2);
        // squared bound (sqrt(2)*2^(nlen/2-1))^2
        BigInteger squaredBound = BigInteger.ONE.shiftLeft(strength - 1);
        // 2^(nlen/2 - 100)
        BigInteger minDiff = BigInteger.ONE.shiftLeft(mindiffbits);
        while (!done) {
            BigInteger p, q, n, d, e, pSub1, qSub1, gcd, lcm;
            e = BigInteger.valueOf(0x10001);
            p = chooseRandomPrime(pbitlength, e, squaredBound);
            //
            // generate a modulus of the required length
            //
            for (;;) {
                q = chooseRandomPrime(qbitlength, e, squaredBound);
                // p and q should not be too close together (or equal!)
                BigInteger diff = q.subtract(p).abs();
                if (diff.bitLength() < mindiffbits || diff.compareTo(minDiff) <= 0) {
                    continue;
                }
                //
                // calculate the modulus
                //
                n = p.multiply(q);
                if (n.bitLength() != strength) {
                    //
                    // if we get here our primes aren't big enough, make the largest
                    // of the two p and try again
                    //
                    p = p.max(q);
                    continue;
                }
                /*
                 * Require a minimum weight of the NAF representation, since low-weight composites may
              * be weak against a version of the number-field-sieve for factoring.
              *
              * See "The number field sieve for integers of low weight", Oliver Schirokauer.
                 */
                if (getNafWeight(n) < minWeight) {
                    p = chooseRandomPrime(pbitlength, e, squaredBound);
                    continue;
                }
                break;
            }
            if (p.compareTo(q) < 0) {
                gcd = p;
                p = q;
                q = gcd;
            }
            pSub1 = p.subtract(BigInteger.ONE);
            qSub1 = q.subtract(BigInteger.ONE);
            gcd = pSub1.gcd(qSub1);
            lcm = pSub1.divide(gcd).multiply(qSub1);
            //
            // calculate the static exponent
            //
            d = e.modInverse(lcm);
            if (d.compareTo(dLowerBound) <= 0) {
                continue;
            } else {
                done = true;
            }
            //
            // calculate the CRT factors
            //
            BigInteger dP, dQ, qInv;

            dP = d.remainder(pSub1);
            dQ = d.remainder(qSub1);
            qInv = q.modInverse(p);

            result = new RSAPrivateCrtKeySpec(n, e, d, p, q, dP, dQ, qInv);
        }
        return result;
    }

    static int getNafWeight(BigInteger k) {
        if (k.signum() == 0) {
            return 0;
        }

        BigInteger _3k = k.shiftLeft(1).add(k);
        BigInteger diff = _3k.xor(k);

        return diff.bitCount();
    }

    /**
     * Choose a random prime value for use with RSA
     *
     * @param bitlength the bit-length of the returned prime
     * @param e the RSA public exponent
     * @return A prime p, with (p-1) relatively prime to e
     */
    static BigInteger chooseRandomPrime(int bitlength, BigInteger e, BigInteger sqrdBound) {
        for (int i = 0; i != 5 * bitlength; i++) {
            BigInteger p = new BigInteger(bitlength, 1, new SecureRandom());

            if (p.mod(e).equals(BigInteger.ONE)) {
                continue;
            }

            if (p.multiply(p).compareTo(sqrdBound) < 0) {
                continue;
            }

            if (!isProbablePrime(p)) {
                continue;
            }

            if (!e.gcd(p.subtract(BigInteger.ONE)).equals(BigInteger.ONE)) {
                continue;
            }

            return p;
        }

        throw new IllegalStateException("unable to generate prime number for RSA key");
    }

    static boolean isProbablePrime(BigInteger x) {
        /*
         * Primes class for FIPS 186-4 C.3 primality checking
         */
        return !hasAnySmallFactors(x) && isMRProbablePrime(x, new SecureRandom(), iterations);
    }

    /**
     * A fast check for small divisors, up to some implementation-specific
     * limit.
     *
     * @param candidate the {@link BigInteger} instance to test for division by
     * small factors.
     * @return <code>true</code> if the candidate is found to have any small
     * factors, <code>false</code> otherwise.
     */
    static boolean hasAnySmallFactors(BigInteger candidate) {
        checkCandidate(candidate, "candidate");

        return implHasAnySmallFactors(candidate);
    }

    static void checkCandidate(BigInteger n, String name) {
        if (n == null || n.signum() < 1 || n.bitLength() < 2) {
            throw new IllegalArgumentException("'" + name + "' must be non-null and >= 2");
        }
    }

    static boolean implHasAnySmallFactors(BigInteger x) {
        /*
         * Bundle trial divisors into ~32-bit moduli then use fast tests on the ~32-bit remainders.
         */
        int m = 2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23;
        int r = x.mod(BigInteger.valueOf(m)).intValue();
        if ((r % 2) == 0 || (r % 3) == 0 || (r % 5) == 0 || (r % 7) == 0 || (r % 11) == 0 || (r % 13) == 0
                || (r % 17) == 0 || (r % 19) == 0 || (r % 23) == 0) {
            return true;
        }

        m = 29 * 31 * 37 * 41 * 43;
        r = x.mod(BigInteger.valueOf(m)).intValue();
        if ((r % 29) == 0 || (r % 31) == 0 || (r % 37) == 0 || (r % 41) == 0 || (r % 43) == 0) {
            return true;
        }

        m = 47 * 53 * 59 * 61 * 67;
        r = x.mod(BigInteger.valueOf(m)).intValue();
        if ((r % 47) == 0 || (r % 53) == 0 || (r % 59) == 0 || (r % 61) == 0 || (r % 67) == 0) {
            return true;
        }

        m = 71 * 73 * 79 * 83;
        r = x.mod(BigInteger.valueOf(m)).intValue();
        if ((r % 71) == 0 || (r % 73) == 0 || (r % 79) == 0 || (r % 83) == 0) {
            return true;
        }

        m = 89 * 97 * 101 * 103;
        r = x.mod(BigInteger.valueOf(m)).intValue();
        if ((r % 89) == 0 || (r % 97) == 0 || (r % 101) == 0 || (r % 103) == 0) {
            return true;
        }

        m = 107 * 109 * 113 * 127;
        r = x.mod(BigInteger.valueOf(m)).intValue();
        if ((r % 107) == 0 || (r % 109) == 0 || (r % 113) == 0 || (r % 127) == 0) {
            return true;
        }

        m = 131 * 137 * 139 * 149;
        r = x.mod(BigInteger.valueOf(m)).intValue();
        if ((r % 131) == 0 || (r % 137) == 0 || (r % 139) == 0 || (r % 149) == 0) {
            return true;
        }

        m = 151 * 157 * 163 * 167;
        r = x.mod(BigInteger.valueOf(m)).intValue();
        if ((r % 151) == 0 || (r % 157) == 0 || (r % 163) == 0 || (r % 167) == 0) {
            return true;
        }

        m = 173 * 179 * 181 * 191;
        r = x.mod(BigInteger.valueOf(m)).intValue();
        if ((r % 173) == 0 || (r % 179) == 0 || (r % 181) == 0 || (r % 191) == 0) {
            return true;
        }

        m = 193 * 197 * 199 * 211;
        r = x.mod(BigInteger.valueOf(m)).intValue();
        if ((r % 193) == 0 || (r % 197) == 0 || (r % 199) == 0 || (r % 211) == 0) {
            return true;
        }

        /*
         * NOTE: Unit tests depend on SMALL_FACTOR_LIMIT matching the
         * highest small factor tested here.
         */
        return false;
    }

    static BigInteger TWO = BigInteger.valueOf(2);

    /**
     * FIPS 186-4 C.3.1 Miller-Rabin Probabilistic Primality Test
     * <p>
     * Run several iterations of the Miller-Rabin algorithm with randomly-chosen
     * bases.
     *
     * @param candidate the {@link BigInteger} instance to test for primality.
     * @param random the source of randomness to use to choose bases.
     * @param iterations the number of randomly-chosen bases to perform the test
     * for.
     * @return <code>false</code> if any witness to compositeness is found
     * amongst the chosen bases (so <code>candidate</code> is definitely NOT
     * prime), or else <code>true</code> (indicating primality with some
     * probability dependent on the number of iterations that were performed).
     */
    static boolean isMRProbablePrime(BigInteger candidate, SecureRandom random, int iterations) {
        checkCandidate(candidate, "candidate");

        if (random == null) {
            throw new IllegalArgumentException("'random' cannot be null");
        }
        if (iterations < 1) {
            throw new IllegalArgumentException("'iterations' must be > 0");
        }
        if (candidate.bitLength() == 2) {
            return true;
        }
        if (!candidate.testBit(0)) {
            return false;
        }

        BigInteger w = candidate;
        BigInteger wSubOne = candidate.subtract(BigInteger.ONE);

        BigInteger wSubTwo = candidate.subtract(TWO);

        int a = wSubOne.getLowestSetBit();
        BigInteger m = wSubOne.shiftRight(a);
        for (int i = 0; i < iterations; ++i) {
            BigInteger b = createRandomInRange(TWO, wSubTwo, random);

            if (!implMRProbablePrimeToBase(w, wSubOne, m, a, b)) {
                return false;
            }
        }

        return true;
    }

    static boolean implMRProbablePrimeToBase(BigInteger w, BigInteger wSubOne, BigInteger m, int a, BigInteger b) {
        BigInteger z = b.modPow(m, w);

        if (z.equals(BigInteger.ONE) || z.equals(wSubOne)) {
            return true;
        }

        boolean result = false;

        for (int j = 1; j < a; ++j) {
            z = z.modPow(TWO, w);

            if (z.equals(wSubOne)) {
                result = true;
                break;
            }

            if (z.equals(BigInteger.ONE)) {
                return false;
            }
        }

        return result;
    }

    static BigInteger createRandomInRange(
            BigInteger min,
            BigInteger max,
            SecureRandom random) {
        int cmp = min.compareTo(max);
        if (cmp >= 0) {
            if (cmp > 0) {
                throw new IllegalArgumentException("'min' may not be greater than 'max'");
            }

            return min;
        }

        if (min.bitLength() > max.bitLength() / 2) {
            return createRandomInRange(BigInteger.ZERO, max.subtract(min), random).add(min);
        }

        for (int i = 0; i < 1000; ++i) {
            BigInteger x = new BigInteger(max.bitLength(), random);
            if (x.compareTo(min) >= 0 && x.compareTo(max) <= 0) {
                return x;
            }
        }

        // fall back to a faster (restricted) method
        return new BigInteger(max.subtract(min).bitLength() - 1, random).add(min);
    }

    static int getNumberOfIterations(int bits, int certainty) {
        /*
         * NOTE: We enforce a minimum 'certainty' of 100 for bits >= 1024 (else 80). Where the
         * certainty is higher than the FIPS 186-4 tables (C.2/C.3) cater to, extra iterations
         * are added at the "worst case rate" for the excess.
         */
        if (bits >= 1536) {
            return certainty <= 100 ? 3
                    : certainty <= 128 ? 4
                            : 4 + (certainty - 128 + 1) / 2;
        } else if (bits >= 1024) {
            return certainty <= 100 ? 4
                    : certainty <= 112 ? 5
                            : 5 + (certainty - 112 + 1) / 2;
        } else if (bits >= 512) {
            return certainty <= 80 ? 5
                    : certainty <= 100 ? 7
                            : 7 + (certainty - 100 + 1) / 2;
        } else {
            return certainty <= 80 ? 40
                    : 40 + (certainty - 80 + 1) / 2;
        }
    }
}

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://pesanrahasia.000webhostapp.com/pembuatan-rsa?kunciPublikTeman=1nbja0qk52ajqk3f7flgyb6ux0y8x80ms73jy7l69d47wy6ii7qiru46ec9f1ct54hvi9g16gz17noj1giurowa9l1nlxwr3t8d97vrsg0v1ms1dzjbr3dlakmc7k8fculthlhaoafi3agn8b9wbjul3b8qodkv1r881rizogqnslz6kykmor429iwwpasgp2w8zvlgml7j6s7mgnfkjsbennd81gfn6qu113s6rhdo1o42ipmbhq7ccvidnef9ku73kmsvxfrswz97zbm8yxypkviwczd8lppkawwltzscp9dx086ks0huih5vux4pt3lv2p5fcf2mx9pattn4sjeo2q9a7a26ol6pwuz7xe4rytksds5q1hcvomnbbf1qhl0jw7cj4fsdhkf120gyaot8ikk4gm2pi0gdxwgu4l9ilk1zc0o0hl9m2nncn995m4i69uldql4symwj79le1dpka3l8sz14m3u9yyogwmth1oezkunqhybox79mrrl7qw5nmqg6w3yj56s5pri7n9izxprsragg6p1avy61bi64lrv9zk5xlaqqn5ydk4zops1788ye51kzvmljjaw1.

Setelah itu, aplikasi Android Instant App RahaSiA 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.SHA512tDigest;
import org.spongycastle.crypto.encodings.OAEPEncoding;
import org.spongycastle.crypto.engines.AESEngine;
import org.spongycastle.crypto.engines.RSABlindedEngine;
import org.spongycastle.crypto.modes.CBCBlockCipher;
import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.crypto.params.ParametersWithIV;
import org.spongycastle.crypto.params.ParametersWithRandom;
import org.spongycastle.crypto.params.RSAKeyParameters;
import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.spongycastle.crypto.signers.PSSSigner;
import org.spongycastle.util.encoders.Hex;
import org.spongycastle.util.encoders.UrlBase64;

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

    public static void main(String[] args) throws Exception {
        BigInteger modulus = new BigInteger("1gylm639mht0kiroaxwm4h7suet8c8msv2jzibcbzww6gpjaps3rh4pmzhcnvn0lbr5ulf5ffttvul3ner3p7fem31jzyeciu7sua8t2hizvtf67k0rb3iiy3nj4o8e4rztsa8xcagtenxch4e5bfz2nsc31ldjqwj1tvy17u3bgmcnakg9km6wkxh7rt4proj9zt4cdt7494j2pg0obvz1paob55lqn39lyuyxm87yp92x47w46yqu1x7sy6235vclw9f26p64e62409ocea5laey557g856tqeillio7a9jh4q6xge6qncu4y26tdsv6zklxg620rvebxjaty3fat3f4lkqlzq27x10mm0iyibe0yzvlxdijjf4ao823hgvcpziswkowltopb57uz0hgf4u00jqo834ta25qqde5mq0gvea8kg17qezdxjlr605qozzpr5nfd7fwcbrn1ktsw8pb9gpsnfjv6m22a9h2galokl2xkid3ytcorietv874js364owrjb2ihvf0ujfsvbh6j42jmgwoa7misqjpevjo84lcrwk0qk6fvh04lbhy3dytsli0ga4b31yg5", 36);
        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[12];
        secureRandom.nextBytes(subadya);

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

        String pesannya = "Halo Dunia";
        SHA512tDigest digest = new SHA512tDigest(256);
        byte[] msg = pesannya.getBytes();
        int msg_len = msg.length;
        RSABlindedEngine rsaBlindedEngine = new RSABlindedEngine();
        PSSSigner pss = new PSSSigner(rsaBlindedEngine, digest, digest, digest.getDigestSize(), PSSSigner.TRAILER_IMPLICIT);

        String myPrimeP = "1dxjr9qg9kxlb7cawaf7nsax29z9ebxzqs3tjx8xgbzs1d1qil5opey617fbjybvrhoahxw6262z0y4e6vrm71bfdt0tvdpyco8jsex8wraz0twa7t6iks7zow9k2k2hmmen1srx76db67dbqtq3eerkjvou2awpdqa4g1r7h2gtyi5aspt2uaghvnjdzamc72xbm9wkt2uks4jb2oyuact4sqdaobqjcd1i8eeonbq5b73kzb7q29xi6q9xf2wcb326r0yb36zdycubkrhkbi05v5fq89frdebcefvo21";
        String myPrimeQ = "11ruw1j65hqtxx2lkvb4x45mbbcjfos78lwu6mlgry57zbhxcqbywegf865s49i53l0rathn4q552bey2nquh2uoa5sw38m23li6c4gd1y3twpjv7rx7m5or94lw8mo8feotq61fvw7c4wqckha0e5nmv7baukvhfmgah316okdijk61b0oqi1sf5t9yy1exqhoub6jv89ib0esenfnmx9fvro899kw7a0xisufbjrqzvqqoisk6zcnvjmychxftgc8sngg13letf8eftqkzabal2cpglayx7xsyfyjc1j";

        BigInteger primaP = new BigInteger(myPrimeP, 36);
        BigInteger primaQ = new BigInteger(myPrimeQ, 36);
        BigInteger e = BigInteger.valueOf(0x10001);
        BigInteger pSub1 = primaP.subtract(BigInteger.ONE);
        BigInteger qSub1 = primaQ.subtract(BigInteger.ONE);
        BigInteger gcd = pSub1.gcd(qSub1);
        BigInteger lcm = pSub1.divide(gcd).multiply(qSub1);
        BigInteger eksponenPribadi = e.modInverse(lcm);
        BigInteger myModulus = primaP.multiply(primaQ);
        BigInteger primaEksponenP = eksponenPribadi.remainder(primaP.subtract(BigInteger.ONE));
        BigInteger primaEksponenQ = eksponenPribadi.remainder(primaQ.subtract(BigInteger.ONE));
        BigInteger koefisienCRT = primaQ.modInverse(primaP);
        RSAPrivateCrtKeyParameters privku = new RSAPrivateCrtKeyParameters(
                myModulus,
                e,
                eksponenPribadi,
                primaP,
                primaQ,
                primaEksponenP,
                primaEksponenQ,
                koefisienCRT);

        pss.init(true,
                new ParametersWithRandom(privku, secureRandom));
        pss.update(msg, 0, msg_len);
        byte[] result = pss.generateSignature();

        byte[] plainTextBytes = new JSONObject()
                .put("eContent", pesannya)
                .put("signature", Hex.toHexString(result))
                //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.
                .put("subjectPublicKey", myModulus.toString(36))
                .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);

        CipherParameters keyEncParam = new RSAKeyParameters(false, modulus, e);
        keyEncParam = new ParametersWithRandom(keyEncParam, secureRandom);
        OAEPEncoding wrapEngine = new OAEPEncoding(
                rsaBlindedEngine,
                digest,
                digest,
                new byte[0]);
        wrapEngine.init(true, keyEncParam);
        byte[] recipientEncryptedKey = wrapEngine.processBlock(encodedContentEncryptionKey, 0, encodedContentEncryptionKey.length);
        System.out.println("encryptedKey: " + new String(UrlBase64.encode(recipientEncryptedKey)));
        System.out.println("nonce: " + new String(UrlBase64.encode(subadya)));
        System.out.println("encryptedContent: " + new String(UrlBase64.encode(cipherText)));
    }
}

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 (1nbja0qk52ajqk3f7flgyb6ux0y8x80ms73jy7l69d47wy6ii7qiru46ec9f1ct54hvi9g16gz17noj1giurowa9l1nlxwr3t8d97vrsg0v1ms1dzjbr3dlakmc7k8fculthlhaoafi3agn8b9wbjul3b8qodkv1r881rizogqnslz6kykmor429iwwpasgp2w8zvlgml7j6s7mgnfkjsbennd81gfn6qu113s6rhdo1o42ipmbhq7ccvidnef9ku73kmsvxfrswz97zbm8yxypkviwczd8lppkawwltzscp9dx086ks0huih5vux4pt3lv2p5fcf2mx9pattn4sjeo2q9a7a26ol6pwuz7xe4rytksds5q1hcvomnbbf1qhl0jw7cj4fsdhkf120gyaot8ikk4gm2pi0gdxwgu4l9ilk1zc0o0hl9m2nncn995m4i69uldql4symwj79le1dpka3l8sz14m3u9yyogwmth1oezkunqhybox79mrrl7qw5nmqg6w3yj56s5pri7n9izxprsragg6p1avy61bi64lrv9zk5xlaqqn5ydk4zops1788ye51kzvmljjaw1), 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 javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.json.JSONObject;
import org.spongycastle.crypto.CipherParameters;
import org.spongycastle.crypto.digests.SHA512tDigest;
import org.spongycastle.crypto.encodings.OAEPEncoding;
import org.spongycastle.crypto.engines.AESEngine;
import org.spongycastle.crypto.engines.RSABlindedEngine;
import org.spongycastle.crypto.modes.CBCBlockCipher;
import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.crypto.params.ParametersWithIV;
import org.spongycastle.crypto.params.ParametersWithRandom;
import org.spongycastle.crypto.params.RSAKeyParameters;
import org.spongycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.spongycastle.crypto.signers.PSSSigner;
import org.spongycastle.util.encoders.Hex;
import org.spongycastle.util.encoders.UrlBase64;

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

    public static void main(String[] args) throws Exception {
        String myPrimeP = "1fjve7wbfw1c5d8g5ujw8xwxhpphph6693cas5aq4mqclv22q7jkr3xki5xxz2bbt390mord7t08s0v0lfzd5zkigxqxltukqjq4ckkt3cplsfz0vmm9f3kg855s9i0sbisk1kivbr8w2zsai5hoi0ekp451z4dusosfi0wofet4p1pvw25v73epiep9ty5b3b9j51builmu1bnfbinnvx0jlzs3fg1u51q4g7lsdpkk4ezjpm19hoko7vziuqlxlaa3wzyacqxsr2yc06m07byoqm8xozzujjr7x90f95";
        String myPrimeQ = "10zfakscb37fnbr8pxx2ekpfiz2m8k8gq0nzopvrprlieyr43jlkpg6baj8513cspmn34d5k3614k7fooyj6w7po45b7rjq336rzst63xwx24ijug90ut709z9xow86vmjbrrldgo4yhiaty58svs7s6benq8bsht4g2i3kyxhnrycyiws41uj7yxmu9evgf98tp99ra6muu5fghtimrlutdjlf91kj4lrb79h6o6wssjzqbmec8xfjy9cykro6752kqv0a6zg0j1sqfegd957mbjnkgui50wdihpft5n1";

        BigInteger primaP = new BigInteger(myPrimeP, 36);
        BigInteger primaQ = new BigInteger(myPrimeQ, 36);
        BigInteger e = BigInteger.valueOf(0x10001);
        BigInteger pSub1 = primaP.subtract(BigInteger.ONE);
        BigInteger qSub1 = primaQ.subtract(BigInteger.ONE);
        BigInteger gcd = pSub1.gcd(qSub1);
        BigInteger lcm = pSub1.divide(gcd).multiply(qSub1);
        BigInteger eksponenPribadi = e.modInverse(lcm);
        BigInteger myModulus = primaP.multiply(primaQ);
        BigInteger primaEksponenP = eksponenPribadi.remainder(primaP.subtract(BigInteger.ONE));
        BigInteger primaEksponenQ = eksponenPribadi.remainder(primaQ.subtract(BigInteger.ONE));
        BigInteger koefisienCRT = primaQ.modInverse(primaP);

        CipherParameters ktParam = new RSAPrivateCrtKeyParameters(
                myModulus,
                e,
                eksponenPribadi,
                primaP,
                primaQ,
                primaEksponenP,
                primaEksponenQ,
                koefisienCRT);
        ktParam = new ParametersWithRandom(ktParam, new SecureRandom());
        SHA512tDigest digest = new SHA512tDigest(256);

        OAEPEncoding wrapEngine1 = new OAEPEncoding(
                new RSABlindedEngine(),
                digest,
                digest,
                new byte[0]);
        wrapEngine1.init(false, ktParam);

        byte[] recipientEncryptedKey = UrlBase64.decode("Q8oiNxa3ZzTOX4QcGLKIc-tWnpDPNQ4uyVZuhp1ogrCcz7sW4yB5HDJAOPAd1nmj1Y6rdCofUSzYa4ePjPsP6ab8bobXt-bg2RZZvCoV1lw3H0QSEIzi9URk7TLzUM6_u7qhor6ZPVlZG-5CH0_VBUJoFaLrueRc8AfCDuVp3k_H5ATNBLyjKwwbesl9l4ekOkM80LhapaBAXeAtBPMFou9ZkeVE0TSYvAlWJlos8vCU1VEJK__B5tLT9a9q1uRrWK7i8gK9efFF8GMQPch0Zonm9rWXj_Tt2UP7MleDfDQi5AuwksTgn_ZU1a9T-I-NfJqo5DJSCRmZnlxd28-QnVNZ27O64MrO9oIwMIzKAsLfTaxZvVwTX4RcCdwc49D-iz30EaCcHUAKrt2IUQQTLsqp04yCcLSTZmBtTwTo3duICl_dUHAa7YV_6CirZbY_qXNS_3EpbwkkkXsvIY60LF44VgzT85_vj69qweh5zgtqDIf6vug65FXEw38JYFOK");
        byte[] contentEncryptionKey = wrapEngine1.processBlock(recipientEncryptedKey, 0, recipientEncryptedKey.length);
        SecretKey key = new SecretKeySpec(contentEncryptionKey, "AES");
        byte[] subadya = UrlBase64.decode("0RChFu79QkR_axNo");
        IvParameterSpec paramSpec = new IvParameterSpec(subadya);
        ParametersWithIV param
                = new ParametersWithIV(new KeyParameter(key.getEncoded()), paramSpec.getIV());
        byte[] cipherText = UrlBase64.decode("s6lK6eIFSBGih0BhZCfflADHx0LjLNMEsb2kHcubxMhSkOKpjIFXxIJYkbIMwpR3X_pCTC1x82nM52fXw_0KN0GDPW1pP1oJOCgbzkp9I0SqcvaGZpVKXtj7ZEBXWteItiOeuxjzOlUQS-t4FMFFmlQh9HEBZDSIhM1ZypEGhzbYtl51NnVjPHVqXrOi7R4WF1MS6Rckbk0n5i3E1UaZMC4HzcAyi0WPNc693nSAMbW48Np3Rn1toDozg4FxdFZzo5OQivq7Hy3tk86ynpCcZO0wyyes5pkUOsfESX6svCmZbP43-eduPk4Iiq6yNcH-qi8JfbZ0VQkdFuR4Kaq6V2-i5rgDFj30UubpQ4_v0-AxYAJ6ucQ3rLvMYdh3a3vbDaiirUQjiyEHWycqih3KFtgN5B8pbAE4e2vH23LBQhlbQbGlfKNR1PulIhvicodRdpHvd5g9qKyKb_BP3BZNW6_m8pz7tI-uP0rMBJDwjiZey0q-O_QCworRCz__Hz4YwAZ089U2nTQhdXel0mUE2bRbMYYLfp2usiQh1SRBpOJQ2gF7ej2B3fA8v8YcTS05lh7oA1rOz8Kan4UeTGQmvgvQir5MHHyOVsRpkqykeBV_maHfp_IgSxwhu21Wwn3r_rWdpkCd6_Srvw_F_TfqWmgsryMsgVhiLSRlcjCiB6KjGGsgJEfrNHKtRLwsexfTslx9dNFmPn1lIBdvYx6KZrRDDbj6wero8eTr0rUo-Oc0s-ioAVoveFdzI1JciM2RUUG5WOquV4iiFoNnOFDdkNnGPcDQU8KvtWEffD6GrwrOtuBO1ZjlgJva8k_PIwWKxZ6IyDz3zDAo34QnZB0esr5e5-sbSurKap0dPdFMbUskbCp9bXbCcT242ufP_pjZyZHyBY-0Jd4o1dd2qSKGwAXK_rIRU0TVEtN1keJA3SgXG2e5smwrjZyztqtF_0X2Ez7mZDATvjKPHaPoxfsVJrMVZ21n3dRRRk-3yS5-iR9gKVSipIklI3JN4d5HBtjTa6WkHsgJOmX0wAvTKBTP0RsBjyAmaGQPpJccWL3zdeNlxFwoJWgiQJNWt4OU_HV2PAeN93ZcllZfNvIRggXvillnW457F3KVfNlVisvoAerR6xCP9Y89d6b6ENYkJ3p_cPKPPSj4v0TD7T1PkcpxoU6RuIdURT3bvU6-7mitWECiZ9NYmVosqPHzcK58sStdfX0uo6DF8ZtgLUQefvVpyFzlDzG_hs82H0c35FcqdYN_VwD3Ao8gWlHPH6NT7er_katc4-zidhJBLqKPAq9QA8D_NwH0j07xWt3wBqA_n9frdA22CqfZSKulHfJjtwXixgj3HkjiQyFPVDNU1xNu_UKc_z8Sxr7736usWRaoYyZAusgpJc4KgHbO5FFLClbVK9wFZrIyB538SDt0fmXg11r14kBvs__qrXXQ3Iu9IyxgjNrYpELi60InTscOdq7ThLCIvLv8O4-qNNetymSzpwlUVPNvq_cOhOge0dEO7M42t9LaZvn8W8ltCKtUV0YHlj6G7LSRwKKkabIQy505q6bEIyZcAKjmwWn5uwoSr-Rf_d1XmWfA__cV8L2IIxcR-K5QTl3gt7T6-fcZQkL1wCShCU6u9-C3E4V2txSiNCWRoZ8QnFf3LAp8KtcQm1jw6iEMl6H7_XQvPkpb87-2yNEWijmKKMa0gjzrCiGLpfsuJN2ayzn4CYkZHctaHdE0v3g6HJGSwv20Lx7n1BYLXe625zFulqmrpycE3EpJbKX8eeXVVXSwhCzXANFpsvH3LHvng_aX_pl5220JH9pw5YBOQ1OUSxKllqHcEsXex29LDHqBG1V6SyGkN2kd0D6XKZhPNAwf-EHa7Ktm8zjMvzXuwOo2_VkW1s1LwxLZPY_4");
        PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
        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);

        //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.
        String subjectPublicKey = jsonObject.getString("subjectPublicKey");
        BigInteger senderPublicKey = new BigInteger(subjectPublicKey, 36);
        PSSSigner pss = new PSSSigner(new RSABlindedEngine(), digest, digest, digest.getDigestSize(), PSSSigner.TRAILER_IMPLICIT);
        pss.init(false, new RSAKeyParameters(false, senderPublicKey, e));
        pss.update(message, 0, message.length);
        System.out.println("konten: " + content);
        System.out.println("TTD: " + signature);
        System.out.println("valid: " + pss.verifySignature(sigBytes));
    }
}

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 ×