我们已经展示了,给定生成点 G,并选择一个足够大的标量 e,我们可以轻松计算出 P=e*G,但是当你拥有 G 和 P 时,几乎不可能得到 e。这是椭圆曲线加密的基础。
所选择的标量 k 被称为私钥,而 Q 是公钥。注意,k 是一个256位的整数,而 Q 包含两部分:一个是 x 坐标,另一个是 y 坐标。
这种公钥加密的名称是椭圆曲线数字签名算法,简称 ECDSA。它涉及以下步骤:
选择一个标量 e,计算 P = e*G,其中 e 是私钥,P 是公钥,将 P 公之于众,所有人都可以知道它。私钥的持有者随机选择两个有限域成员 u 和 v,并计算 k = u + ve,k 需要保密。计算 R = k * G = (u + v*e)*G = u*G + v*(e*G) = u*G+v*P,我们仅使用 R 的 x 坐标,并将 x 的值命名为 r。私钥持有者生成一条任意长度的消息文本(可以公开),并通过 sha256 或 md5 将其哈希为一个256位的数字,称该哈希结果为 z。通过公式 s = (z + r*e) / k 计算一个数字 s(所有这些计算都基于模 p 的运算)。将三元组 (z, s, r) 作为私钥持有者的签名发布。任何想要验证消息确实由私钥持有者创建的人可以执行以下步骤:计算 u = z / s,v = r / s。计算 u* G + v* P = (z/s)*G + (r/s)*P = (z/s)*G + (r/s)*(e*G) = (z/s)*G + (re/s)*G = ((z+re)/s)*G = k*G = R',取 R' 的 x 坐标并与 r 进行比较,如果它们相同,那么可以确定消息 z 确实是由私钥持有者创建的。注意我们已经展示了 n*G 是单位元,因此上述涉及 z、s、r、e 的计算都需要基于模 n 进行,并且要记住运算符 “/” 并不是普通的算术除法,而是乘法的逆操作。
我们将这些步骤写入代码中,首先创建一个名为 signature.go 的新文件:
package elliptic_curveimport ("fmt"
)type Signature struct {r *FieldElements *FieldElement
}func NewSignature(r *FieldElement, s *FieldElement) *Signature {return &Signature{r: r,s: s,}
}func (s *Signature) String() string {return fmt.Sprintf("Signature(r: {%s}, s:{%s})", s.r, s.s)
}
它包含两个组件,一个是 r,另一个是 s,它们将用于验证。接着为 FieldElement 添加一个新函数,该函数用于获取当前域元素的逆元素:
func (f *FieldElement) Inverse() *FieldElement {//use Fermat's little theorem to get the reverse of the given field elementvar op big.Intreturn f.Power(op.Sub(f.order, big.NewInt(int64(2))))
}
接着在 point.go 中添加验证函数Verify:
func (p *Point) getGenerator() *Point {Gx := new(big.Int)Gx.SetString("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 16)Gy := new(big.Int)Gy.SetString("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", 16)G := S256Point(Gx, Gy)return G
}func (p *Point) Verify(z *FieldElement, sig *Signature) bool {/*7. any one who want to verify message z is created by owner of e:1, compute u = z/s, v=r/s,2, compute u*G + v*P = (z/s)*G + (r/s)*P = (z/s)*G+(r/s)*eG=(z/s)*P + (r*e/s)*G = ((z+r*e)/s))*G = k*G = R'3, take the x coordinate of R' compare with rif the same => verify the message z is created by owner of enotice we have shown that n * G is identity, therefore the above computation related to z, s, r, e need to do base on mudulur of n, and remember the operator"/" is not the normal arithmetic divide , its the inverse of multilication.*/sInverse := sig.s.Inverse()u := z.Multiply(sInverse)v := sig.r.Multiply(sInverse)G := p.getGenerator()total := (G.ScalarMul(u.num)).Add(p.ScalarMul(v.num))return total.x.num.Cmp(sig.r.num) == 0
}
运行上面代码后所得结果如下:
sig is Signature(r: {FieldElement{order: 115792089237316195423570985008687907852837564279074904382605163141518161494337, num: 34760265646743494300414826834746237430743723107036364618855713969497218599817}}, s:{FieldElement{order: 115792089237316195423570985008687907852837564279074904382605163141518161494337, num: 6051507193975158704644645619286490297172153466045651190239770626531632363100}})
verify signature result: true
由此我们可以看到,代码能针对明文生成签名对象,同时认证函数又能对签名对象实现准确的验证,由此可以证明我们代码实现的正确性。
更多内容请参看:
http://m.study.163.com/provider/7600199/index.htm?share=2&shareId=7600199
b 站搜索:coding 迪斯尼
公号:coding 迪斯尼