PointyCastle / pointycastle

Moved into the Bouncy Castle project: https://github.com/bcgit/pc-dart
MIT License
270 stars 76 forks source link

RSAKeyGenerator broken in Dart 2 #127

Open stevenroose opened 6 years ago

stevenroose commented 6 years ago

Migrating from BigInteger to Dart 2's BigInt introduced an issue with the RSA Key Generator.

The PR introducing the issue is here: https://github.com/PointyCastle/pointycastle/pull/122

This could be solved by https://github.com/dart-lang/sdk/issues/32626, but I'm currently unable to test this.

krzaklus commented 6 years ago

This bug is still exist? In my code, method generateKeyPair() don't return any value? Anyone had the same problem?

var parameters = RSAKeyGeneratorParameters(BigInt.from(65537), 1024, 5);

var secureRandom = new FortunaRandom();
var random = new Random.secure();
List<int> seeds = [];
for (int i = 0; i < 32; i++) {
  seeds.add(random.nextInt(255));
}
secureRandom.seed(new KeyParameter(new Uint8List.fromList(seeds)));

var params = new ParametersWithRandom(parameters, secureRandom);

rsaKeyGenerator.init(params);

// This block never return value
var keyPair = rsaKeyGenerator.generateKeyPair();

Using FixedSecureRandom have the same effect.

I used my code in flutter.

krzaklus commented 6 years ago

Ok i repair this bug.

Fixed implementation of method generateKeyPair, please update this method or give me right to repository push.

The was with bad usage BigInit.

For example:

if (e.gcd(q - BigInt.one) == 1) {
    break;
}

This "if" was never "true" because "BigInt.gcd()" return "BigInt" not "int", so "break" never happen and method start infinity loop. I change it to:

if (e.gcd(q - BigInt.one) == BigInt.one) {
    break;
}

Full fixed method:

AsymmetricKeyPair generateKeyPair() {
    var p, q, n, e;

    // p and q values should have a length of half the strength in bits
    var strength = _params.bitStrength;
    var pbitlength = (strength + 1) ~/ 2;
    var qbitlength = strength - pbitlength;
    var mindiffbits = strength ~/ 3;

    e = _params.publicExponent;

    // TODO Consider generating safe primes for p, q (see DHParametersHelper.generateSafePrimes)
    // (then p-1 and q-1 will not consist of only small factors - see "Pollard's algorithm")

    // generate p, prime and (p-1) relatively prime to e
    for ( ; ; ) {
      p = generateProbablePrime(pbitlength, 1, _random);

      if (p % e == BigInt.one) {
        continue;
      }

      if (!_isProbablePrime(p, _params.certainty)) {
        continue;
      }

      if (e.gcd(p - BigInt.one) == BigInt.one) {
        break;
      }
    }

    // generate a modulus of the required length
    for ( ; ; ) {

      // generate q, prime and (q-1) relatively prime to e, and not equal to p
      for ( ; ; ) {
        q = generateProbablePrime(qbitlength, 1, _random);

        if ((q - p).abs().bitLength < mindiffbits) {
          continue;
        }

        if (q.modInverse(e) == BigInt.one) {
          continue;
        }

        if (!_isProbablePrime(q, _params.certainty)) {
          continue;
        }

        if (e.gcd(q - BigInt.one) == BigInt.one) {
          break;
        }
      }

      // calculate the modulus
      n = (p * q);

      if (n.bitLength == _params.bitStrength) {
        break;
      }

      // if we get here our primes aren't big enough, make the largest of the two p and try again
      p = (p.compareTo(q) > 0) ? p : q;
    }

    // Swap p and q if necessary
    if (p < q) {
      var swap = p;
      p = q;
      q = swap;
    }

    // calculate the private exponent
    var pSub1 = (p - BigInt.one);
    var qSub1 = (q - BigInt.one);
    var phi = (pSub1 * qSub1);
    var d = e.modInverse(phi);

    return new AsymmetricKeyPair(new RSAPublicKey(n, e), new RSAPrivateKey(n, d, p, q));
  }
ZeCarlosCoutinho commented 6 years ago

@krzaklus I tried your method, and now the generateKeyPair() is not stuck anymore :) Thanks! However, when i initialize the RSAEngine with the public key, it gives me the following error: type 'RSAPublicKey' is not a subtype of type 'Null'

The code I have is the following:

RSAEngine cipherBlock = RSAEngine()
    ..init(true, new PublicKeyParameter(keys.publicKey));

and he throws the exception in the constructor of the PublicKeyParameter.

Is this a problem a consequence of the changes you made, or is it something else?

krzaklus commented 6 years ago

I think is something else, I didn't any changes on types, if you want solve this you must used this code below (keyParametersPublic must by cast to type AsymmetricKeyParameter):

AsymmetricKeyParameter keyParametersPublic = new PublicKeyParameter(publicKey);

var cipher = new RSAEngine()..init(true, keyParametersPublic);

My full method:

