Unduh berkas bcprov-jdk15on-159.jar, salin kode sumber berikut ke dalam editor kode sumber kesukaan anda, simpan berkas dengan nama EllipticCurveDiffieHellmanEphemeral.java

//berkas EllipticCurveDiffieHellmanEphemeral.java
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidParameterSpecException;
import java.util.Base64;
import javax.crypto.SecretKey;
import javax.crypto.spec.*;
import javax.xml.bind.DatatypeConverter;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.asn1.x9.X9IntegerConverter;
import org.bouncycastle.crypto.*;
import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
import org.bouncycastle.crypto.digests.SHA384Digest;
import org.bouncycastle.crypto.engines.*;
import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.jcajce.provider.asymmetric.ec.*;
import org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi.ECDH;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.*;

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

    private static final String ellipticCurvePrivateKey;

    static {
        Security.insertProviderAt(new BouncyCastleProvider(), 1);
        ellipticCurvePrivateKey
                = "MIIBXwIBADCB6gYHKoZIzj0CATCB3gIBATArBgcqhkjOPQEBAiB/////////////"
                + "////////////////////////////7TBEBCAqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
                + "qqqqqqqYSRShRAQge0Je0Je0Je0Je0Je0Je0Je0Je0Je0Je0JgtenHcQyGQEQQQq"
                + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq0kWiCuGaG4oIa04B7dLHdI0UyS"
                + "PU1+bXxhsinpxaJ+ztPZAiAQAAAAAAAAAAAAAAAAAAAAFN753qL3nNZYEmMaXPXT"
                + "7QIBCARtMGsCAQEEIAHuUF+hqgHX+87jRXu2vIYVP9Ia7ikwXO/pJN0TpNIYoUQD"
                + "QgAEDQmu8fQdG6MwV6tHDvEKHfo3D6oVEaKoic9QI3hS/bAnxyPWlFauSvtgVdAm"
                + "MGoTcTVb67F0tf/NgFrCuIYWDQ==";
    }

    public static void main(String args[]) throws Exception {
        DERBitString origPublicKey = new DERBitString(DatatypeConverter.parseHexBinary(
                "04"//tag
                + "31"//length
                + "ead49f08806c87f2ecae371ce4de58546821b893b2aae2ed6e0624d7894789"
                + "2adcf074fd15b0fcf72521ce6356ed90df40833df02b963d6aa448306f382b0f"));
        DLSequence keyEncAlgorithm = (DLSequence) new ASN1InputStream(
                DatatypeConverter.parseHexBinary(
                        "30"//tag
                        + "15"//length
                        + "06062b8104010e02"//1.3.132.1.14.2 (dhSinglePass-stdDH-hkdf-sha384-scheme)
                        + "30"//tag
                        + "0b"//length
                        + "060960864801650304012d"//2.16.840.1.101.3.4.1.45 (id_aes256_wrap)
                )).readObject();
        DLSequence messageAlgorithm = (DLSequence) new ASN1InputStream(
                DatatypeConverter.parseHexBinary(
                        "30" + "1B"
                        + "060B2A864886F70D0109100312"//ChaCha20-Poly1305
                        + "04" + "0C"
                        + "137FD93917D359D759D53EC8"//Octet String[12] 
                )).readObject();
        DEROctetString encryptedKey = new DEROctetString(
                DatatypeConverter.parseHexBinary(
                        "0C0857C83069B88115AC907203ACE9CFBDA2F506"
                        + "D2B5E05D63AE0A9EAE7B8F6059741BD7051E2BD2"));
        DEROctetString encryptedContent = new DEROctetString(
                DatatypeConverter.parseHexBinary(
                        "25AB51E372AEE452573DFDDCE4126A"
                        + "487AD855722FA2144ABDD6E60A3257"
                        + "4E1E9E96F07B45F4831EE31713FEA3"
                        + "71196EF137FF5C9991BF49FB28940C"
                        + "FD3A4A55A108351C48B139"));
        AlgorithmIdentifier keyEncryptionAlgorithm
                = AlgorithmIdentifier.getInstance(keyEncAlgorithm);
        AlgorithmIdentifier wrapAlg
                = AlgorithmIdentifier.getInstance(keyEncryptionAlgorithm.getParameters());
        AlgorithmIdentifier contentEncryptionAlgorithm
                = AlgorithmIdentifier.getInstance(messageAlgorithm);
        byte[] der = Base64.getDecoder().decode(ellipticCurvePrivateKey);
        ECDH keyFactory = new ECDH();
        BCECPrivateKey receiverPrivateKey = (BCECPrivateKey) keyFactory.generatePrivate(PrivateKeyInfo.getInstance(der));
        AlgorithmIdentifier recKeyAlgId
                = AlgorithmIdentifier.getInstance(
                        ASN1Sequence.getInstance(der)
                                .getObjectAt(1));
        SubjectPublicKeyInfo senderKey
                = new SubjectPublicKeyInfo(recKeyAlgId, origPublicKey.getOctets());
        BCECPublicKey senderPublicKey = (BCECPublicKey) keyFactory.generatePublic(senderKey);
        SecretKey agreedWrapKey = calculateAgreedWrapKey(keyEncryptionAlgorithm, wrapAlg,
                senderPublicKey, receiverPrivateKey);
        Key recoveredContentEncryptionKey
                = unwrapSessionKey(wrapAlg.getAlgorithm(), agreedWrapKey,
                        contentEncryptionAlgorithm.getAlgorithm(), encryptedKey.getOctets());
        String decryptedContent
                = decryptContent(contentEncryptionAlgorithm,
                        recoveredContentEncryptionKey, encryptedContent.getOctets());
        System.out.println(decryptedContent);
    }

    private static SecretKey calculateAgreedWrapKey(AlgorithmIdentifier keyEncryptionAlgorithm,
            AlgorithmIdentifier wrapAlg, BCECPublicKey senderPublicKey, BCECPrivateKey receiverPrivateKey) throws IOException {
        ECDHBasicAgreement agreement = new ECDHBasicAgreement();
        ECParameterSpec es = receiverPrivateKey.getParameters();
        ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(
                receiverPrivateKey.getD(),
                new ECDomainParameters(es.getCurve(), es.getG(), es.getN(), es.getH(), es.getSeed()));
        agreement.init(privKey);
        ECParameterSpec s = senderPublicKey.getParameters();
        ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
                senderPublicKey.getQ(),
                new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
        X9IntegerConverter converter = new X9IntegerConverter();
        byte[] agreedKey = converter.integerToBytes(agreement.calculateAgreement(pubKey),
                converter.getByteLength(privKey.getParameters().getCurve()));
        int keySize = 256;
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(wrapAlg);
        v.add(new DERTaggedObject(true, 2, new DEROctetString(Pack.intToBigEndian(keySize))));//SizeInBits
        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        DEROutputStream dOut = new DEROutputStream(bOut);
        dOut.writeObject(new DERSequence(v));
        byte[] ukmParameters = bOut.toByteArray();
        HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA384Digest());
        hkdf.init(new HKDFParameters(agreedKey, null, ukmParameters));
        byte[] secret = new byte[keySize / 8];//SizeInOctets
        hkdf.generateBytes(secret, 0, secret.length);
        return new SecretKeySpec(secret, "AES");
    }

    private static Key unwrapSessionKey(ASN1ObjectIdentifier wrapAlg,
            SecretKey agreedKey, ASN1ObjectIdentifier contentEncryptionAlgorithm, byte[] encryptedContentEncryptionKey) throws InvalidCipherTextException {
        CipherParameters param = new KeyParameter(agreedKey.getEncoded());
        param = new ParametersWithRandom(param, new SecureRandom());
        param = ((ParametersWithRandom) param).getParameters();
        AESWrapEngine wrapEngine = new AESWrapEngine();
        wrapEngine.init(false, param);
        byte[] encoded = wrapEngine.unwrap(encryptedContentEncryptionKey, 0, encryptedContentEncryptionKey.length);
        return new SecretKeySpec(encoded, contentEncryptionAlgorithm.getId());
    }

    private static String decryptContent(AlgorithmIdentifier encryptedDataAlgorithm,
            Key decryptionKey, byte[] cipherTextBytes) throws IOException, IllegalStateException, InvalidCipherTextException, NoSuchAlgorithmException, NoSuchProviderException, InvalidParameterSpecException {
        ASN1Encodable sParams = encryptedDataAlgorithm.getParameters();
        AlgorithmParameters params;
        params = AlgorithmParameters.getInstance("AES", "BC");
        params.init(sParams.toASN1Primitive().getEncoded());
        IvParameterSpec paramSpec = params.getParameterSpec(IvParameterSpec.class);
        ParametersWithIV param
                = new ParametersWithIV(new KeyParameter(decryptionKey.getEncoded()), paramSpec.getIV());
        ChaCha7539Engine cipher = new ChaCha7539Engine();
        cipher.init(false, param);
        byte[] plainText = new byte[cipherTextBytes.length];
        cipher.processBytes(cipherTextBytes, 0, cipherTextBytes.length, plainText, 0);
        return new String(plainText, StandardCharsets.UTF_8);
    }
}

