GithubHelp home page GithubHelp logo

Comments (2)

FlutterCrypto avatar FlutterCrypto commented on August 16, 2024 1

It takes you through some conversions to get a base64 signature in IEEE P1363 encoding. This encoding means that the two parts of the signature (R and S value, each 32 bytes long) are concatenated to a 64 byte long value.

Kindly keep in mind that this encoding is not compatible to the second encoding type, the DER encoded signature.

Below you find a full running example from my cross platform cryptography project (https://github.com/java-crypto/cross_platform_crypto/tree/main/EcSignatureP256Sha256String), it is using "ECDSA with SHA-256":

import 'dart:convert';
import 'dart:typed_data';
import 'package:pointycastle/asn1.dart';
import 'package:pointycastle/asn1/object_identifiers.dart';
import "package:pointycastle/export.dart";
import 'package:basic_utils/basic_utils.dart';

void main() {
/* add in pubspec.yaml:
dependencies:
  pointycastle: ^3.1.1
  basic_utils: ^3.4.0
 */
  // https://pub.dev/packages/pointycastle
  // https://github.com/bcgit/pc-dart/
  // https://pub.dev/packages/basic_utils
  // https://github.com/Ephenodrom/Dart-Basic-Utils

  print('EC signature string (ECDSA with SHA256)');

  final dataToSignString = 'The quick brown fox jumps over the lazy dog';
  final dataToSign = createUint8ListFromString(dataToSignString);
  print('plaintext: ' + dataToSignString);

  // # # # usually we would load the private and public key from a file or keystore # # #
  // # # # here we use hardcoded keys for demonstration - don't do this in real programs # # #

  print('\n* * * sign the plaintext with the EC private key * * *');
  final privateKeyPem = loadEcPrivateKeyPem();
  // get the data from PEM
  Uint8List ecPrivateKeyPkcs8Der =
      CryptoUtils.getBytesFromPEMString(privateKeyPem);
  ECPrivateKey ecPrivateKey =
      ecPrivateKeyFromDerBytesPkcs8(ecPrivateKeyPkcs8Der);
  final signatureBase64 = ecSignToBase64(ecPrivateKey, dataToSign);
  print('signature (Base64): ' + signatureBase64);

  print(
      '\n* * * verify the signature against the plaintext with the EC public key * * *');
  final publicKeyPem = loadEcPublicKeyPem();
  final ecPublicKey =
      CryptoUtils.ecPublicKeyFromPem(publicKeyPem) as ECPublicKey;
  bool signatureVerifiedBase64 =
      ecVerifySignatureFromBase64(ecPublicKey, dataToSign, signatureBase64);
  print('signature (Base64) verified: ' + signatureVerifiedBase64.toString());
}

String ecSignToBase64(ECPrivateKey privateKey, Uint8List messageByte) {
  ECSignature ecSignature = CryptoUtils.ecSign(privateKey, messageByte,
      algorithmName: 'SHA-256/ECDSA');
  BigInt r = ecSignature.r;
  BigInt s = ecSignature.s;
  Uint8List rUint8ListV2 = encodeBigInt(r) as Uint8List;
  Uint8List sUint8ListV2 = encodeBigInt(s) as Uint8List;
  var bbV2 = BytesBuilder();
  bbV2.add(rUint8ListV2);
  bbV2.add(sUint8ListV2);
  var signature = bbV2.toBytes();
  return base64Encoding(signature);
}

bool ecVerifySignatureFromBase64(
    ECPublicKey publicKey, Uint8List messageByte, String signatureBase64) {
  var signatureValue = base64Decoding(signatureBase64);
  BigInt r = decodeBigInt(Uint8List.sublistView(signatureValue, 0, 32));
  BigInt s = decodeBigInt(Uint8List.sublistView(signatureValue, 32, 64));
  ECSignature signature = new ECSignature(r, s);
  return CryptoUtils.ecVerify(publicKey, messageByte, signature,
      algorithm: 'SHA-256/ECDSA');
}

Uint8List createUint8ListFromString(String s) {
  var ret = new Uint8List(s.length);
  for (var i = 0; i < s.length; i++) {
    ret[i] = s.codeUnitAt(i);
  }
  return ret;
}

// http://phoenix.yizimg.com/ethereumdart/rlp/blob/master/lib/src/pointycastle-utils.dart
/// Decode a BigInt from bytes in big-endian encoding.
BigInt decodeBigInt(List<int> bytes) {
  BigInt result = new BigInt.from(0);
  for (int i = 0; i < bytes.length; i++) {
    result += new BigInt.from(bytes[bytes.length - i - 1]) << (8 * i);
  }
  return result;
}

var _byteMask = new BigInt.from(0xff);

/// Encode a BigInt into bytes using big-endian encoding.
Uint8List encodeBigInt(BigInt number) {
  // Not handling negative numbers. Decide how you want to do that.
  int size = (number.bitLength + 7) >> 3;
  var result = new Uint8List(size);
  for (int i = 0; i < size; i++) {
    result[size - i - 1] = (number & _byteMask).toInt();
    number = number >> 8;
  }
  return result;
}

///
/// Decode the given [bytes] in PKCS8 encoding into an [ECPrivateKey].
///
ECPrivateKey ecPrivateKeyFromDerBytesPkcs8(Uint8List bytes) {
  ASN1Parser asn1Parser = ASN1Parser(bytes);
  ASN1Sequence topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
  ASN1Sequence innerSeq = topLevelSeq.elements!.elementAt(1) as ASN1Sequence;
  ASN1ObjectIdentifier b2 =
      innerSeq.elements!.elementAt(1) as ASN1ObjectIdentifier;
  String? b2Data = b2.objectIdentifierAsString;
  Map<String, dynamic>? b2Curvedata =
      ObjectIdentifiers.getIdentifierByIdentifier(b2Data);
  dynamic curveName;
  if (b2Curvedata != null) {
    curveName = b2Curvedata['readableName'];
  }
  // get the octet string data for the private key der data
  ASN1OctetString octetString =
      topLevelSeq.elements!.elementAt(2) as ASN1OctetString;
  asn1Parser = ASN1Parser(octetString.valueBytes);
  ASN1Sequence octetStringSeq = asn1Parser.nextObject() as ASN1Sequence;
  ASN1OctetString octetStringKeyData =
      octetStringSeq.elements!.elementAt(1) as ASN1OctetString;
  // now generate the key
  Uint8List privateKeyDer = octetStringKeyData.valueBytes!;
  return ECPrivateKey(
      decodeBigInt(privateKeyDer), ECDomainParameters(curveName));
}

// don't worry - it's a sample key
String loadEcPublicKeyPem() {
  return ('''-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMZHnt1D1tddLNGlEHXEtEw5K/G5Q
HkgaLi+IV84oiV+THv/DqGxYDX2F5JOkfyv36iYSf5lfIC7q9el4YLnlwA==
-----END PUBLIC KEY-----''');
}

// don't worry - it's a sample key
String loadEcPrivateKeyPem() {
  return ('''-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgciPt/gulzw7/Xe12
YOu/vLUgIUZ+7gGo5VkmU0B+gUWhRANCAAQxkee3UPW110s0aUQdcS0TDkr8blAe
SBouL4hXziiJX5Me/8OobFgNfYXkk6R/K/fqJhJ/mV8gLur16XhgueXA
-----END PRIVATE KEY-----''');
}

String base64Encoding(Uint8List input) {
  return base64.encode(input);
}

Uint8List base64Decoding(String input) {
  return base64.decode(input);
}

from dart-basic-utils.

Ephenodrom avatar Ephenodrom commented on August 16, 2024

This can be considered as closed as with the latest commits it is now possible to create a base64 signature following the ASN1 structure from https://datatracker.ietf.org/doc/html/rfc5480#page-17.

from dart-basic-utils.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.