Ошибка декодирования ECDSA: исключение в потоке «main» java.security.SignatureException: ошибка декодирования байтов подписи

Я пытаюсь проверить подпись ECDSA, используя java, ключ был создан с помощью golang:

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"reflect"
)

func doit(){
privateKey, _ := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
publicKey := &privateKey.PublicKey

if !elliptic.P384().IsOnCurve(publicKey.X, publicKey.Y) {
    fmt.Printf(" public key invalid. ")
}

encPriv, encPub := encode(privateKey, publicKey)

fmt.Println(encPriv)
fmt.Println(encPub)
}

подпись происходит здесь: (сообщение было закодировано golang, используя этот метод):

func SignMessage(message []byte) (r *big.Int, s *big.Int, err error) {
zero := big.NewInt(0)
// Hash message:
h := sha1.New()
io.WriteString(h, string(message))
hashBytes := h.Sum(nil)
hash := fmt.Sprintf("%x", hashBytes)

// hash message
// get private key from disk:
pemEncoded, err := ioutil.ReadFile("./ecc/eccpriv.pem")
if err != nil {
    return zero, zero, err
}
pemEncodedPub, err := ioutil.ReadFile("./ecc/eccpub.pem")
if err != nil {
    return zero, zero, err
}

var priv *ecdsa.PrivateKey
//var _pub *ecdsa.PublicKey
priv, _, err = ECCDecodeFromPem(pemEncoded, pemEncodedPub)
if err != nil {
    return zero, zero, err
}

r, s, err = ecdsa.Sign(rand.Reader, priv, []byte(hash))
if err != nil {
    return zero, zero, err
}

return r, s, nil

}

здесь происходит декодирование:

        //Verify Response
        String signature = ac.getECCDSAPublicKeyFromServer();
        String cleanSignature = ac.cleanBytes(signature);
        byte[] bSignature = Base64.getDecoder().decode(cleanSignature);

        System.out.println(cleanSignature);
        PublicKey ecdsaPublicKey = ac.getPemPublicKeyFromString(signature,"ECDSA");

        //PublicKey ecdsaPublicKey = ac.getECDSAKeyFromBytes(cleanSignature.getBytes("UTF-8"));

        Signature ecdsaVerify = Signature.getInstance("ECDSA", "BC");
        ecdsaVerify.initVerify(ecdsaPublicKey);
        ecdsaVerify.update(json_response.getBytes("UTF-8"));
        System.out.println("SIG:");
        for(int i=0;i<bSignature.length;i++){
            System.out.println(bSignature[i]);
        }
        System.out.println(new String(bSignature, StandardCharsets.UTF_8));
        System.out.println("/SIG");

        boolean result = ecdsaVerify.verify(bSignature);
        System.out.println("Result is:"+result);

однако, к сожалению, программа не работает по следующим причинам:

 Exception in thread "main" java.security.SignatureException: error decoding signature bytes.
 at org.bouncycastle.jcajce.provider.asymmetric.util.DSABase.engineVerify(Unknown Source)
 at java.base/java.security.Signature$Delegate.engineVerify(Signature.java:1245)
 at java.base/java.security.Signature.verify(Signature.java:674)
 at ...

Это вызывает интересную дилемму, поскольку Открытый ключ:

-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEBiTlkxxYVLduJeiQ7V1AqG4bY9lxrxLX
un+qd4BeaICC1Yx/nsDvvXEPwfCYwXgnyk3u7DV3ldUiaXCIr89OoNei6D2Xgrs3
KYtpVEv7ylnUzo8xZH3/mMdLMUiy6fl8
-----END PUBLIC KEY-----

Судя по этому сайту, кажется правильным:

https://lapo.it/asn1js/#3076301006072A8648CE3D020106052B81040022036200040624E5931C5854B76E25E890ED5D40A86E1B63D971AF12D7BA7FAA77805E688082D58C7F9EC0EFBD710FC1F098C17827CA4DEEEC357795D522697088AFCF4EA0D7A2E83D9782BB37298B69544BFBCA59D4CE8F31647DFF98C74B3148B2E9F97C

Ключ был сгенерирован правильно, и ASN.1 Parse правильно его декодирует. Почему Java не нравится мой код?

Кроме того, прошу прощения за плохой отступ.

Bouncy Castle не жалуется на ваш открытый ключ, он жалуется на формат подписи, которую вы нам не показали. Пожалуйста, укажите байты подписи в шестнадцатеричном формате.

Maarten Bodewes 04.08.2018 22:06

Также очень неудобно печатать по одному байту в строке; рассмотрим Arrays.toString(byte[]) или шестнадцатеричный javax.xml.bind.DatatypeConverter.printHexBinary(byte[]). И никогда (никогда) не пытайтесь преобразовать произвольные двоичные файлы, такие как криптографические подписи и другие криптообъекты, в Java String напрямую; это отличный способ уничтожить ваши данные. И, наконец, использование ECDSA (P384) с SHA1 фактически отбрасывает большую часть вашей безопасности, но оставьте это для другого стека после того, как ваш код заработает.

dave_thompson_085 04.08.2018 23:12

Java JCE определенно не ожидает подписи в формате JSON ... поэтому json_response.getBytes("UTF-8") заставляет меня нервничать в вашем вызове обновления.

lockcmpxchg8b 05.08.2018 00:53

@ lockcmpxchg8b + Формат JWS для подписи ECDSA - (base64url of) big-endian unsigned r, s фиксированной длины без метаданных, таких как теги, который является форматом, используемым PKCS11 и IINM P1363, и поддерживается Bouncy (но не SunEC) хотя по умолчанию не используется. Но JWA требует, чтобы хэш соответствовал кривой, поэтому ECDSA (P-384) требует SHA-384.

dave_thompson_085 06.08.2018 02:27

Я голосую за то, чтобы закрыть этот вопрос как не по теме, потому что без подписи на этот вопрос нельзя ответить.

Maarten Bodewes 08.08.2018 02:15

Ух ты, столько ответов ты предоставил! Мне плохо сейчас приходится голосовать за закрытие. Измените вопрос и укажите необходимую информацию!

Maarten Bodewes 08.08.2018 02:23
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
6
342
1

Ответы 1

Я думаю, ваша проблема здесь:

hash := fmt.Sprintf("%x", hashBytes)

И вы должны передать hashBytes прямо в

r, s, err = ecdsa.Sign(rand.Reader, priv, hashBytes)

Другие вопросы по теме