Buka program terminal atau command prompt, lakukan kompilasi program dengan menjalankan perintah javac -cp bcprov-jdk15on-159.jar EllipticCurveDiffieHellmanEphemeral.java, lalu jalankan program tersebut dengan menjalankan perintah java -cp "bcprov-jdk15on-159.jar:." EllipticCurveDiffieHellmanEphemeral, bila anda menggunakan sistem operasi Windows, tanda titik dua : (colon), diganti menjadi titik koma ; (semicolon). Berikut luaran program tersebut di atas.

Contoh pesan yang terenkripsi dengan format EnvelopedData (1.2.840.113549.1.7.3)

Program java di atas, mengambil contoh pesan terenkripsi dari pesan berikut. Pesan terenkripsi disandikan dengan Base64.

-----BEGIN PKCS7-----
MIAGCSqGSIb3DQEHA6CAMIACAQAxggFdoYIBWQIBA6BToVEwCwYHKoZIzj0CAQUAA0IABDHq1J8I
gGyH8uyuNxzk3lhUaCG4k7Kq4u1uBiTXiUeJKtzwdP0VsPz3JSHOY1btkN9Agz3wK5Y9aqRIMG84
Kw8wFQYGK4EEAQ4CMAsGCWCGSAFlAwQBLTCB5zCB5DCBtzCBrjEmMCQGCSqGSIb3DQEJARYXcm9z
ZXR0YW5ldEBtZW5kZWxzb24uZGUxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNV
BAcMBkJlcmxpbjEiMCAGA1UECgwZbWVuZGVsc29uLWUtY29tbWVyY2UgR21iSDEiMCAGA1UECwwZ
bWVuZGVsc29uLWUtY29tbWVyY2UgR21iSDENMAsGA1UEAwwEbWVuZAIEQ4798wQoU6Gfe6jpUQBc
dUOrm0ZLLSyED0NyZuJ5btK69H+C73Pdf9TMYPQ6ajCABgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEu
MBEEDOqcvcuXhbYQv/7tQQIBEKCABFf/d8u1ID5dmKsrgVeMXo+al/6F8TaBxKN9WTqr8J0FXGiq
nC/s1Z0TOzgF1xxnYmu7rs2faVjW8OykBMTWXjCVNuQ7nS16SVC51IA1UU7xWJF79QTSyuMAAAAA
AAAAAAAA
-----END PKCS7-----
EnvelopedData berisi konten yang terenkripsi beserta kunci untuk membuka konten yang terenkripsi tersebut, tapi kuncinya juga terenkripsi.

