diff options
author | Arne Schwabe <arne@rfc2549.org> | 2021-09-27 14:56:56 +0200 |
---|---|---|
committer | Arne Schwabe <arne@rfc2549.org> | 2021-10-01 19:56:39 +0200 |
commit | 9e704d04dc7f2f93bddf85d371772340fa5af0b1 (patch) | |
tree | 0e6dc66cfbd792aee7d745949dd7c74615669195 /main | |
parent | ea408251e6a50a3f655cdd6fbe01a11cb7209c11 (diff) |
Implement new signing API as required by the OpenSSL 3.0 provider
Diffstat (limited to 'main')
4 files changed, 154 insertions, 72 deletions
diff --git a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java index 4f66edae..5c2edd6b 100644 --- a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java +++ b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java @@ -16,12 +16,15 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.security.KeyChain; import android.security.KeyChainException; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import android.text.TextUtils; import android.util.Base64; import de.blinkt.openvpn.core.*; + import org.spongycastle.util.io.pem.PemObject; import org.spongycastle.util.io.pem.PemWriter; @@ -38,6 +41,8 @@ import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; import java.util.Collection; import java.util.HashSet; import java.util.Locale; @@ -475,7 +480,8 @@ public class VpnProfile implements Serializable, Cloneable { if (!TextUtils.isEmpty(ks[1])) cfg.append("<extra-certs>\n").append(ks[1]).append("\n</extra-certs>\n"); cfg.append("<cert>\n").append(ks[2]).append("\n</cert>\n"); - cfg.append("management-external-key nopadding\n"); + cfg.append("management-external-key nopadding pkcs1\n"); + // for xkey branch:" management-external-key pss digest } else { cfg.append(context.getString(R.string.keychain_access)).append("\n"); if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) @@ -1161,18 +1167,18 @@ public class VpnProfile implements Serializable, Cloneable { } @Nullable - public String getSignedData(Context c, String b64data, boolean pkcs1padding) { + public String getSignedData(Context c, String b64data, OpenVPNManagement.SignaturePadding padding, String saltlen, String hashalg, boolean needDigest) { byte[] data = Base64.decode(b64data, Base64.DEFAULT); byte[] signed_bytes; if (mAuthenticationType == TYPE_EXTERNAL_APP) { - RsaPaddingType paddingType = pkcs1padding ? RsaPaddingType.PKCS1_PADDING : RsaPaddingType.NO_PADDING; + /* TODO: FIXME */ + RsaPaddingType paddingType = (padding == OpenVPNManagement.SignaturePadding.RSA_PKCS1_PADDING) ? RsaPaddingType.PKCS1_PADDING : RsaPaddingType.NO_PADDING; Bundle extra = new Bundle(); extra.putInt(EXTRA_RSA_PADDING_TYPE, paddingType.ordinal()); signed_bytes = getExtAppSignedData(c, data, extra); - } - else { - signed_bytes = getKeyChainSignedData(data, pkcs1padding); + } else { + signed_bytes = getKeyChainSignedData(data, padding, saltlen, hashalg, needDigest); } if (signed_bytes != null) @@ -1192,13 +1198,13 @@ public class VpnProfile implements Serializable, Cloneable { } } - private byte[] getKeyChainSignedData(byte[] data, boolean pkcs1padding) { - + private byte[] getKeyChainSignedData(byte[] data, OpenVPNManagement.SignaturePadding padding, String saltlen, String hashalg, boolean needDigest) { PrivateKey privkey = getKeystoreKey(); // The Jelly Bean *evil* Hack // 4.2 implements the RSA/ECB/PKCS1PADDING in the OpenSSLprovider if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) { - return processSignJellyBeans(privkey, data, pkcs1padding); + /* TODO: really fix this?! */ + return processSignJellyBeans(privkey, data, padding); } @@ -1207,86 +1213,130 @@ public class VpnProfile implements Serializable, Cloneable { String keyalgorithm = privkey.getAlgorithm(); byte[] signed_bytes; - if (keyalgorithm.equals("EC")) { - Signature signer = Signature.getInstance("NONEwithECDSA"); - - signer.initSign(privkey); - signer.update(data); - signed_bytes = signer.sign(); - + if (needDigest || keyalgorithm.equals("EC")) { + return doDigestSign(privkey, data, padding, hashalg, saltlen); } else { - /* ECB is perfectly fine in this special case, since we are using it for + /* ECB is perfectly fine in this special case, since we are using it for the public/private part in the TLS exchange */ Cipher signer; - if (pkcs1padding) + if (padding == OpenVPNManagement.SignaturePadding.RSA_PKCS1_PADDING) signer = Cipher.getInstance("RSA/ECB/PKCS1PADDING"); - else + else if (padding == OpenVPNManagement.SignaturePadding.NO_PADDING) signer = Cipher.getInstance("RSA/ECB/NoPadding"); + else + throw new NoSuchPaddingException("Unknown padding used for signature"); signer.init(Cipher.ENCRYPT_MODE, privkey); signed_bytes = signer.doFinal(data); + + return signed_bytes; + } + } catch + (NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchPaddingException | SignatureException | InvalidAlgorithmParameterException + e){ + VpnStatus.logError(R.string.error_rsa_sign, e.getClass().toString(), e.getLocalizedMessage()); + return null; } - return signed_bytes; - } catch (NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException - | BadPaddingException | NoSuchPaddingException | SignatureException e) { - VpnStatus.logError(R.string.error_rsa_sign, e.getClass().toString(), e.getLocalizedMessage()); - return null; } + + private byte[] doDigestSign(PrivateKey privkey, byte[] data, OpenVPNManagement.SignaturePadding padding, String hashalg, String saltlen) throws SignatureException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException { + /* RSA */ + Signature sig = null; + + if (privkey.getAlgorithm().equals("EC")) { + if (hashalg.equals("")) + hashalg = "NONE"; + /* e.g. SHA512withECDSA */ + hashalg = hashalg + "withECDSA"; + sig = Signature.getInstance(hashalg.toUpperCase(Locale.ROOT)); + } else if (padding == OpenVPNManagement.SignaturePadding.RSA_PKCS1_PSS_PADDING) { + /* https://developer.android.com/training/articles/keystore#SupportedSignatures */ + if (!"digest".equals(saltlen)) + throw new SignatureException("PSS signing requires saltlen=digest"); + + sig = Signature.getInstance(hashalg + "withRSA/PSS"); + + PSSParameterSpec pssspec = null; + switch (hashalg) { + case "SHA256": + pssspec = new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1); + break; + case "SHA512": + pssspec = new PSSParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 64, 1); + break; + case "SHA384": + pssspec = new PSSParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, 48, 1); + break; + } + sig.setParameter(pssspec); + } else if (padding == OpenVPNManagement.SignaturePadding.RSA_PKCS1_PADDING) { + sig = Signature.getInstance(hashalg + "withRSA"); + } + + sig.initSign(privkey); + sig.update(data); + return sig.sign(); } - private byte[] processSignJellyBeans(PrivateKey privkey, byte[] data, boolean pkcs1padding) { - try { - Method getKey = privkey.getClass().getSuperclass().getDeclaredMethod("getOpenSSLKey"); - getKey.setAccessible(true); + private byte[] processSignJellyBeans (PrivateKey privkey, byte[] data, OpenVPNManagement.SignaturePadding padding){ + try { + boolean pkcs1padding=false; + if (padding == OpenVPNManagement.SignaturePadding.RSA_PKCS1_PADDING) + pkcs1padding = true; + else if (padding != OpenVPNManagement.SignaturePadding.NO_PADDING) + throw new IllegalAccessException("Unsuppoirted padding for jelly bean native signing"); - // Real object type is OpenSSLKey - Object opensslkey = getKey.invoke(privkey); + Method getKey = privkey.getClass().getSuperclass().getDeclaredMethod("getOpenSSLKey"); + getKey.setAccessible(true); - getKey.setAccessible(false); + // Real object type is OpenSSLKey + Object opensslkey = getKey.invoke(privkey); - Method getPkeyContext = opensslkey.getClass().getDeclaredMethod("getPkeyContext"); + getKey.setAccessible(false); - // integer pointer to EVP_pkey - getPkeyContext.setAccessible(true); - int pkey = (Integer) getPkeyContext.invoke(opensslkey); - getPkeyContext.setAccessible(false); + Method getPkeyContext = opensslkey.getClass().getDeclaredMethod("getPkeyContext"); - // 112 with TLS 1.2 (172 back with 4.3), 36 with TLS 1.0 - return NativeUtils.rsasign(data, pkey, pkcs1padding); + // integer pointer to EVP_pkey + getPkeyContext.setAccessible(true); + int pkey = (Integer) getPkeyContext.invoke(opensslkey); + getPkeyContext.setAccessible(false); - } catch (NoSuchMethodException | InvalidKeyException | InvocationTargetException | IllegalAccessException | IllegalArgumentException e) { - VpnStatus.logError(R.string.error_rsa_sign, e.getClass().toString(), e.getLocalizedMessage()); - return null; + // 112 with TLS 1.2 (172 back with 4.3), 36 with TLS 1.0 + return NativeUtils.rsasign(data, pkey, pkcs1padding); + + } catch (NoSuchMethodException | InvalidKeyException | InvocationTargetException | IllegalAccessException | IllegalArgumentException e) { + VpnStatus.logError(R.string.error_rsa_sign, e.getClass().toString(), e.getLocalizedMessage()); + return null; + } } - } - private boolean usesExtraProxyOptions() { - if (mUseCustomConfig && mCustomConfigOptions != null && mCustomConfigOptions.contains("http-proxy-option ")) - return true; - for (Connection c : mConnections) - if (c.usesExtraProxyOptions()) + private boolean usesExtraProxyOptions () { + if (mUseCustomConfig && mCustomConfigOptions != null && mCustomConfigOptions.contains("http-proxy-option ")) return true; + for (Connection c : mConnections) + if (c.usesExtraProxyOptions()) + return true; - return false; - } + return false; + } - static class NoCertReturnedException extends Exception { - public NoCertReturnedException(String msg) { - super(msg); + /** + * The order of elements is important! + */ + private enum RsaPaddingType { + NO_PADDING, + PKCS1_PADDING } - } - /** - * The order of elements is important! - */ - private enum RsaPaddingType { - NO_PADDING, - PKCS1_PADDING + static class NoCertReturnedException extends Exception { + public NoCertReturnedException(String msg) { + super(msg); + } + } } -} diff --git a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java index 46631e9e..6ec4cbf3 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java +++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java @@ -16,6 +16,12 @@ public interface OpenVPNManagement { screenOff, } + enum SignaturePadding { + RSA_PKCS1_PSS_PADDING, + RSA_PKCS1_PADDING, + NO_PADDING + } + int mBytecountInterval = 2; void reconnect(); diff --git a/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java index 0f15eec2..26d4893f 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java +++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java @@ -769,8 +769,29 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { String[] arguments = argument.split(",");
- boolean pkcs1padding = arguments[1].equals("RSA_PKCS1_PADDING");
- String signed_string = mProfile.getSignedData(mOpenVPNService, arguments[0], pkcs1padding);
+ // NC9t8IkYrjAQcCzc85zN0H5TvwfAUDwYkR4j2ga6fGw=,RSA_PKCS1_PSS_PADDING,hashalg=SHA256,saltlen=digest
+
+
+ SignaturePadding padding = SignaturePadding.NO_PADDING;
+ String saltlen="";
+ String hashalg="";
+ boolean needsDigest = false;
+
+ for (int i=1;i < arguments.length;i++) {
+ String arg = arguments[i];
+ if(arg.equals("RSA_PKCS1_PADDING"))
+ padding = SignaturePadding.RSA_PKCS1_PADDING;
+ else if (arg.equals("RSA_PKCS1_PSS_PADDING"))
+ padding = SignaturePadding.RSA_PKCS1_PSS_PADDING;
+ else if (arg.startsWith("saltlen="))
+ saltlen= arg.substring(8);
+ else if (arg.startsWith("hashalg="))
+ hashalg = arg.substring(8);
+ else if (arg.equals("data=message"))
+ needsDigest = true;
+ }
+
+ String signed_string = mProfile.getSignedData(mOpenVPNService, arguments[0], padding, saltlen, hashalg, needsDigest);
if (signed_string == null) {
managmentCommand("pk-sig\n");
diff --git a/main/src/ui/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java b/main/src/ui/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java index 1d81da1a..a6a1054d 100644 --- a/main/src/ui/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java +++ b/main/src/ui/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java @@ -240,16 +240,21 @@ public class OpenVPNThreadv3 extends ClientAPI_OpenVPNClient implements Runnable @Override public void external_pki_sign_request(ClientAPI_ExternalPKISignRequest signreq) { VpnStatus.logDebug("Got external PKI signing request from OpenVPN core for algorithm " + signreq.getAlgorithm()); - boolean pkcs1padding; - if (signreq.getAlgorithm().equals("RSA_PKCS1_PADDING")) - pkcs1padding = true; - else if (signreq.getAlgorithm().equals("RSA_NO_PADDING")) - pkcs1padding = false; - else if (signreq.getAlgorithm().equals("ECDSA")) - pkcs1padding = false; - else - throw new IllegalArgumentException("Illegal padding in sign request" + signreq.getAlgorithm()); - signreq.setSig(mVp.getSignedData(mService, signreq.getData(), pkcs1padding)); + SignaturePadding padding; + switch (signreq.getAlgorithm()) { + case "RSA_PKCS1_PADDING": + padding = SignaturePadding.RSA_PKCS1_PADDING; + break; + case "RSA_NO_PADDING": + padding = SignaturePadding.NO_PADDING; + break; + case "ECDSA": + padding = SignaturePadding.NO_PADDING; + break; + default: + throw new IllegalArgumentException("Illegal padding in sign request" + signreq.getAlgorithm()); + } + signreq.setSig(mVp.getSignedData(mService, signreq.getData(), padding, "", "", false)); } void setUserPW() { |