bcgit / pc-dart

Pointy Castle - Dart Derived Bouncy Castle APIs
MIT License
241 stars 125 forks source link

RSA Verification failed with SHA-384/RSA #210

Open SebastianKlaiber opened 1 year ago

SebastianKlaiber commented 1 year ago

Hello, We are currently porting a native Android app to flutter and need RSA verification for it. Unfortunately, the verification with pointycastle does not work when we use our test cases. The signature is created on a spring boot backend based on java. Can you help us further?

pointycastle version 3.7.3

Android Implementation

    private fun verifySignatue(signaturePublicKey: String, signaturePayload: String, base64CipheredPayload: String): Boolean {
        val signature: Signature = Signature.getInstance("SHA384withRSA")
        val publicKey = KeyFactory.getInstance("RSA")
            .generatePublic(X509EncodedKeySpec(decodeBase64(signaturePublicKey)))
        signature.initVerify(publicKey)
        signature.update(base64CipheredPayload.toByteArray(StandardCharsets.UTF_8))
        return if (signature.verify(decodeBase64(signaturePayload))) {
            true
        } else {
            throw BarcodeException("signature check failed")
        }
    }

Flutter

const publicKey =   'MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAtDTEndUdEOh7SQUioTdF5oJWJBFl/x+S5K8lICMABWoHt7ityLM16GHW+veR16frgJNVPy9V9QGzvGoTQNro0T3f/mMn3Rj1EvlT0Of7QLmniSowXQYtjFhTxPpuSlGG4X4vtFrEQWyq+sQLIwy9Q6O7wBvVpK9NJ+X4s2X909KW52XMCy2q3nlT7xJRK0m9b9OH2YWORztEBBX2rx8CD5xJnx3VC8Mcc4wnlruHl21zRyjvUF7Fk7z1c6pb71juCxt2QmaM2ulKQI2UiZN/vHFIeg3V6SKkOH+qhwQbDuiFO6pAQTCsbuDYKGxltSk8qaqU8LfijVFczAZLPbb02cktmhyUlqbHoeRcl4/nSutvb+n6ite259mcSeoz+FHRXqolCiviQAAnpfF/sPfNKpHH7kaXrbjQA5Ak47YjCcqax3sfduh1CYDrm3mlEmgtdzEIpO9rWvQmpAwuBKleq0fiC/PxPEPRjDprbi84GyY/8nh9BbLn+ffCkBoZGJexAgMBAAE=';

const signature = 'P1EopKclBjgn37mL3EhjWr8tyVvMw1cxJAys533HcNyxWW0q7sEd5HWXoJ1pSIK9cXlCVzAN7wK+VsO2qCCvEeDDbpO/PPtYE7jrB/Sm8XjnL6BBvt4rMcLGVNBamRgLjbQ0Qa466KgOfNdpcFDu04Vxk8cknRYXsWmEkh++QgNCdtIvhu3k/rFyD6iJjj/nqs3/1JMh4utbiOcCXZylPDVv7WJBTYYmKlTT92aq3H42o5EImclA4u7KEjUff++yiPgZ++d1E/gFs2omp99fpoSWVBGgs5todUFS/6qvLkPzPnvUL5FOUm5CClbJWVgSgEaRxZ4ctg790HQCO1OFk81USacQPfIWqHvOpayORM1G121nEO31fvTJYf7FTL1EfIGHNnT8AcFbS2Z/crxbnp00xIzGF9uRQ8xftGvdb1lQH7VXa9ZxA6GtFiXFdiT81MLJdcyd9VYpYNzyE1Z3JoeAW88q9YmFRZj9gCVu2EbirphvFXe114otZQUnqCeg';

const payload = 'C7KDZkNbemqgs7d4JXlAYZQx11+mAB53QkJogF6/FfyXjspoykb6gf3yTm4tPaoaDTTvFrEJGETuCkeXLaLPz2OtKlOdSoZaI/RpGqCsRx1YftWzLrGTFPtXno0tVsr499vCv971wO/cOPJxCl460g8v64vj6WGFL5vHhFrlqzRS3p/pZ/APIAMNyZjwqpVfPru61shXpI8PIm66tOy2LhzfFOhg50dSHuyYG8M5kVf+fiO/QFLdE3t7KsCl/tvQXHlNAkTaOkPx8fH9ZMYdqk9SxF+7hJrxBq1lXx/YO427G49uxlkf9NPQk3nJAv0MtVfZprr6WZ+FVMDsk3y2bdVoFg4DUFLE/x3tCFs3gAyZBFYuJzne7SAtYTQUEUnoiD6RPU6xSvFfnuyzO7v5KevJZy0dxfKoIccxsuo08JlTQ/Xhdg1rcsTIUoJXKSNzKscEnPygRLBz2iR00xpSMgCfTYOp1PdebAfSYtJAQeMJ2Vppkfpgjey+xcXaUF8q6/vH+CyddUra48bkVVXrcu2ssRW9kv5VdzIhIK85QGdi59nCDFyx4tbOVjKwM0ztn5s347fJ+8/DLUWNBhy5X292rtibz+nR8rDoGsJVAO2eP6BUIKRA6zSYpKDYFC2cT+xERGb609DbVEm6fPdYmVwSdm4BjCP/bMY4Cy924TWcPcY9Z7NeeIiPdIGiJBLcNv4ARJQZjVzUFNJgxBDlua4RxrR2mDeJLQ==';

class RsaVerifier {
  bool verifyRsaSignature({
    String publicKeyBase64 = publicKey,
    String signatureBase64 = signature,
    String payloadBase64 = payload,
  }) {
    final signature = base64Decode(signatureBase64);
    final publicKey = base64Decode(publicKeyBase64);
    final signedData = payloadBase64.convertToUint8List();

    final rsaSignature = RSASignature(signature);

    final rsaPublicKey = PublicKeyParameter<RSAPublicKey>(
      _rsaPublicKeyFromDERBytes(publicKey),
    );

    // final verifier = RSASigner(SHA384Digest(), '0609608648016503040202');
    final verifier = Signer('SHA-384/RSA')
      ..init(
        false, // false means verify
        rsaPublicKey,
      );

    try {
       return verifier.verifySignature(signedData, rsaSignature);
    } catch (error, stackTrace) {
      throw SignatureVerificationFailedException(
        error,
        stackTrace: stackTrace,
      );
    }
  }

  RSAPublicKey _rsaPublicKeyFromDERBytes(Uint8List bytes) {
    final asn1Parser = ASN1Parser(bytes);
    final topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
    ASN1Sequence publicKeySeq;
    if (topLevelSeq.elements![1].runtimeType == ASN1BitString) {
      final publicKeyBitString = topLevelSeq.elements![1] as ASN1BitString;

      final publicKeyAsn =
          ASN1Parser(publicKeyBitString.stringValues as Uint8List?);
      publicKeySeq = publicKeyAsn.nextObject() as ASN1Sequence;
    } else {
      publicKeySeq = topLevelSeq;
    }
    final modulus = publicKeySeq.elements![0] as ASN1Integer;
    final exponent = publicKeySeq.elements![1] as ASN1Integer;

    final rsaPublicKey = RSAPublicKey(modulus.integer!, exponent.integer!);

    return rsaPublicKey;
  }
}

extension on String {
  Uint8List convertToUint8List() {
    return Uint8List.fromList(utf8.encode(this));
  }
}