Bila penyandian Base64 tersebut dikonversi menjadi heksadesimal, menjadi seperti berikut
308006092A864886F70D010703A08030800201003182015DA1820159020103A053A151300B06072A8648CE3D020105000342000431EAD49F08806C87F2ECAE371CE4DE58546821B893B2AAE2ED6E0624D78947892ADCF074FD15B0FCF72521CE6356ED90DF40833DF02B963D6AA448306F382B0F301506062B8104010E02300B060960864801650304012D3081E73081E43081B73081AE3126302406092A864886F70D0109011617726F73657474616E6574406D656E64656C736F6E2E6465310B3009060355040613024445310F300D06035504080C064265726C696E310F300D06035504070C064265726C696E31223020060355040A0C196D656E64656C736F6E2D652D636F6D6D6572636520476D624831223020060355040B0C196D656E64656C736F6E2D652D636F6D6D6572636520476D6248310D300B06035504030C046D656E640204438EFDF3042853A19F7BA8E951005C7543AB9B464B2D2C840F437266E2796ED2BAF47F82EF73DD7FD4CC60F43A6A308006092A864886F70D010701301E060960864801650304012E3011040CEA9CBDCB9785B610BFFEED41020110A0800457FF77CBB5203E5D98AB2B81578C5E8F9A97FE85F13681C4A37D593AABF09D055C68AA9C2FECD59D133B3805D71C67626BBBAECD9F6958D6F0ECA404C4D65E309536E43B9D2D7A4950B9D48035514EF158917BF504D2CAE300000000000000000000

