Search code examples
javabouncycastle

Importing and exporting keys


Hello I am trying to import and export (to and from string) public/private keys for RSA, ECC, AES GCM, and ChaChaPoly1305.

I am using bouncy castle 1.59 to accomplish most of this. RSA I can use a KeyFactory natively supported by java so that is probably fine.

However, the other ones do not seem possible. Does anyone know of previous work in this area? I am looking for something like wolfcrypt's import and export functionality.

Essentially I need to be able to store a key and then recreate it.

Here is my code ( I know its a lot but its what I got).

Important snippet

    public static void packRsaKey(Key key, KeyPair keyPair) {
        key.toBuilder()
                .addPrimaryKey(ByteString.copyFrom(keyPair.getPrivate().getEncoded()))
                .addPublicKey(ByteString.copyFrom(keyPair.getPublic().getEncoded()))
                .build();
    }

    public static void packECCKey(Key key, KeyPair keyPair) {
        key.toBuilder()
                .addPrimaryKey(ByteString.copyFrom(keyPair.getPrivate().getEncoded()))
                .addPublicKey(ByteString.copyFrom(keyPair.getPublic().getEncoded()))
                .build();
    }

    public static void packAESGCMKey(Key key, SecretKey secretKey, byte[] IV) {
        key.toBuilder().setIv(ByteString.copyFrom(IV)).addPrimaryKey(ByteString.copyFrom(secretKey.getEncoded())).build();
    }

    public static void packChaChaPoly1305Key(Key key, SecretKey secretKey) {
        key.toBuilder().addPrimaryKey(ByteString.copyFrom(secretKey.getEncoded()));
    }

    public static PrivateKey unpackRsaPrivateKey(Key key) throws InvalidKeySpecException, NoSuchAlgorithmException {
        KeyFactory kf = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(key.getPublicKey(0).toByteArray());
        return kf.generatePrivate(pkSpec);
    }

    public static PublicKey unpackRsaPublicKey(Key key) throws InvalidKeySpecException, NoSuchAlgorithmException {
        KeyFactory kf = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(key.getPrimaryKey(0).toByteArray());
        return kf.generatePublic(pkSpec);
    }

    public static SecretKey unpackAESGCMKey(Key key) {
        return new SecretKeySpec(key.getPrimaryKey(0).toByteArray(), 0, key.getPrimaryKey(0).toByteArray().length, "AES");
    }

    public static SecretKey unpackChaChaPoly1305Key(Key key) {
        return new SecretKeySpec(key.getPrimaryKey(0).toByteArray(), 0, key.getPrimaryKey(0).toByteArray().length, "ChaCha20");
    }

    public static PrivateKey unpackECCPrivateKey(Key key) throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyFactory kf = KeyFactory.getInstance("EC");
        X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(key.getPublicKey(0).toByteArray());
        return kf.generatePrivate(pkSpec);
    }

    public static PublicKey unpackECCPublicKey(Key key) throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyFactory kf = KeyFactory.getInstance("EC");
        X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(key.getPrimaryKey(0).toByteArray());
        return kf.generatePublic(pkSpec);
    }

Full code

package com.keiros.security.encryption;

import java.security.*;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import com.google.protobuf.ByteString;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.jce.provider.BouncyCastleProvider;


import com.keiros.security.AsymmetricEncryptionClass.AsymmetricEncryption;
import com.keiros.security.KeyClass.Key;
import com.keiros.security.SymmetricEncryptionClass.SymmetricEncryption;

/**
 * Helper class for encrypting strings.
 */
public class EncryptionHelper {

    public static final int AES_KEY_SIZE = 32;
    public static final int GCM_IV_LENGTH = 16;
    public static final int GCM_TAG_LENGTH = 16;


    public static boolean generateKey(Key key) {

        if (key.hasSymmetricKeyType()) {
            return generateSymmetricKey(key);
        } else if (key.hasAsymmetricKeyType()) {
            return generateAsymmetricKey(key);
        }
        return false;
    }

    public static boolean generateSymmetricKey(Key key) {
        if (key.getSymmetricKeyType().getType() == SymmetricEncryption.Types.AES_GCM) {
            try {
                generateAESGCMKey(key);
                return true;
            } catch (Exception e) {
                return false;
            }
        } else if (key.getSymmetricKeyType().getType() == SymmetricEncryption.Types.CHA_CHA_20_POLY_1305) {
            try {
                packChaChaPoly1305Key(key, generateChaChaPoly1305Key());
                return true;
            } catch (Exception e) {
                return false;
            }
        }
        return false;
    }

