dart 版本的 Diffie-Hellman 实现

最近工作中要用到这个算法,从而实现数据交换时的安全。

在flutter/dart的库中没有找到直接能使用的,所以去找了几个版本,最后用C#的一个版本做了移植(原代码出处请看代码内注释)。

希望对需要的人有帮助。

 

代码如下:

//
// DiffieHellmanManaged.cs: Implements the Diffie-Hellman key agreement algorithm
//
// Author:
//	Pieter Philippaerts ([email protected])
//
// (C) 2003 The Mentalis.org Team (http://www.mentalis.org/)
//
//   References:
//     - PKCS#3  [http://www.rsasecurity.com/rsalabs/pkcs/pkcs-3/]
//

import 'dart:math';
import 'dart:typed_data';

import 'util.dart';

/// 
/// Implements the Diffie-Hellman algorithm.
/// 
class DiffieHellmanManaged {

    /// 
    /// Initializes a new  instance.
    /// 
    /// The length, in bits, of the public P parameter.
    /// The length, in bits, of the secret value X. This parameter can be set to 0 to use the default size.
    /// One of the  values.
    /// The larger the bit length, the more secure the algorithm is. The default is 1024 bits. The minimum bit length is 128 bits.
The size of the private value will be one fourth of the bit length specified.
/// The specified bit length is invalid. DiffieHellmanManaged(int bitlen, int l, DHKeyGeneration keygen) { if (bitlen < 256 || l < 0) throw Exception('invliad bitlength'); //BigInt p, g; var ary = GenerateKey(bitlen, keygen); _initialize(ary[0], ary[1], null, l, false); } /// /// Initializes a new instance. /// /// The P parameter of the Diffie-Hellman algorithm. This is a public parameter. /// The G parameter of the Diffie-Hellman algorithm. This is a public parameter. /// The X parameter of the Diffie-Hellman algorithm. This is a private parameter. If this parameters is a null reference (Nothing in Visual Basic), a secret value of the default size will be generated. DiffieHellmanManaged.init(Uint8List p, Uint8List g, [Uint8List x]) { if (p == null || g == null) throw new Exception('invalid parameters'); if (x == null) _initialize(Util.decodeBigInt(p), Util.decodeBigInt(g), null, 0, true); else _initialize(Util.decodeBigInt(p), Util.decodeBigInt(g), Util.decodeBigInt(x), 0, true); } DiffieHellmanManaged.init2(BigInt p, BigInt g,[int secretLen = 0,BigInt x]) { if (p == null || g == null) throw new Exception('invalid parameters'); _initialize(p, g, x, secretLen, true); } //is prime: https://www.codeproject.com/articles/2728/c-biginteger-class // initializes the private variables (throws CryptographicException) void _initialize(BigInt p, BigInt g, BigInt x, int secretLen, bool checkInput) { //if (!p.isProbablePrime() || g <= 0 || g >= p || (x != null && (x <= 0 || x > p - 2))) // throw new CryptographicException(); if ( g <= BigInt.zero || g >= p || (x != null && (x <= BigInt.zero || x > p - BigInt.from(2)))) throw Exception('Invalid Paramters'); m_P = p; m_G = g; // default is to generate a number as large as the prime this // is usually overkill, but it's the most secure thing we can // do if the user doesn't specify a desired secret length ... if (secretLen == 0) secretLen = m_P.bitLength; //p.bitCount(); if (x == null) { m_X = BigInt.zero; BigInt pm1 = m_P - BigInt.from(1); //for (m_X = BigInt.genRandom(secretLen); m_X >= pm1 || m_X == 0; m_X = BigInt.genRandom(secretLen)) {} while (m_X >= pm1 || m_X == BigInt.zero) { var re = _randBytes(Random(DateTime .now() .millisecond), (secretLen / 8).toInt()); m_X = Util.decodeBigInt(re); } } else { m_X = x; } } /// Returns [n] random bytes. Uint8List _randBytes(Random generator, int n) { final Uint8List random = Uint8List(n); for (int i = 0; i < random.length; i++) { random[i] = generator.nextInt(255); } return random; } /// /// Creates the key exchange data. /// /// The key exchange data to be sent to the intended recipient. Uint8List CreateKeyExchange() { BigInt y = m_G.modPow(m_X, m_P); //byte[] ret = y.getBytes(); var re = Util.encodeBigInt(y); y = BigInt.zero; return re; } /// /// Extracts secret information from the key exchange data. /// /// The key exchange data within which the shared key is hidden. /// The shared key derived from the key exchange data. Uint8List DecryptKeyExchange(Uint8List keyEx) { //BigInt pvr = new BigInt(keyEx); var pvr = Util.decodeBigInt(keyEx); BigInt z = pvr.modPow(m_X, m_P); //byte[] ret = z.getBytes(); var re = Util.encodeBigInt(z); z = BigInt.zero; return re; } /// /// Releases the unmanaged resources used by the SymmetricAlgorithm and optionally releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. // void Dispose(bool disposing) { // if (!m_Disposed) { // m_P = BigInt.zero; // m_G = BigInt.zero; // m_X = BigInt.zero; // } // m_Disposed = true; // } /// /// Exports the . /// /// true to include private parameters; otherwise, false. /// The parameters for . DHParameters ExportParameters(bool includePrivateParameters) { DHParameters ret = new DHParameters(); ret.P = Util.encodeBigInt(m_P);//.getBytes(); ret.G = Util.encodeBigInt(m_G);//.getBytes(); if (includePrivateParameters) { ret.X = Util.encodeBigInt(m_X);//.getBytes(); } return ret; } // /// // /// Imports the specified . // /// // /// The parameters for . // /// or is a null reference (Nothing in Visual Basic) -or- is not a prime number. // void ImportParameters(DHParameters parameters) { // if (parameters.P == null) // throw new CryptographicException("Missing P value."); // if (parameters.G == null) // throw new CryptographicException("Missing G value."); // // BigInt p = new BigInt(parameters.P), // g = new BigInt(parameters.G), // x = null; // if (parameters.X != null) { // x = new BigInt(parameters.X); // } // Initialize(p, g, x, 0, true); // } //TODO: implement DH key generation methods //return P,G List GenerateKey(int bitlen, DHKeyGeneration keygen) { BigInt p, g; if (keygen == DHKeyGeneration.Static) { if (bitlen == 768) p = Util.decodeBigInt(m_OAKLEY768); else if (bitlen == 1024) p = Util.decodeBigInt(m_OAKLEY1024); else if (bitlen == 1536) p = Util.decodeBigInt(m_OAKLEY1536); else throw Exception("Invalid bit size."); g = BigInt.from(22); // all OAKLEY keys use 22 as generator //} else if (keygen == DHKeyGeneration.SophieGermain) { // throw new NotSupportedException(); //TODO //} else if (keygen == DHKeyGeneration.DSA) { // 1. Let j = (p - 1)/q. // 2. Set h = any integer, where 1 < h < p - 1 // 3. Set g = h^j mod p // 4. If g = 1 go to step 2 // BigInt j = (p - 1) / q; } else { // random //p = BigInt.genPseudoPrime(bitlen); p = Util.decodeBigInt(_randBytes(Random(DateTime .now() .millisecond), (bitlen / 8).toInt())); g = new BigInt.from(3); // always use 3 as a generator } return [p,g]; } BigInt m_P; BigInt m_G; BigInt m_X; //bool m_Disposed; final m_OAKLEY768 = [ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ]; final m_OAKLEY1024 = [ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] ; final m_OAKLEY1536 = [ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ]; } enum DHKeyGeneration { /// /// [TODO] you first randomly select a prime Q of size 160 bits, then choose P randomly among numbers like /// Q*R+1 with R random. Then you go along with finding a generator G which has order exactly Q. The private /// key X is then a number modulo Q. /// [FIPS 186-2-Change1 -- http://csrc.nist.gov/publications/fips/] /// // see RFC2631 [http://www.faqs.org/rfcs/rfc2631.html] //DSA, /// /// Returns dynamically generated values for P and G. Unlike the Sophie Germain or DSA key generation methods, /// this method does not ensure that the selected prime offers an adequate security level. /// Random, /// /// Returns dynamically generated values for P and G. P is a Sophie Germain prime, which has some interesting /// security features when used with Diffie Hellman. /// //SophieGermain, /// /// Returns values for P and G that are hard coded in this library. Contrary to what your intuition may tell you, /// using these hard coded values is perfectly safe. /// The values of the P and G parameters are taken from 'The OAKLEY Key Determination Protocol' [RFC2412]. /// This is the prefered key generation method, because it is very fast and very safe. /// Because this method uses fixed values for the P and G parameters, not all bit sizes are supported. /// The current implementation supports bit sizes of 768, 1024 and 1536. /// Static } /// /// Represents the parameters of the Diffie-Hellman algorithm. /// class DHParameters { /// /// Represents the public P parameter of the Diffie-Hellman algorithm. /// Uint8List P; /// /// Represents the public G parameter of the Diffie-Hellman algorithm. /// Uint8List G; /// /// Represents the private X parameter of the Diffie-Hellman algorithm. /// Uint8List X; }

注:里面的  isProbablePrime()  判断未实现。

 

测试代码:

      var dh1 = DiffieHellmanManaged(1024,1024,DHKeyGeneration.Static);
      var key1 = dh1.CreateKeyExchange();
      print("key1:$key1");
      var dh2 = DiffieHellmanManaged.init2(dh1.m_P, dh1.m_G);
      var key2 = dh2.CreateKeyExchange();
      print("key2:$key2");

      var secretKey1 = dh1.DecryptKeyExchange(key2);
      print("exchanged secret from key1:$secretKey1");
      var secretKye2 = dh2.DecryptKeyExchange(key1);
      print("exchanged secret from key2:$secretKye2");

      expect(secretKey1,secretKey1);

--END--

你可能感兴趣的:(flutter,Diffie-Hellman,dart)