Subject Public Key

Bagian

(03) 42
     00
     04 31 EA D4 9F 08 80 6C  87 F2 EC AE 37 1C E4 DE
     58 54 68 21 B8 93 B2 AA  E2 ED 6E 06 24 D7 89 47
     89 2A DC F0 74 FD 15 B0  FC F7 25 21 CE 63 56 ED
     90 DF 40 83 3D F0 2B 96  3D 6A A4 48 30 6F 38 2B
     0F
adalah kunci publik milik pengirim, atau umum disebut sebagai subjectPublicKey, menurut RFC 5480, bagian 2, kolom ini bertipe BIT STRING. Penandanya adalah nilai Tag pada byte pertama yaitu 0x03.

Pembaca bisa mendapatkan deretan nilai heksadesimal kunci publik tersebut dengan cara sebagai berikut.

import java.security.interfaces.ECPublicKey;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERBitString;
import javax.xml.bind.DatatypeConverter;

ECPublicKey senderPublicKey = ...;
ASN1Sequence seq = ASN1Sequence.getInstance(senderPublicKey.getEncoded());
DERBitString keyData = DERBitString.getInstance(seq.getObjectAt(1));
System.out.println(DatatypeConverter.printHexBinary(keyData.getEncoded()));

Key Agreement dan Key Wrap

Bagian berikutnya,

(30) 1A
    (06) 0B 2A 86 48 86 F7 0D 01 09 10 03 13  //1.2.840.113549.1.9.16.3.19 keyAgreementOID
     30 0B
       (06) 09 60 86 48 01 65 03 04 01 2D  //2.16.840.1.101.3.4.1.45 keyEncryptionOID
adalah nomor OID algoritma Key Agreement beserta parameternya berupa nomor ID algoritma Key Wrap. Dalam pesan di atas, bila diterjemahkan ke bahasa manusia, 2A 86 48 86 F7 0D 01 09 10 03 13 adalah penyandian OID algoritma dhSinglePass-stdDH-hkdf-sha384-scheme, sedangkan 60 86 48 01 65 03 04 01 2D adalah penyandian OID algoritma id-aes256-wrap. Algoritma Key Agreement menentukan cara pembangkitan kunci bersama, bila di Java menggunakan kelas javax.crypto.KeyAgreement, sedangkan algoritma Key Wrap untuk mengenkripsi kunci enkripsi konten menggunakan kunci bersama yang dibangkitkan oleh kelas javax.crypto.KeyAgreement tadi. Kedua algoritma ini ditentukan oleh pengirim, adapun pilihan algoritma Key Agreement antara lain bisa dilihat pada bagian 7 draft-ietf-curdle-cms-ecdh-new-curves-10, sedangkan pilihan algoritma Key Wrap yang ada bisa dilihat dalam RFC 5753 bagian 7.1.5.