    public static SecretKey generateChaChaPoly1305Key() throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("ChaCha20");
        keyGenerator.init(32);
        return keyGenerator.generateKey();
    }

    public static void generateAESGCMKey(Key key) throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(AES_KEY_SIZE);
        // Generate Key
        byte[] IV = new byte[GCM_IV_LENGTH];
        SecureRandom random = new SecureRandom();
        random.nextBytes(IV);

        SecretKey secretKey = keyGenerator.generateKey();
        packAESGCMKey(key, secretKey, IV);
    }

    public static boolean generateAsymmetricKey(Key key) {
        if (key.getAsymmetricKeyType().getType() == AsymmetricEncryption.Types.ECC) {
            try {
                KeyPair keyPair = generateECCKeys();
                packECCKey(key, keyPair);
            } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchProviderException e) {
                return false;
            }
        } else if (key.getAsymmetricKeyType().getType() == AsymmetricEncryption.Types.SSH_2_RSA) {
            try {
                KeyPair keyPair = generateRsaKeys();
                packRsaKey(key, keyPair);
                return true;
            } catch (NoSuchAlgorithmException e) {
                return false;
            }
        }
        return false;
    }

    public static KeyPair generateRsaKeys() throws NoSuchAlgorithmException {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(2048);
        return kpg.generateKeyPair();
    }

    public static KeyPair generateECCKeys() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
        kpg.initialize(new ECGenParameterSpec("secp256r1"));
        return kpg.generateKeyPair();
    }

    public static void packRsaKey(Key key, KeyPair keyPair) {
        key.toBuilder()
                .addPrimaryKey(ByteString.copyFrom(keyPair.getPrivate().getEncoded()))
                .addPublicKey(ByteString.copyFrom(keyPair.getPublic().getEncoded()))
                .build();
    }

    public static void packECCKey(Key key, KeyPair keyPair) {
        key.toBuilder()
                .addPrimaryKey(ByteString.copyFrom(keyPair.getPrivate().getEncoded()))
                .addPublicKey(ByteString.copyFrom(keyPair.getPublic().getEncoded()))
                .build();
    }

    public static void packAESGCMKey(Key key, SecretKey secretKey, byte[] IV) {
        key.toBuilder().setIv(ByteString.copyFrom(IV)).addPrimaryKey(ByteString.copyFrom(secretKey.getEncoded())).build();
    }

    public static void packChaChaPoly1305Key(Key key, SecretKey secretKey) {
        key.toBuilder().addPrimaryKey(ByteString.copyFrom(secretKey.getEncoded()));
    }

    public static PrivateKey unpackRsaPrivateKey(Key key) throws InvalidKeySpecException, NoSuchAlgorithmException {
        KeyFactory kf = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(key.getPublicKey(0).toByteArray());
        return kf.generatePrivate(pkSpec);
    }

    public static PublicKey unpackRsaPublicKey(Key key) throws InvalidKeySpecException, NoSuchAlgorithmException {
        KeyFactory kf = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(key.getPrimaryKey(0).toByteArray());
        return kf.generatePublic(pkSpec);
    }

    public static SecretKey unpackAESGCMKey(Key key) {
        return new SecretKeySpec(key.getPrimaryKey(0).toByteArray(), 0, key.getPrimaryKey(0).toByteArray().length, "AES");
    }

    public static SecretKey unpackChaChaPoly1305Key(Key key) {
        return new SecretKeySpec(key.getPrimaryKey(0).toByteArray(), 0, key.getPrimaryKey(0).toByteArray().length, "ChaCha20");
    }

    public static PrivateKey unpackECCPrivateKey(Key key) throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyFactory kf = KeyFactory.getInstance("EC");
        X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(key.getPublicKey(0).toByteArray());
        return kf.generatePrivate(pkSpec);
    }

    public static PublicKey unpackECCPublicKey(Key key) throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyFactory kf = KeyFactory.getInstance("EC");
        X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(key.getPrimaryKey(0).toByteArray());
        return kf.generatePublic(pkSpec);
    }

    public boolean encryptData(String in, StringBuilder out, Key key) {
        if (key.hasAsymmetricKeyType()) {
            if (key.getAsymmetricKeyType().getType() == AsymmetricEncryption.Types.SSH_2_RSA) {
                try {
                    out.append(encryptRsa(in.getBytes(), unpackRsaPublicKey(key)));
                    return true;
                } catch (Exception e) {
                    return false;
                }
            }
        } else if (key.hasSymmetricKeyType()) {
            if (key.getSymmetricKeyType().getType() == SymmetricEncryption.Types.AES_GCM) {
                try {
                    out.append(encryptAESGCM(in, key));
                    return true;
                } catch (Exception e) {
                    return false;
                }
            } else if (key.getSymmetricKeyType().getType() == SymmetricEncryption.Types.CHA_CHA_20_POLY_1305) {
                try {
                    out.append(encryptChaChaPoly1305(in, key));
                    return true;
                } catch (Exception e) {
                    return false;
                }
            }
        }
        return false;
    }

    public boolean encryptData(String in, StringBuilder out, Key firstKey, Key secondKey) {

        if (firstKey.hasAsymmetricKeyType() && firstKey.getAsymmetricKeyType().getType() == AsymmetricEncryption.Types.ECC) {
            if (secondKey.hasAsymmetricKeyType() && secondKey.getAsymmetricKeyType().getType() == AsymmetricEncryption.Types.ECC) {
                try {
                    out.append(encryptECC(in.getBytes(), (ECPrivateKey) unpackECCPrivateKey(firstKey),
                            (ECPublicKey) unpackECCPublicKey(secondKey)));
                    return true;
                } catch (Exception e) {
                    return false;
                }
            }
        }
        return false;
    }

    public static String encryptRsa(byte[] data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return new String(cipher.doFinal(data));
    }

    // Source https://zhishenyong.com/ecc-asymmetric-encryption
    public static String encryptECC(byte[] data, ECPrivateKey keyOnePrivateKey, ECPublicKey keyTwoPublicKey)
            throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException,
            InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        // 1. Generate the pre-master shared secret
        KeyAgreement ka = KeyAgreement.getInstance("ECDH", "BC");
        ka.init(keyOnePrivateKey);
        ka.doPhase(keyTwoPublicKey, true);
        byte[] sharedSecret = ka.generateSecret();

        // 2. (Optional) Hash the shared secret.
        //      Alternatively, you don't need to hash it.
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
        messageDigest.update(sharedSecret);
        byte[] digest = messageDigest.digest();

        // 3. (Optional) Split up hashed shared secret into an initialization vector and a session key
        //      Alternatively, you can just use the shared secret as the session key and not use an iv.
        int digestLength = digest.length;
        byte[] iv = Arrays.copyOfRange(digest, 0, (digestLength + 1) / 2);
        byte[] sessionKey = Arrays.copyOfRange(digest, (digestLength + 1) / 2, digestLength);

        // 4. Create a secret key from the session key and initialize a cipher with the secret key
        SecretKey secretKey = new SecretKeySpec(sessionKey, 0, sessionKey.length, "AES");
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);

        // 5. Encrypt whatever message you want to send
        return new String(cipher.doFinal(data));
    }

    public String encryptAESGCM(String in, Key key) throws NoSuchPaddingException, NoSuchAlgorithmException,
            InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        // Get Cipher Instance
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

        // Create GCMParameterSpec
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, key.getIv().toByteArray());

        // Initialize Cipher for ENCRYPT_MODE
        cipher.init(Cipher.ENCRYPT_MODE, unpackAESGCMKey(key), gcmParameterSpec);

        // Perform Encryption
        byte[] cipherText = cipher.doFinal(in.getBytes());

        return new String(cipherText);
    }

    public String encryptChaChaPoly1305(String in, Key key) throws NoSuchPaddingException, NoSuchAlgorithmException,
            BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException {
        byte[] nonceBytes = new byte[12];

        // Get Cipher Instance
        Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");

        // Create IvParamterSpec
        AlgorithmParameterSpec ivParameterSpec = new IvParameterSpec(nonceBytes);

        // Initialize Cipher for ENCRYPT_MODE
        cipher.init(Cipher.ENCRYPT_MODE, unpackChaChaPoly1305Key(key), ivParameterSpec);

        // Perform Encryption
        byte[] cipherText = cipher.doFinal(in.getBytes());

        return new String(cipherText);
    }

    public boolean decryptData(String in, StringBuilder out, Key key) {
        if (key.hasAsymmetricKeyType()) {
            if (key.getAsymmetricKeyType().getType() == AsymmetricEncryption.Types.SSH_2_RSA) {
                try {
                    out.append(decryptRsa(in, unpackRsaPrivateKey(key)));
                    return true;
                } catch (Exception e) {
                    return false;
                }
            }
        } else if (key.hasSymmetricKeyType()) {
            if (key.getSymmetricKeyType().getType() == SymmetricEncryption.Types.AES_GCM) {
                try {
                    out.append(decryptAESGCM(in, key));
                    return true;
                } catch (Exception e) {
                    return false;
                }
            } else if (key.getSymmetricKeyType().getType() == SymmetricEncryption.Types.CHA_CHA_20_POLY_1305) {
                try {
                    out.append(decryptChaChaPoly1305(in, key));
                    return true;
                } catch (Exception e) {
                    return false;
                }
            }
        }
        return false;
    }

    public boolean decryptData(String in, StringBuilder out, Key firstKey, Key secondKey) {
        if (firstKey.hasAsymmetricKeyType() && firstKey.getAsymmetricKeyType().getType() == AsymmetricEncryption.Types.ECC) {
            if (secondKey.hasAsymmetricKeyType() && secondKey.getAsymmetricKeyType().getType() == AsymmetricEncryption.Types.ECC) {
                try {
                    out.append(decryptECC(in.getBytes(), (ECPrivateKey) unpackECCPrivateKey(firstKey),
                            (ECPublicKey) unpackECCPublicKey(secondKey)));
                    return true;
                } catch (Exception e) {
                    return false;
                }
            }
        }
        return false;
    }

    public static String decryptRsa(String encrypted, PrivateKey privateKey) throws InvalidCipherTextException,
            NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException,
            IllegalBlockSizeException {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return new String(cipher.doFinal(encrypted.getBytes()));
    }

    // Source https://zhishenyong.com/ecc-asymmetric-encryption
    public static String decryptECC(byte[] data, ECPrivateKey keyOnePrivateKey, ECPublicKey keyTwoPublicKey)
            throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException,
            InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        // 1. Generate the pre-master shared secret
        KeyAgreement ka = KeyAgreement.getInstance("ECDH", "BC");
        ka.init(keyOnePrivateKey);
        ka.doPhase(keyTwoPublicKey, true);
        byte[] sharedSecret = ka.generateSecret();

        // 2. (Optional) Hash the shared secret.
        //      Alternatively, you don't need to hash it.
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
        messageDigest.update(sharedSecret);
        byte[] digest = messageDigest.digest();

        // 3. (Optional) Split up hashed shared secret into an initialization vector and a session key
        //      Alternatively, you can just use the shared secret as the session key and not use an iv.
        int digestLength = digest.length;
        byte[] iv = Arrays.copyOfRange(digest, 0, (digestLength + 1) / 2);
        byte[] sessionKey = Arrays.copyOfRange(digest, (digestLength + 1) / 2, digestLength);

        // 4. Create a secret key from the session key and initialize a cipher with the secret key
        SecretKey secretKey = new SecretKeySpec(sessionKey, 0, sessionKey.length, "AES");
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);

        // 5. Encrypt whatever message you want to send
        return new String(cipher.doFinal(data));
    }

    public static String decryptAESGCM(String in, Key key) throws NoSuchPaddingException, NoSuchAlgorithmException,
            InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        // Get Cipher Instance
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

        // Create GCMParameterSpec
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, key.getIv().toByteArray());

        // Initialize Cipher for DECRYPT_MODE
        cipher.init(Cipher.DECRYPT_MODE, unpackAESGCMKey(key), gcmParameterSpec);

        // Perform Decryption
        byte[] decryptedText = cipher.doFinal(in.getBytes());

        return new String(decryptedText);
    }

    public static String decryptChaChaPoly1305(String in, Key key) throws NoSuchPaddingException,
            NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException,
            IllegalBlockSizeException {
        byte[] nonceBytes = new byte[12];

        // Get Cipher Instance
        Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");

        // Create IvParamterSpec
        AlgorithmParameterSpec ivParameterSpec = new IvParameterSpec(nonceBytes);

        // Initialize Cipher for DECRYPT_MODE
        cipher.init(Cipher.DECRYPT_MODE, unpackChaChaPoly1305Key(key), ivParameterSpec);

        // Perform Decryption
        byte[] decryptedText = cipher.doFinal(in.getBytes());

        return new String(decryptedText);
    }

    public static boolean signData(String in, StringBuilder out, Key key) {
        if (key.hasAsymmetricKeyType()) {

            if (key.getAsymmetricKeyType().getType() == AsymmetricEncryption.Types.ECC) {

            } else if (key.getAsymmetricKeyType().getType() == AsymmetricEncryption.Types.SSH_2_RSA) {
                try {
                    out.append(signRsa(in, unpackRsaPrivateKey(key)));
                } catch (Exception e) {
                    return false;
                }
            }
        }
        return false;
    }

    public static String signRsa(String in, PrivateKey privateKey)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature sig = Signature.getInstance("SHA1WithRSA");
        sig.initSign(privateKey);
        sig.update(in.getBytes());
        return new String(sig.sign());
    }

    public static String signECC(String in, PrivateKey privateKey)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature sig = Signature.getInstance("SHA256withECDSA");
        sig.initSign(privateKey);
        sig.update(in.getBytes());
        return new String(sig.sign());
    }


    public static boolean verifyData(String signature, String message, Key key) {
        if (key.hasAsymmetricKeyType()) {
            if (key.getAsymmetricKeyType().getType() == AsymmetricEncryption.Types.ECC) {
                try {
                    verifyECC(signature, message, unpackECCPublicKey(key));
                } catch (Exception e) {
                    return false;
                }
            } else if (key.getAsymmetricKeyType().getType() == AsymmetricEncryption.Types.SSH_2_RSA) {
                try {
                    verifyRsa(signature, message, unpackRsaPublicKey(key));
                } catch (Exception e) {
                    return false;
                }
            }
        }
        return false;
    }

    public static boolean verifyRsa(String signature, String message, PublicKey publicKey)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature sig = Signature.getInstance("SHA1WithRSA");
        sig.initVerify(publicKey);
        sig.update(message.getBytes());
        return sig.verify(signature.getBytes());
    }

    public static boolean verifyECC(String signature, String message, PublicKey publicKey)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature sig = Signature.getInstance("SHA256withECDSA");
        sig.initVerify(publicKey);
        sig.update(message.getBytes());
        return sig.verify(signature.getBytes());
    }
}


