独自暗号貨幣を作ろう 第8回

第1回 第2回 第3回 第4回 第5回 第6回 第7回


電子署名
電子署名の枠組みを実装します。
Bitcoinと同様に楕円曲線DSAを使用します。但し、secp256k1ではなく、取り敢えずは普通の(.NET Frameworkが提供している)楕円曲線DSAにします。

普通の楕円曲線DSAによる署名と署名検証を実行する関数を拡張メソッドとして実装します。署名で用いる暗号学的要約関数はSHA-256とします。

public static byte[] SignEcdsaSha256(this byte[] data, byte[] privKey)
{
    using (ECDsaCng dsa = new ECDsaCng(CngKey.Import(privKey, CngKeyBlobFormat.EccPrivateBlob)))
    {
        dsa.HashAlgorithm = CngAlgorithm.Sha256;
 
        return dsa.SignData(data);
    }
}
 
public static bool VerifyEcdsa(this byte[] data, byte[] signature, byte[] pubKey)
{
    using (ECDsaCng dsa = new ECDsaCng(CngKey.Import(pubKey, CngKeyBlobFormat.EccPublicBlob)))
        return dsa.VerifyData(data, signature);
}


後からsecp256k1に差し替えることができるように、鍵対を保持し、署名と署名検証を行うための抽象クラスを作成します。公開鍵と秘密鍵は別々のクラスで保持することにします。

public abstract class DSAPUBKEYBASE : SHAREDDATA
{
    public DSAPUBKEYBASE() : base(null) { }
 
    public DSAPUBKEYBASE(byte[] _pubKey) : base(null) { pubKey = _pubKey; }
 
    public byte[] pubKey { get; private set; }
 
    public abstract bool Verify(byte[] data, byte[] signature);
 
    protected override Func<ReaderWriter, IEnumerable<MainDataInfomation>> StreamInfo
    {
        get
        {
            return (msrw) => new MainDataInfomation[]{
                new MainDataInfomation(typeof(byte[]), null, () => pubKey, (o) => pubKey = (byte[])o),
            };
        }
    }
    public Func<ReaderWriter, IEnumerable<MainDataInfomation>> PublicStreamInfo { get { return StreamInfo; } }
}
 
public abstract class DSAPRIVKEYBASE : SHAREDDATA
{
    public DSAPRIVKEYBASE() : base(null) { }
 
    public DSAPRIVKEYBASE(byte[] _privKey) : base(null) { privKey = _privKey; }
 
    public byte[] privKey { get; private set; }
 
    public abstract byte[] Sign(byte[] data);
 
    protected override Func<ReaderWriter, IEnumerable<MainDataInfomation>> StreamInfo
    {
        get
        {
            return (msrw) => new MainDataInfomation[]{
                new MainDataInfomation(typeof(byte[]), null, () => privKey, (o) => privKey = (byte[])o),
            };
        }
    }
    public Func<ReaderWriter, IEnumerable<MainDataInfomation>> PublicStreamInfo { get { return StreamInfo; } }
}
 
public abstract class DSAKEYPAIRBASE<DsaPubKeyType, DsaPrivKeyType> : SHAREDDATA
    where DsaPubKeyType : DSAPUBKEYBASE
    where DsaPrivKeyType : DSAPRIVKEYBASE
{
    public DSAKEYPAIRBASE() : base(null) { }
 
    public DsaPubKeyType pubKey { get; protected set; }
    public DsaPrivKeyType privKey { get; protected set; }
 
    protected override Func<ReaderWriter, IEnumerable<MainDataInfomation>> StreamInfo { get { return (msrw) => StreamInfoInner(msrw); } }
    private IEnumerable<MainDataInfomation> StreamInfoInner(ReaderWriter msrw)
    {
        if (pubKey == null)
            pubKey = Activator.CreateInstance(typeof(DsaPubKeyType)) as DsaPubKeyType;
        if (privKey == null)
            privKey = Activator.CreateInstance(typeof(DsaPrivKeyType)) as DsaPrivKeyType;
 
        foreach (var mdi in pubKey.PublicStreamInfo(null))
            yield return mdi;
        foreach (var mdi in privKey.PublicStreamInfo(null))
            yield return mdi;
    }
}


DSAPUBKEYBASE抽象クラスのVerify抽象メソッドで署名検証を行います。
DSAPRIVKEYBASE抽象クラスのSign抽象メソッドで署名を行います。

普通の楕円曲線DSAを実装します。

public class Ecdsa256PubKey : DSAPUBKEYBASE
{
    public Ecdsa256PubKey() : base() { }
 
    public Ecdsa256PubKey(byte[] _pubKey) : base(_pubKey) { }
 
    public override bool Verify(byte[] data, byte[] signature)
    {
        return data.VerifyEcdsa(signature, pubKey);
    }
}
 
public class Ecdsa256PrivKey : DSAPRIVKEYBASE
{
    public Ecdsa256PrivKey() : base() { }
 
    public Ecdsa256PrivKey(byte[] _privKey) : base(_privKey) { }
 
    public override byte[] Sign(byte[] data)
    {
        return data.SignEcdsaSha256(privKey);
    }
}
 
public class Ecdsa256KeyPair : DSAKEYPAIRBASE<Ecdsa256PubKey, Ecdsa256PrivKey>
{
    public Ecdsa256KeyPair() : this(false) { }
 
    public Ecdsa256KeyPair(bool isCreate)
    {
        if (isCreate)
        {
            CngKey ck = CngKey.Create(CngAlgorithm.ECDsaP256, null, new CngKeyCreationParameters { ExportPolicy = CngExportPolicies.AllowPlaintextExport });
 
            pubKey = new Ecdsa256PubKey(ck.Export(CngKeyBlobFormat.EccPublicBlob));
            privKey = new Ecdsa256PrivKey(ck.Export(CngKeyBlobFormat.EccPrivateBlob));
        }
    }
}