Di dalam RFC 5753 bagian 3.1.1, algoritma Key Agreement disebut sebagai keyEncryptionAlgorithm, sedangkan algoritma Key Wrap sebagai parameter dari keyEncryptionAlgorithm. keyEncryptionAlgorithm memiliki tipe data AlgorithmIdentifier. Tipe data AlgorithmIdentifier merupakan SEQUENCE dari OBJECT IDENTIFIER dan ANY DEFINED BY algorithm OPTIONAL. Tipe data SEQUENCE memiliki nilai Tag 0x30, sedangkan OBJECT IDENTIFIER memiliki nilai Tag 0x06. Tapi di dalam kode sumber kelas JceKeyAgreeRecipientInfoGenerator.java, Key Agreement disebut sebagai keyAgreementOID, sedangkan Key Wrap disebut sebagai keyEncryptionOID.

Pembaca bisa mendapatkan deretan nilai heksadesimal algoritma key agreement dan key wrap tersebut dengan cara sebagai berikut.

import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import javax.xml.bind.DatatypeConverter;

AlgorithmIdentifier keyAgreeAlg = new AlgorithmIdentifier(new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.19"),
new AlgorithmIdentifier(new ASN1ObjectIdentifier("2.16.840.1.101.3.4.1.45")));
System.out.println(DatatypeConverter.printHexBinary(keyAgreeAlg.getEncoded()));

Encrypted Key

Bagian

(04) 28
     0C 08 57 C8 30 69 B8 81
     15 AC 90 72 03 AC E9 CF
     BD A2 F5 06 D2 B5 E0 5D
     63 AE 0A 9E AE 7B 8F 60
     59 74 1B D7 05 1E 2B D2
adalah Encrypted Key, merupakan kunci konten enkripsi yang dienkripsi menggunakan kunci bersama yang dibangkitkan dengan algoritma Key Agree, pengenkripsian kunci konten enkripsi menggunakan algoritma Key Wrap. Menurut RFC 5652, bagian 6.2, kolom ini bertipe OCTET STRING. Penandanya adalah nilai Tag 0x04.

Pembaca bisa mendapatkan deretan nilai heksadesimal kunci konten terenkripsi tersebut dengan cara sebagai berikut.

import java.io.ByteArrayOutputStream;
import java.security.SecureRandom;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x9.X9IntegerConverter;
import org.bouncycastle.crypto.*;
import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
import org.bouncycastle.crypto.digests.SHA384Digest;
import org.bouncycastle.crypto.engines.AESWrapEngine;
import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.jcajce.provider.asymmetric.ec.*;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.Pack;

public class EncryptedKey {

