dart 版本的 Diffie-Hellman 实现

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

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

希望对需要的人有帮助。

 

代码如下:

//
// DiffieHellmanManaged.cs: Implements the Diffie-Hellman key agreement algorithm
//
// Author:
//	Pieter Philippaerts (Pieter@mentalis.org)
//
// (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';

/// <summary>
/// Implements the Diffie-Hellman algorithm.
/// </summary>
class DiffieHellmanManaged {

    /// <summary>
    /// Initializes a new <see cref="DiffieHellmanManaged"/> instance.
    /// </summary>
    /// <param name="bitlen">The length, in bits, of the public P parameter.</param>
    /// <param name="l">The length, in bits, of the secret value X. This parameter can be set to 0 to use the default size.</param>
    /// <param name="keygen">One of the <see cref="DHKeyGeneration"/> values.</param>
    /// <remarks>The larger the bit length, the more secure the algorithm is. The default is 1024 bits. The minimum bit length is 128 bits.<br/>The size of the private value will be one fourth of the bit length specified.</remarks>
    /// <exception cref="ArgumentException">The specified bit length is invalid.</exception>
    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);
    }
    /// <summary>
    /// Initializes a new <see cref="DiffieHellmanManaged"/> instance.
    /// </summary>
    /// <param name="p">The P parameter of the Diffie-Hellman algorithm. This is a public parameter.</param>
    /// <param name="g">The G parameter of the Diffie-Hellman algorithm. This is a public parameter.</param>
    /// <param name="x">The X parameter of the Diffie-Hellman algorithm. This is a private parameter. If this parameters is a null reference (<b>Nothing</b> in Visual Basic), a secret value of the default size will be generated.</param>
    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;
    }

    /// <summary>
    /// Creates the key exchange data.
    /// </summary>
    /// <returns>The key exchange data to be sent to the intended recipient.</returns>
    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;
    }

    /// <summary>
    /// Extracts secret information from the key exchange data.
    /// </summary>
    /// <param name="keyEx">The key exchange data within which the shared key is hidden.</param>
    /// <returns>The shared key derived from the key exchange data.</returns>
    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;
    }

    /// <summary>
    /// Releases the unmanaged resources used by the SymmetricAlgorithm and optionally releases the managed resources.
    /// </summary>
    /// <param name="disposing"><b>true</b> to release both managed and unmanaged resources; <b>false</b> to release only unmanaged resources.</param>
//    void Dispose(bool disposing) {
//        if (!m_Disposed) {
//            m_P = BigInt.zero;
//            m_G = BigInt.zero;
//            m_X = BigInt.zero;
//        }
//        m_Disposed = true;
//    }

    /// <summary>
    /// Exports the <see cref="DHParameters"/>.
    /// </summary>
    /// <param name="includePrivateParameters"><b>true</b> to include private parameters; otherwise, <b>false</b>.</param>
    /// <returns>The parameters for <see cref="DiffieHellman"/>.</returns>
    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;
    }

//    /// <summary>
//    /// Imports the specified <see cref="DHParameters"/>.
//    /// </summary>
//    /// <param name="parameters">The parameters for <see cref="DiffieHellman"/>.</param>
//    /// <exception cref="CryptographicException"><paramref name="P"/> or <paramref name="G"/> is a null reference (<b>Nothing</b> in Visual Basic) -or- <paramref name="P"/> is not a prime number.</exception>
//    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<BigInt> 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 {
    /// <summary>
    /// [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/]
    /// </summary>
    // see RFC2631 [http://www.faqs.org/rfcs/rfc2631.html]
    //DSA,
    /// <summary>
    /// 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.
    /// </summary>
    Random,
    /// <summary>
    /// 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.
    /// </summary>
    //SophieGermain,
    /// <summary>
    /// 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.
    /// </summary>
    Static
}

/// <summary>
/// Represents the parameters of the Diffie-Hellman algorithm.
/// </summary>
 class DHParameters {
     /// <summary>
     /// Represents the public <b>P</b> parameter of the Diffie-Hellman algorithm.
     /// </summary>
     Uint8List P;

     /// <summary>
     /// Represents the public <b>G</b> parameter of the Diffie-Hellman algorithm.
     /// </summary>
     Uint8List G;

     /// <summary>
     /// Represents the private <b>X</b> parameter of the Diffie-Hellman algorithm.
     /// </summary>
     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--

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值