static String encrypt(String plainText, RSAPublicKey publicKey) { try { AsymmetricKeyParameter keyParametersPublic = new PublicKeyParameter(publicKey);

var cipher = new RSAEngine()..init(true, keyParametersPublic);

var cipherText = cipher.process(new Uint8List.fromList(plainText.codeUnits));

return base64.encode(cipherText); } catch (e) { print(e.toString()); return null; } }

Encrypt and Decrypt works well, but I stucked on sign data with rsa, my code.

var rsaSigner = RSASigner(new SHA256Digest(), "0609608648016503040201");

AsymmetricBlockCipher _rsa = PKCS1Encoding(RSAEngine()); AsymmetricKeyParameter keyParametersPrivate = new PrivateKeyParameter(keyPair.privateKey);

rsaSigner.init(true, keyParametersPrivate);

method init throw me

EXCEPTION: Bad state: Reflectable has not been initialized. Did you forget to add the main file to the reflectable transformer's entry_points in pubspec.yaml?

I compile my code on flutter, and I dont now whot shoul I do next:/

czw., 28 cze 2018 o 20:32 José Carlos Coutinho notifications@github.com napisał(a):

@krzaklus https://github.com/krzaklus I tried your method, and now the generateKeyPair() is not stuck anymore :) Thanks! However, when i initialize the RSAEngine with the public key, it gives me the following error: type 'RSAPublicKey' is not a subtype of type 'Null'

The code I have is the following:

RSAEngine cipherBlock = RSAEngine() ..init(true, new PublicKeyParameter(keys.publicKey));

and he throws the exception in the constructor of the PublicKeyParameter.

Is this a problem a consequence of the changes you made, or is it something else?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/PointyCastle/pointycastle/issues/127#issuecomment-401131166, or mute the thread https://github.com/notifications/unsubscribe-auth/ANV0TmqmiP7UugFikXmayWGqAuwjBtygks5uBSFSgaJpZM4UQZpZ .

krzaklus commented 6 years ago

I think property final AsymmetricBlockCipher _rsa = PKCS1Encoding(RSAEngine());

from lib class class RSASigner implements Signer

need reflection to build, thats why I have that error, but I dont know how to overide this, do you have any sugestion?

czw., 28 cze 2018 o 20:58 Krzysztof krzaklus@gmail.com napisał(a):

I think is something else, I didn't any changes on types, if you want solve this you must used this code below (keyParametersPublic must by cast to type AsymmetricKeyParameter):

AsymmetricKeyParameter keyParametersPublic = new PublicKeyParameter(publicKey);

var cipher = new RSAEngine()..init(true, keyParametersPublic);

My full method:

static String encrypt(String plainText, RSAPublicKey publicKey) { try { AsymmetricKeyParameter keyParametersPublic = new PublicKeyParameter(publicKey);

var cipher = new RSAEngine()..init(true, keyParametersPublic);

var cipherText = cipher.process(new Uint8List.fromList(plainText.codeUnits));

return base64.encode(cipherText); } catch (e) { print(e.toString()); return null; } }

Encrypt and Decrypt works well, but I stucked on sign data with rsa, my code.

var rsaSigner = RSASigner(new SHA256Digest(), "0609608648016503040201");

AsymmetricBlockCipher _rsa = PKCS1Encoding(RSAEngine()); AsymmetricKeyParameter keyParametersPrivate = new PrivateKeyParameter(keyPair.privateKey);

rsaSigner.init(true, keyParametersPrivate);

method init throw me

EXCEPTION: Bad state: Reflectable has not been initialized. Did you forget to add the main file to the reflectable transformer's entry_points in pubspec.yaml?

I compile my code on flutter, and I dont now whot shoul I do next:/

czw., 28 cze 2018 o 20:32 José Carlos Coutinho notifications@github.com napisał(a):

@krzaklus https://github.com/krzaklus I tried your method, and now the generateKeyPair() is not stuck anymore :) Thanks! However, when i initialize the RSAEngine with the public key, it gives me the following error: type 'RSAPublicKey' is not a subtype of type 'Null'

The code I have is the following:

RSAEngine cipherBlock = RSAEngine() ..init(true, new PublicKeyParameter(keys.publicKey));

and he throws the exception in the constructor of the PublicKeyParameter.

Is this a problem a consequence of the changes you made, or is it something else?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/PointyCastle/pointycastle/issues/127#issuecomment-401131166, or mute the thread https://github.com/notifications/unsubscribe-auth/ANV0TmqmiP7UugFikXmayWGqAuwjBtygks5uBSFSgaJpZM4UQZpZ .

ZeCarlosCoutinho commented 6 years ago

I am still a beginner, but I believe it is a problem when the program reaches the SecureRandom constructor. In pkcs1.dart, it crashes in the init method, when the cypher parameters are not an instance of ParametersWithRandom. The program enters the "else", and tries to create the instance of SecureRandom without the algorithmName argument, which I believe its the cause of the error.

// pkcs1.dart

