Проверка цифровой подписи ECDSA на Android P

#android #digital-signature #bouncycastle #ecdsa

#Android #цифровая подпись #bouncycastle #ecdsa

Вопрос:

 import android.os.Bundle;
import android.util.Base64;
import android.widget.Toast;
import org.bouncycastle.jce.provider.BouncyCastleProvider; // implementation 'org.bouncycastle:bcprov-jdk16:1.46'
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    static final String PUBLIC_KEY = "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEMEV3EPREEDc0t4MPeuYgreLMHMVfD7iYJ2Cnkd0ucwf3GYVySvYTttMVMNMEKF554NYmdrOlqwo2s8J2tKt/oQ==";

    static final String DATA = "Hello";

    static final String SIGNATURE = "MEUCIQCsuI4OcBAyA163kiWji1lb7xAtC8S0znf62EpdA U4zQIgBcLbXtcuxXHcwQ9/DmiVfoiigKnefeYgpVXZzjIuYn8=";

    static boolean verifyData() throws Exception {
        PublicKey pk = getPublicKey();
        byte[] signatureBytes = Base64.decode(SIGNATURE, Base64.NO_WRAP);

        Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
        signature.initVerify(pk);
        signature.update(DATA.getBytes("UTF-8"));
        return signature.verify(signatureBytes);
    }

    static PublicKey getPublicKey() throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decode(PUBLIC_KEY, Base64.NO_WRAP));
        PublicKey key = keyFactory.generatePublic(x509EncodedKeySpec);
        return key;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        try {
            Toast.makeText(this, verifyData()   "", Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            Toast.makeText(this, "Failure: "   e.getMessage(), Toast.LENGTH_LONG).show();
            e.printStackTrace();
        }
    }

}
  

Приведенный выше код выполняет проверку цифровой подписи ECDSA. Этот код отлично работает на всех версиях Android ниже Android P. На Android P я получаю это исключение.

 java.security.NoSuchAlgorithmException: The BC provider no longer provides an implementation for Signature.SHA1withRSA. Please see https://android-developers.googleblog.com/2018/03/cryptography-changes-in-android-p.html for more details.
  

Итак, как уже упоминалось здесь, я попытался удалить параметр provider из этих инструкций следующим образом:

 Signature signature = Signature.getInstance("SHA256withECDSA");
KeyFactory keyFactory = KeyFactory.getInstance("EC");
  

Но это не решило проблему? Как же так? Теперь я получаю это исключение:

 java.security.InvalidKeyException: com.android.org.conscrypt.OpenSSLX509CertificateFactory$ParsingException: Error parsing public key
  

Одно из возможных решений — снизить targetSdkVersion до 27, но этого недостаточно. Есть ли лучшее решение проблемы?

Ответ №1:

Я могу воспроизвести проблему на своем компьютере (Android P, уровень API 28). Одним из возможных решений проблемы является удаление предварительно установленной версии перед добавлением поставщика BC:

 Security.removeProvider("BC");
Security.addProvider(new BouncyCastleProvider());
  

или в качестве альтернативы:

 Security.removeProvider("BC");
Security.insertProviderAt(new BouncyCastleProvider(), 1);
  

Кроме того, поставщик BC должен быть указан явно:

 KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
...
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
  

В этом созвездии подпись может быть проверена.

Различное поведение по сравнению с более низкими уровнями API может быть связано с изменениями, касающимися поставщика BC, которые были реализованы в Android P, здесь.