Here are the protos reference.

Key

message Key {

  // Private key or the sole key.
  // For ChaCha-Poly1305 the first key is the ChaCha the second is the Poly.
  repeated bytes primary_key = 1;

  // Public key (if this is asymmetric). Will almost always be one (no cases with multiple rn).
  repeated bytes public_key = 2;

  // What kind of key this is.
  oneof key_type {
    AsymmetricEncryption asymmetric_key_type = 3;
    SymmetricEncryption symmetric_key_type = 4;
  }

  // The initialization vector if one is needed along with the key.
  bytes iv = 5;

  // An authentication tag if one is needed along with the key.
  bytes auth_tag = 6;

  // An authentication vector if one is needed along with the key.
  bytes auth_vector = 7;

  // Arbitrary additional data.
  bytes arbitrary_additional_data = 8;
}

Asymmetric Encryption

message AsymmetricEncryption {
    // All the types of asymmetric encryption supported. 
    enum Types {
        // The primary type within Keiros. Will be used for encrypting connections.
        ECC = 0;
        // A supported type that can be used only for signing.
        ED25519 = 1;
        // Useful for dealing with the keys that are generated by AWS. 
        SSH_2_RSA = 2;
    }
    Types type = 1;


    // If there is a number of bits to go along with an encryption type. For example,
    // SSH_2_RSA can be with 1024, 2048 etc... 
    // See https://security.stackexchange.com/questions/62247/should-i-use-more-than-2048-bits-in-my-ssh-2-rsa-key. 
    int64 bits_for_encryption_method = 2;
}