    public static void main(String[] args) throws Exception {
        AlgorithmIdentifier contentEncKeyAlgOid = new AlgorithmIdentifier(new ASN1ObjectIdentifier("2.16.840.1.101.3.4.1.46"));
        AlgorithmIdentifier keyAgreementOid = new AlgorithmIdentifier(new ASN1ObjectIdentifier("1.3.132.1.14.2"));
        AlgorithmIdentifier keyWrapAlgOid = new AlgorithmIdentifier(new ASN1ObjectIdentifier("2.16.840.1.101.3.4.1.45"));
        CipherKeyGenerator engine = new CipherKeyGenerator();
        engine.init(new KeyGenerationParameters(new SecureRandom(), 256));
        SecretKey contentEncryptionKey = new SecretKeySpec(engine.generateKey(), "ChaCha7539");
        SecretKeySpec sks = new SecretKeySpec(contentEncryptionKey.getEncoded(), contentEncKeyAlgOid.getAlgorithm().getId());
        BCECPublicKey recipientPublicKey = ...;
        BCECPrivateKey originatorPrivateKey = ...;
        ECDHBasicAgreement agreement = new ECDHBasicAgreement();
        ECParameterSpec es = originatorPrivateKey.getParameters();
        ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(
                originatorPrivateKey.getD(),
                new ECDomainParameters(es.getCurve(), es.getG(), es.getN(), es.getH(), es.getSeed()));
        agreement.init(privKey);
        ECParameterSpec s = recipientPublicKey.getParameters();
        ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
                recipientPublicKey.getQ(),
                new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
        X9IntegerConverter converter = new X9IntegerConverter();
                byte[] agreedKey = converter.integerToBytes(agreement.calculateAgreement(pubKey), converter.getByteLength(privKey.getParameters().getCurve()));
        int keySize = 256;
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(keyWrapAlgOid);
        v.add(new DERTaggedObject(true, 2, new DEROctetString(Pack.intToBigEndian(keySize))));//SizeInBits
        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        DEROutputStream dOut = new DEROutputStream(bOut);
        dOut.writeObject(new DERSequence(v));
        byte[] ukmParameters = bOut.toByteArray();
        HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA384Digest());
        hkdf.init(new HKDFParameters(agreedKey, null, ukmParameters));
        byte[] secret = new byte[keySize / 8];//SizeInOctets
        hkdf.generateBytes(secret, 0, secret.length);
        SecretKey keyEncryptionKey = new SecretKeySpec(secret, "AES");
        CipherParameters param = new KeyParameter(keyEncryptionKey.getEncoded());
        param = new ParametersWithRandom(param, new SecureRandom());
        param = ((ParametersWithRandom) param).getParameters();
        AESWrapEngine wrapEngine = new AESWrapEngine();
        wrapEngine.init(true, param);
        byte[] encoded = sks.getEncoded();
        byte[] recipientEncryptedKey = wrapEngine.wrap(encoded, 0, encoded.length);
        System.out.println(DatatypeConverter.printHexBinary(new DEROctetString(recipientEncryptedKey).getEncoded()));
    }
}

Content Encryption Algorithm

Bagian berikutnya,

(30) 1B
    (06) 0B 2A 86 48 86 F7 0D 01 09 10 03 12
    (04) 0C
         13 7F D9 39 17 D3
         59 D7 59 D5 3E C8
adalah nomor OID algoritma Content Encryption beserta parameternya berupa Initialization Vector. Dalam pesan di atas, bila diterjemahkan ke bahasa manusia, 2A 86 48 86 F7 0D 01 09 10 03 12 adalah penyandian OID algoritma ChaCha20-Poly1305, sedangkan 13 7F D9 39 17 D3 59 D7 59 D5 3E C8 adalah parameter algoritma Content Encryption yaitu Nonce-nya. Algoritma Content Encryption menentukan cara pengenkripsian pesan, bila di Java menggunakan kelas javax.crypto.Cipher, sedangkan Nonce digunakan sebagai parameter konstruktor kelas javax.crypto.spec.IvParameterSpec. Menurut RFC 5652, bagian 6.1, kolom ini bertipe AlgorithmIdentifier. Tipe data AlgorithmIdentifier merupakan SEQUENCE dari OBJECT IDENTIFIER dan ANY DEFINED BY algorithm OPTIONAL. Tipe data SEQUENCE memiliki nilai Tag 0x30, sedangkan OBJECT IDENTIFIER memiliki nilai Tag 0x06. Untuk kolom Nonce bertipe OCTET STRING, penandanya adalah nilai Tag 0x04.

Pembaca bisa mendapatkan deretan nilai heksadesimal algoritma content encryption dan initialization vector tersebut dengan cara sebagai berikut.

import java.security.*;
import javax.crypto.spec.IvParameterSpec;
import javax.xml.bind.DatatypeConverter;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;

