summaryrefslogtreecommitdiff
path: root/app/src/main/java/se/leap/bitmaskclient/base/utils/PrivateKeyHelper.java
blob: 43af5200ee8c1fcd0787486b67c8e1ee90c1fbb0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package se.leap.bitmaskclient.base.utils;

import static android.util.Base64.encodeToString;

import android.util.Log;

import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import org.spongycastle.util.encoders.Base64;

import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;

import de.blinkt.openvpn.core.NativeUtils;

public class PrivateKeyHelper {

    public static final String TAG = PrivateKeyHelper.class.getSimpleName();

    public static final String RSA = "RSA";
    public static final String ED_25519 = "Ed25519";
    public static final String ECDSA = "ECDSA";

    public static final String RSA_KEY_BEGIN = "-----BEGIN RSA PRIVATE KEY-----\n";
    public static final String RSA_KEY_END = "-----END RSA PRIVATE KEY-----";
    public static final String EC_KEY_BEGIN = "-----BEGIN PRIVATE KEY-----\n";
    public static final String EC_KEY_END = "-----END PRIVATE KEY-----";


    public interface PrivateKeyHelperInterface {


        @Nullable PrivateKey parsePrivateKeyFromString(String privateKeyString);
    }

    public static class DefaultPrivateKeyHelper implements PrivateKeyHelperInterface {

        public PrivateKey parsePrivateKeyFromString(String privateKeyString) {
            if (privateKeyString == null || privateKeyString.isBlank()) {
                return null;
            }
            if (privateKeyString.contains(RSA_KEY_BEGIN)) {
                return parseRsaKeyFromString(privateKeyString);
            } else if (privateKeyString.contains(EC_KEY_BEGIN)) {
               return parseECPrivateKey(privateKeyString);
            } else {
                return null;
            }
        }

        private RSAPrivateKey parseRsaKeyFromString(String rsaKeyString) {
            RSAPrivateKey key;
            try {
                KeyFactory kf;
                kf = KeyFactory.getInstance(RSA, "BC");
                rsaKeyString = rsaKeyString.replaceFirst(RSA_KEY_BEGIN, "").replaceFirst(RSA_KEY_END, "");

                PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(rsaKeyString));
                key = (RSAPrivateKey) kf.generatePrivate(keySpec);
            } catch (InvalidKeySpecException | NoSuchAlgorithmException | NullPointerException |
                     NoSuchProviderException e) {
                e.printStackTrace();
                return null;
            }

            return key;
        }

        private PrivateKey parseECPrivateKey(String ecKeyString) {
            String base64 = ecKeyString.replace(EC_KEY_BEGIN, "").replace(EC_KEY_END, "");
            byte[] keyBytes = Base64.decode(base64);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            String errMsg;
            try {
                KeyFactory keyFactory = KeyFactory.getInstance(ED_25519, "BC");
                return keyFactory.generatePrivate(keySpec);
            } catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchProviderException e) {
                errMsg = e.toString();
            }

            try {
                KeyFactory keyFactory = KeyFactory.getInstance(ECDSA, "BC");
                return keyFactory.generatePrivate(keySpec);
            } catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchProviderException e) {
                errMsg += "\n" + e.toString();
                Log.e(TAG, errMsg);
            }
            return null;
        }
    }

    public static String getPEMFormattedPrivateKey(PrivateKey key) throws NullPointerException {
        if (key == null) {
            throw new NullPointerException("Private key was null.");
        }
        String keyString = encodeToString(key.getEncoded(), android.util.Base64.DEFAULT);

        if (key instanceof RSAPrivateKey) {
            return (RSA_KEY_BEGIN + keyString + RSA_KEY_END);
        } else {
            return EC_KEY_BEGIN + keyString + EC_KEY_END;
        }
    }

    private static PrivateKeyHelperInterface instance = new DefaultPrivateKeyHelper();

    @VisibleForTesting
    public PrivateKeyHelper(PrivateKeyHelperInterface helperInterface) {
        if (!NativeUtils.isUnitTest()) {
            throw new IllegalStateException("PrivateKeyHelper injected with PrivateKeyHelperInterface outside of an unit test");
        }
        instance = helperInterface;
    }

    public static @Nullable PrivateKey parsePrivateKeyFromString(String rsaKeyString) {
       return instance.parsePrivateKeyFromString(rsaKeyString);
    }
}