void init(bool forEncryption, CipherParameters params) {
    AsymmetricKeyParameter akparams;

    if (params is ParametersWithRandom) {
      ParametersWithRandom paramswr = params;

      _random = paramswr.random;
      akparams = paramswr.parameters;
    } else {
      _random = new SecureRandom();        //Crashes here
      akparams = params;
    }

    _engine.init(forEncryption, akparams);

    _forPrivateKey = (akparams.key is PrivateKey);
    _forEncryption = forEncryption;
  }

Maybe using a default algorithm name in SecureRandom constructor, instead of "" would solve the problem? (I don't know almost anything about the secure random number generators, so I don't know the names of the possible algorithms :cry: )

// secure_random.dart

factory SecureRandom([String algorithmName = "<algorithmName>"]) =>
      registry.create(SecureRandom, algorithmName) as SecureRandom;
proteye commented 6 years ago

Please, update 'generateKeyPair' to: AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey> generateKeyPair() {...}

Otherwise I can not do this: print(keyPair.publicKey.modulus); print(keyPair.publicKey.exponent);

proteye commented 6 years ago

I can not use the method SecureRandom() in Flutter - it crashes. flutter: Another exception was thrown: NoSuchMethodError: The method '[]' was called on null.

stevenroose commented 6 years ago

@proteye for that specific issue you need to specify a random algorithm. new SecureRandom("Fortuna") with registry or new FortunaRandom() without registry.

As for the prior comments: are those still relevant? If so, could someone make a PR perhaps?

proteye commented 6 years ago

@stevenroose thank you. I realized this as follows: https://gist.github.com/proteye/982d9991922276ccfb011dfc55443d74

I fixed the code as @krzaklus and sent PR. https://github.com/PointyCastle/pointycastle/pull/135

mslavkovski commented 5 years ago

I'm trying to make RSAEngine to work and I am facing the same issue: type 'RSAPublicKey' is not a subtype of type 'Null'

I referenced the RC version of the pointycastle library in my pubspec.yaml: pointycastle: ^1.0.0-rc4

The exception is thrown in the following line of code: var cipher = new RSAEngine()..init( true, new PublicKeyParameter(keyPair.publicKey));

I'm struggling for this thing to work in flutter and so far there is no hope nor there are relevant resources out there that I can find. Is this fixed or there is another workaround?

P.S. I have a simple method just to test the functionality:

@protected
  void testGenerateRSA() {
    print('Generating RSA keys...');
    var keyParams = new RSAKeyGeneratorParameters(new BigInt.from(65537), 2048, 5);

    var secureRandom = new SecureRandom("Fortuna"); // new FortunaRandom();
    var random = new Random.secure();

    const int seedLength = 32;
    const int randomMax = 255;
    final Uint8List uint8list = new Uint8List(seedLength);

    for (int i=0; i < seedLength; i++) {
      uint8list[i] = random.nextInt(randomMax);
    }

    final KeyParameter keyParameter = new KeyParameter(uint8list);
    secureRandom.seed(keyParameter);

    var rngParams = new ParametersWithRandom(keyParams, secureRandom);
    var k = new RSAKeyGenerator();
    k.init(rngParams);

    var keyPair = k.generateKeyPair();
    var cipher = new RSAEngine()..init( true, new PublicKeyParameter(keyPair.publicKey));

    var cipherText = cipher.process(new Uint8List.fromList("Hello World".codeUnits));

    print("Encrypted: ${new String.fromCharCodes(cipherText)}");

    cipher.init( false, new PrivateKeyParameter(keyPair.privateKey));
    var decrypted = cipher.process(cipherText);
    print("Decrypted: ${new String.fromCharCodes(decrypted)}");
  }
coylums commented 5 years ago

I am also see this issue above except with a private key and receive type 'RSAPrivateKey' is not a subtype of type 'Null' Anyone else seeing this issue?

glachac commented 5 years ago

@mslavkovski @coylums

You are not providing the type in the generic classes in two of your lines where you create PublicKeyParameter and PrivateKeyParameter. I suspect the underlying code is casting to a T which is undefined, so you are getting cast to null errors.

The two changes are below:

var cipher = new RSAEngine()..init( true, new PublicKeyParameter<RSAPublicKey>(keyPair.publicKey));

cipher.init( false, new PrivateKeyParameter<RSAPrivateKey>r(keyPair.privateKey));

With these two changes, I got your code snippet to work and properly encode and decode "Hello World"

mslavkovski commented 5 years ago

@mslavkovski @coylums

You are not providing the type in the generic classes in two of your lines where you create PublicKeyParameter and PrivateKeyParameter. I suspect the underlying code is casting to a T which is undefined, so you are getting cast to null errors.

The two changes are below:

var cipher = new RSAEngine()..init( true, new PublicKeyParameter<RSAPublicKey>(keyPair.publicKey));

cipher.init( false, new PrivateKeyParameter<RSAPrivateKey>r(keyPair.privateKey));

With these two changes, I got your code snippet to work and properly encode and decode "Hello World"

That was indeed the issue there. Now it works.

Thanks!

lets-swapcode commented 5 years ago

can you share decrypt function ?