AlgorithmIdentifier contentEncKeyAlgOid = new AlgorithmIdentifier(new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.18"));
byte[] nonce = new byte[12];
new SecureRandom().nextBytes(nonce);
AlgorithmParameters params = AlgorithmParameters.getInstance("AES", "BC");
params.init(new IvParameterSpec(nonce));
ASN1Encodable asn1Params = ASN1Primitive.fromByteArray(params.getEncoded());
AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(contentEncKeyAlgOid.getAlgorithm(), asn1Params);
System.out.println(DatatypeConverter.printHexBinary(algorithmIdentifier.getEncoded()));

Encrypted Content

Bagian berikutnya,

(04) 47
     25 AB 51 E3 72 AE E4 52 57 3D
     FD DC E4 12 6A 48 7A D8 55 72
     2F A2 14 4A BD D6 E6 0A 32 57
     4E 1E 9E 96 F0 7B 45 F4 83 1E
     E3 17 13 FE A3 71 19 6E F1 37
     FF 5C 99 91 BF 49 FB 28 94 0C
     FD 3A 4A 55 A1 08 35 1C 48 B1
     39
adalah pesan terenkripsi (cipher text) milik pengirim yang ingin disampaikan kepada penerima, yang berisi kata-kata berikut:
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

Halo Dunia
menurut RFC 5652 bagian 6.1, EncryptedContent bertipe OCTET STRING, penandanya adalah nilai Tag 0x04.

Pembaca bisa mendapatkan deretan nilai heksadesimal encryptedContent tersebut dengan cara sebagai berikut.

import java.nio.charset.StandardCharsets;
import java.security.*;
import javax.crypto.SecretKey;
import javax.crypto.spec.*;
import javax.xml.bind.DatatypeConverter;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.crypto.*;
import org.bouncycastle.crypto.engines.ChaCha7539Engine;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

CipherKeyGenerator engine = new CipherKeyGenerator();
engine.init(new KeyGenerationParameters(new SecureRandom(), 256));
SecretKey contentEncryptionKey = new SecretKeySpec(engine.generateKey(), "ChaCha7539");
byte[] nonce = new byte[12];
new SecureRandom().nextBytes(nonce);
AlgorithmParameters params = AlgorithmParameters.getInstance("AES", "BC");
params.init(new IvParameterSpec(nonce));
IvParameterSpec paramSpec = params.getParameterSpec(IvParameterSpec.class);
ParametersWithIV param
        = new ParametersWithIV(new KeyParameter(contentEncryptionKey.getEncoded()), paramSpec.getIV());
ChaCha7539Engine cipher = new ChaCha7539Engine();
cipher.init(true, param);
byte[] plainTextBytes = ("Content-Type: text/plain\r\n"
        + "Content-Transfer-Encoding: 7bit\r\n\r\n"
        + "Halo Dunia").getBytes(StandardCharsets.UTF_8);
byte[] encryptedContent = new byte[plainTextBytes.length];
cipher.processBytes(plainTextBytes, 0, plainTextBytes.length, encryptedContent, 0);
System.out.println(DatatypeConverter.printHexBinary(new DEROctetString(encryptedContent).getEncoded()));

Jadi, kalau S/MIME atau CMS atau PKCS7 mulai ditinggalkan oleh pengembang karena penggunaan ASN.1, dan beralih ke JSON, struktur JSON yang pas untuk membungkus pesan terenkripsi apakah kira-kira seperti di bawah ini ya? Ah, tapi as2.amazonsedi.com masih pakai S/MIME dan versi tomcat yang dipakai 6.0.48 baru keluar 15 November 2016.

{
   "subjectPublicKey":[
      4,
      49,
      -22,
      -44
   ],
   "keyEncryptionAlgorithm":{
      "algorithm":"1.3.132.1.14.2",
      "parameters":"2.16.840.1.101.3.4.1.45"
   },
   "encryptedKey":[
      4,
      40,
      -7,
      -25
   ],
   "contentEncryptionAlgorithm":{
      "algorithm":"2.16.840.1.101.3.4.1.46",
      "parameters":[
         4,
         16,
         50,
         115
      ]
   },
   "encryptedContent":[
      4,
      80,
      114,
      33
   ]
}

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 ×