Symmetric Encryption

message SymmetricEncryption {
    // All the types of symmetric encryption supported. 
    enum Types {
        // The primary type within Keiros. Useful for storage and for maintaining an encrypted
        // connection. 
        AES_GCM = 0;
        // A secondary type to allow stream decrypting -- just additional felxibility.
        CHA_CHA_20_POLY_1305 = 1;
    }
    Types type = 1;


    // If there is a number of bits to go along with an encryption type. For example,
    // AES_GCM can use a "tag" of size 128, 120, 112, 104, or 96.
    // See: https://en.wikipedia.org/wiki/Galois/Counter_Mode.
    int64 bits_for_encryption_method = 2;
}

So what is confusing me here is that I have the same code in C++ but wolfcrypt encodes the keys specifically as DER for RSA and X.963 format for ECC. Then with AESGCM and ChaChaPoly1305 they are both raw byte arrays while here they are not. I am just wondering if I encode in java and then decode in c++ is there any way to get that working. I imagine there must be because TLS works in java and c++.


Solution

  • The description 'DER' is ambiguous, but in the contrast you quoted it probably means that RSA keys are in the ASN.1 format(s?) defined by PKCS1 aka RFC8017 et pred Appendix A.1 which like all ASN.1 data can be and for crypto often is encoded in DER. 'X.963' is clearly a mistake; the relevant standards for ECC crypto are X9.62 and X9.63, but the publickey format which indeed is not ASN.1/DER was defined by X9.62 and copied by X9.63, while the privatekey format was not standardized at all, but is often a raw octet string and not ASN.1/DER. (X.(number) standards are from the international treaty organization formerly called International Consultative Committee on Telephony and Telegraphy CCITT and now International Telecommunication Union Telecommunication Standardization Sector ITU-T; X9.(number) are from the USA private-sector financial-industry organization ANSI accredited standards committee (ASC) X9.) OTOH Java crypto encoding for private key is PKCS8 aka RFC5208 (unencrypted) which adds metadata. To convert a Java key to the format your 'wolfcrypt' apparently wants is fairly easy, by just extracting the algorithm-dependent element, while the reverse, using a 'wolfcrypt' key in Java, would be quite difficult with just Java, but adding BouncyCastle helps a lot; there are numerous existing Qs about both (or all three) of these, which I will dig up for you later.

    Some other, partial, comments:

    generateChaChaPoly1305Key, generateAESGCMKey: KeyGenerator.init(int) takes the number of bits, which should be 256 for ChaCha20 and 128, 192 or 256 for AES. (So does KeyPairGenerator and you got that one right for RSA.) The IV should NOT be generated with the key for AES-GCM; see more below.

    unpackRsaPrivateKey, unpackRsaPublicKey, unpackECCPrivateKey, unpackECCPublicKey: the 'PrivateKey' methods use key.getPublicKey() and the 'PublicKey' methods use key.getPrivateKey() which appears to be backwards. Assuming you change the PrivateKey methods to use the privatekey, as above the Java encoding of a privatekey is PKCS8EncodedKeySpec -- not X509EncodedKeySpec which is only for publickey.

    unpackAESGCMKey, unpackChaChaPoly1305Key: to use a whole byte[] as the key, you don't need to specify , 0, array.length, you can use the simpler ctor SecretKeySpec(byte[], String algorithm).

    encrypt*: you do new String(byte[]) on the ciphertext, and (String_var).getBytes() in the decrypt* methods. THIS IS NOT SAFE. Java String is designed to hold characters, and although crypto still uses the traditional terms plaintext and ciphertext, since about 1950 the plaintext is not required to be and the ciphertext is NEVER actually characters, but rather arbitrary bit patterns. Trying to put these bits in a Java String and then get them back will usually fail, especially if done across multiple systems (e.g. encrypt on A, transmit, and decrypt on B, which is a common use case). The best thing is to handle them consistently as byte[]; if you must run them through something that can't handle arbitrary bits, like SMS or the Web (HTML), you need to encode in a text form that preserves all bits; the common methods for this are hexadecimal (or hex) and base64.

    encryptAESGCM: it's not clear what key.getIV() does/uses, but if this repeats an IV value for multiple encryptions using the same key, that is CATASTROPHIC; even one reuse destroys all authenticity (attacker can forge any data), and depending on your data anywhere from one to a small number of reuses (like two or ten) destroys confidentiality (attacker can expose supposedly secret data).

    encryptChaChaPoly1305: this clearly uses the same nonce, all-zeros, for every encryption; if you reuse the key at all, which seems to be the point of your design, this destroys all security in the same way as for AES-GCM.

    At this point I gave up. You should throw out this design and replace it with one from someone who knows about cryptography.