C#下ECDsa签名、验签
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                C#下ECDsa签名、验签
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.                        
                                因?yàn)闃I(yè)務(wù)需要,與第三方對(duì)接時(shí),第三方簽名方法居然采用的ECDsa,而不是更常見(jiàn)的RSA、MD5之類(lèi),真是不走尋常路,當(dāng)然我是不會(huì)承認(rèn)是我見(jiàn)識(shí)太少的!!!
麻利的讓對(duì)方提供了簽名算法代碼,奈何對(duì)方是java,提供的也是java版本代碼,具體代碼如下:
/*** Project Name:trustsql_sdk* File Name:ECDSAAlgoUtil.java* Package Name:com.tencent.trustsql.sdk.algo* Date:Jul 26, 20175:17:04 PM* Copyright (c) 2017, Tencent All Rights Reserved.**/package com.tencent.trustsql.sdk.algorithm;import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; import java.security.MessageDigest; import java.security.SecureRandom;import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Hex; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.DERSequenceGenerator; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.signers.ECDSASigner; import org.bouncycastle.crypto.signers.HMacDSAKCalculator; import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; import org.bouncycastle.math.ec.ECPoint; import org.spongycastle.asn1.ASN1InputStream; import org.spongycastle.asn1.ASN1Integer; import org.spongycastle.asn1.DERSequenceGenerator; import org.spongycastle.asn1.DLSequence; import org.spongycastle.asn1.x9.X9ECParameters; import org.spongycastle.crypto.digests.SHA256Digest; import org.spongycastle.crypto.ec.CustomNamedCurves; import org.spongycastle.crypto.params.ECDomainParameters; import org.spongycastle.crypto.params.ECPrivateKeyParameters; import org.spongycastle.crypto.params.ECPublicKeyParameters; import org.spongycastle.crypto.signers.ECDSASigner; import org.spongycastle.crypto.signers.HMacDSAKCalculator; import org.spongycastle.math.ec.FixedPointUtil;import com.google.common.base.Objects; import com.tencent.trustsql.sdk.Constants;/*** * @author sagazhang**/ public class ECDSAAlgorithm {public static final ECDomainParameters CURVE;public static final BigInteger HALF_CURVE_ORDER;static {X9ECParameters CURVE_PARAMS = CustomNamedCurves.getByName("secp256k1");// Tell Bouncy Castle to precompute data that's needed during secp256k1// calculations. Increasing the width// number makes calculations faster, but at a cost of extra memory usage// and with decreasing returns. 12 was// picked after consulting with the BC team.FixedPointUtil.precompute(CURVE_PARAMS.getG(), 12);CURVE = new ECDomainParameters(CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN(),CURVE_PARAMS.getH());HALF_CURVE_ORDER = CURVE_PARAMS.getN().shiftRight(1);}public static String generatePrivateKey() throws Exception {SecureRandom secureRandom;try {secureRandom = SecureRandom.getInstance(Constants.RANDOM_NUMBER_ALGORITHM,Constants.RANDOM_NUMBER_ALGORITHM_PROVIDER);} catch (Exception e) {secureRandom = new SecureRandom();}// Generate the key, skipping as many as desired.byte[] privateKeyAttempt = new byte[32];secureRandom.nextBytes(privateKeyAttempt);BigInteger privateKeyCheck = new BigInteger(1, privateKeyAttempt);while (privateKeyCheck.compareTo(BigInteger.ZERO) == 0 || privateKeyCheck.compareTo(Constants.MAXPRIVATEKEY) == 1) {secureRandom.nextBytes(privateKeyAttempt);privateKeyCheck = new BigInteger(1, privateKeyAttempt);}// System.out.println(Hex.encodeHexString(privateKeyAttempt));String result = Base64.encodeBase64String(privateKeyAttempt);result = result.replaceAll("[\\s*\t\n\r]", "");return result;}public static String generatePublicKey(String priateKeyBase64String, boolean encode) throws Exception {try {byte[] privateKeyBytes = Base64.decodeBase64(priateKeyBase64String);ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");ECPoint pointQ = spec.getG().multiply(new BigInteger(1, privateKeyBytes));String result = Base64.encodeBase64String(pointQ.getEncoded(encode));result = result.replaceAll("[\\s*\t\n\r]", "");return result;} catch (Exception e) {throw new RuntimeException(e);}}public static String generatePublicKey(String priateKeyBase64String) throws Exception {return generatePublicKey(priateKeyBase64String, false);}public static String decodePublicKey(String encodePubKeyBase64String) throws Exception {try {byte[] encodePubkeyBytes = Base64.decodeBase64(encodePubKeyBase64String);ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");ECPoint pointQ = spec.getG().getCurve().decodePoint(encodePubkeyBytes);String result = Base64.encodeBase64String(pointQ.getEncoded(false));result = result.replaceAll("[\\s*\t\n\r]", "");return result;} catch(Exception e) {throw new RuntimeException(e);}}public static String getAddress(byte[] keyBytes, int...version) throws Exception {byte[] hashSha256 = BaseAlgorithm.encode("SHA-256", keyBytes);MessageDigest messageDigest = MessageDigest.getInstance("RipeMD160");messageDigest.update(hashSha256);byte[] hashRipeMD160 = messageDigest.digest();byte[] versionHashBytes = new byte[1 + hashRipeMD160.length];if(version == null || version.length == 0) {versionHashBytes[0] = 0;} else {versionHashBytes[0] = (byte) version[0];}System.arraycopy(hashRipeMD160, 0, versionHashBytes, 1, hashRipeMD160.length);byte[] checkSumBytes = BaseAlgorithm.encodeTwice("SHA-256", versionHashBytes);byte[] rawAddr = new byte[versionHashBytes.length + 4];System.arraycopy(versionHashBytes, 0, rawAddr, 0, versionHashBytes.length);System.arraycopy(checkSumBytes, 0, rawAddr, versionHashBytes.length, 4); // byte[] hashRipeMD160 = messageDigest.digest(); // byte[] hashDoubleSha256 = BaseAlgorithm.encodeTwice("SHA-256", hashRipeMD160); // byte[] rawAddr = new byte[1 + hashRipeMD160.length + 4]; // rawAddr[0] = 0; // System.arraycopy(hashRipeMD160, 0, rawAddr, 1, hashRipeMD160.length); // System.arraycopy(hashDoubleSha256, 0, rawAddr, hashRipeMD160.length + 1, 4);return Base58Algorithm.encode(rawAddr);}public static String sign(String privateKey, byte[] data, boolean isHash256) throws Exception {byte[] hash256 = data;if(!isHash256) { //未hash256 encode的字節(jié)數(shù)組hash256 = BaseAlgorithm.encode("SHA-256", data);}ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest()));//ECDSASigner signer = new ECDSASigner();BigInteger pri = new BigInteger(1, Base64.decodeBase64(privateKey));ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(pri, CURVE);signer.init(true, privKey);BigInteger[] components = signer.generateSignature(hash256);byte[] content = new ECDSASignature(components[0], components[1]).toCanonicalised().encodeToDER();String result = Base64.encodeBase64String(content);result = result.replaceAll("[\\s*\t\n\r]", "");return result;}public static String sign(String privateKey, byte[] data) throws Exception {byte[] hash256 = data;ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest()));BigInteger pri = new BigInteger(1, Base64.decodeBase64(privateKey));ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(pri, CURVE);signer.init(true, privKey);BigInteger[] components = signer.generateSignature(hash256);byte[] content = new ECDSASignature(components[0], components[1]).toCanonicalised().encodeToDER();String result = Base64.encodeBase64String(content);result = result.replaceAll("[\\s*\t\n\r]", "");return result;}public static boolean verify(String srcStr,String sign, String pubKey) throws Exception {byte[] hash256 = BaseAlgorithm.encode("SHA-256", srcStr.getBytes("UTF-8"));ECDSASignature eCDSASignature = ECDSASignature.decodeFromDER(Base64.decodeBase64(sign));ECDSASigner signer = new ECDSASigner();org.spongycastle.math.ec.ECPoint pub = CURVE.getCurve().decodePoint(Base64.decodeBase64(pubKey));ECPublicKeyParameters params = new ECPublicKeyParameters(CURVE.getCurve().decodePoint(pub.getEncoded()), CURVE);signer.init(false, params);return signer.verifySignature(hash256, eCDSASignature.r, eCDSASignature.s);}public static boolean verify(String srcStr,boolean isHash256, String sign, String pubKey) throws Exception {byte[] hash256 = srcStr.getBytes("UTF-8");if(!isHash256) {hash256 = BaseAlgorithm.encode("SHA-256", srcStr.getBytes("UTF-8"));}ECDSASignature eCDSASignature = ECDSASignature.decodeFromDER(Base64.decodeBase64(sign));ECDSASigner signer = new ECDSASigner();org.spongycastle.math.ec.ECPoint pub = CURVE.getCurve().decodePoint(Base64.decodeBase64(pubKey));ECPublicKeyParameters params = new ECPublicKeyParameters(CURVE.getCurve().decodePoint(pub.getEncoded()), CURVE);signer.init(false, params);return signer.verifySignature(hash256, eCDSASignature.r, eCDSASignature.s);}public static boolean verify(byte[] bytes, boolean isHash256, String sign, String pubKey) throws Exception {byte[] hash256 = bytes;if(!isHash256) {hash256 = BaseAlgorithm.encode("SHA-256", bytes);}ECDSASignature eCDSASignature = ECDSASignature.decodeFromDER(Base64.decodeBase64(sign));ECDSASigner signer = new ECDSASigner();org.spongycastle.math.ec.ECPoint pub = CURVE.getCurve().decodePoint(Base64.decodeBase64(pubKey));ECPublicKeyParameters params = new ECPublicKeyParameters(CURVE.getCurve().decodePoint(pub.getEncoded()), CURVE);signer.init(false, params);return signer.verifySignature(hash256, eCDSASignature.r, eCDSASignature.s);}public static boolean VerifyRenSign(String pubKey, byte[] bytes, String sign) {byte[] hash256 = bytes;//byte[] hash256 = BaseAlgorithm.encode("SHA-256", bytes);ECDSASignature eCDSASignature = ECDSASignature.decodeFromDER(Base64.decodeBase64(sign));ECDSASigner signer = new ECDSASigner();org.spongycastle.math.ec.ECPoint pub = CURVE.getCurve().decodePoint(Base64.decodeBase64(pubKey));ECPublicKeyParameters params = new ECPublicKeyParameters(CURVE.getCurve().decodePoint(pub.getEncoded()), CURVE);signer.init(false, params);return signer.verifySignature(hash256, eCDSASignature.r, eCDSASignature.s);}public static class ECDSASignature {/** The two components of the signature. */public final BigInteger r, s;/*** Constructs a signature with the given components. Does NOT* automatically canonicalise the signature.*/public ECDSASignature(BigInteger r, BigInteger s) {this.r = r;this.s = s;}/*** Returns true if the S component is "low", that means it is below* {@link ECKey#HALF_CURVE_ORDER}. See <a href=* "https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#Low_S_values_in_signatures">* BIP62</a>.*/public boolean isCanonical() {return s.compareTo(HALF_CURVE_ORDER) <= 0;}/*** Will automatically adjust the S component to be less than or equal to* half the curve order, if necessary. This is required because for* every signature (r,s) the signature (r, -s (mod N)) is a valid* signature of the same message. However, we dislike the ability to* modify the bits of a Bitcoin transaction after it's been signed, as* that violates various assumed invariants. Thus in future only one of* those forms will be considered legal and the other will be banned.*/public ECDSASignature toCanonicalised() {if (!isCanonical()) {// The order of the curve is the number of valid points that// exist on that curve. If S is in the upper// half of the number of valid points, then bring it back to the// lower half. Otherwise, imagine that// N = 10// s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) are// valid solutions.// 10 - 8 == 2, giving us always the latter solution, which is// canonical.return new ECDSASignature(r, CURVE.getN().subtract(s));} else {return this;}}/*** DER is an international standard for serializing data structures* which is widely used in cryptography. It's somewhat like protocol* buffers but less convenient. This method returns a standard DER* encoding of the signature, as recognized by OpenSSL and other* libraries.*/public byte[] encodeToDER() {try {return derByteStream().toByteArray();} catch (IOException e) {throw new RuntimeException(e); // Cannot happen.}}public static ECDSASignature decodeFromDER(byte[] bytes) {ASN1InputStream decoder = null;try {decoder = new ASN1InputStream(bytes);DLSequence seq = (DLSequence) decoder.readObject();if (seq == null)throw new RuntimeException("Reached past end of ASN.1 stream.");ASN1Integer r, s;try {r = (ASN1Integer) seq.getObjectAt(0);s = (ASN1Integer) seq.getObjectAt(1);} catch (ClassCastException e) {throw new IllegalArgumentException(e);}// OpenSSL deviates from the DER spec by interpreting these// values as unsigned, though they should not be// Thus, we always use the positive versions. See:// http://r6.ca/blog/20111119T211504Z.htmlreturn new ECDSASignature(r.getPositiveValue(), s.getPositiveValue());} catch (IOException e) {throw new RuntimeException(e);} finally {if (decoder != null)try {decoder.close();} catch (IOException x) {}}}protected ByteArrayOutputStream derByteStream() throws IOException {// Usually 70-72 bytes.ByteArrayOutputStream bos = new ByteArrayOutputStream(72);DERSequenceGenerator seq = new DERSequenceGenerator(bos);seq.addObject(new ASN1Integer(r));seq.addObject(new ASN1Integer(s));seq.close();return bos;}@Overridepublic boolean equals(Object o) {if (this == o)return true;if (o == null || getClass() != o.getClass())return false;ECDSASignature other = (ECDSASignature) o;return r.equals(other.r) && s.equals(other.s);}@Overridepublic int hashCode() {return Objects.hashCode(r, s);}} }好家伙,用的還是Tencent的sdk,而且一如RSA一樣,對(duì)方提供的公鑰私鑰果然還是base64格式,根本無(wú)法應(yīng)用于C#下的ECDsaCng,好在之前已經(jīng)做過(guò)了大量BouncyCastle相關(guān)的代碼,雖然略有波折,但還是成功的將java代碼改造成了C#代碼,而過(guò)程中發(fā)現(xiàn)java下的ASN1Integer,實(shí)際與C#下的DerInteger對(duì)應(yīng),這樣如果后續(xù)還有其它基于BouncyCastle的跨語(yǔ)言對(duì)接,應(yīng)該也可以減少一些對(duì)接中的阻礙
using Org.BouncyCastle.Asn1;using Org.BouncyCastle.Crypto.Digests;using Org.BouncyCastle.Crypto.EC;using Org.BouncyCastle.Crypto.Parameters;using Org.BouncyCastle.Crypto.Signers;using Org.BouncyCastle.Math;using System.IO;/// <summary>/// ECDsa簽名驗(yàn)證算法/// </summary>public static class ECDsaHelper{private static readonly ECDomainParameters Curve;private static readonly BigInteger HalfCurveOrder;static ECDsaHelper(){var curveParams = CustomNamedCurves.GetByName("secp256k1");Curve = new ECDomainParameters(curveParams.Curve, curveParams.G, curveParams.N, curveParams.H);HalfCurveOrder = curveParams.N.ShiftRight(1);}/// <summary>/// 簽名/// </summary>/// <param name="privateKey">ECDsa私鑰</param>/// <param name="content">待簽名的內(nèi)容</param>/// <returns></returns>public static byte[] SignData(byte[] privateKey, byte[] content){//為了保證簽名結(jié)果與java一致,此處參數(shù)同樣傳遞了IDsaKCalculator,但測(cè)試發(fā)現(xiàn)如果不傳雖然簽名結(jié)果不一致,但驗(yàn)簽是可以通過(guò)的var signer = new ECDsaSigner(new HMacDsaKCalculator(new Sha256Digest()));var priKey = new ECPrivateKeyParameters(new BigInteger(1, privateKey), Curve);signer.Init(true, priKey);var components = signer.GenerateSignature(content);using (var stream = new MemoryStream()){var seq = new DerSequenceGenerator(stream);seq.AddObject(new DerInteger(components[0]));var s = components[1];if (s.CompareTo(HalfCurveOrder) > 0){s = Curve.N.Subtract(components[1]);}seq.AddObject(new DerInteger(s));seq.Close();var signature = stream.ToArray();return signature;}}/// <summary>/// 驗(yàn)簽/// </summary>/// <param name="publicKey">ECDsa公鑰</param>/// <param name="content">待驗(yàn)簽的內(nèi)容</param>/// <param name="signature">待比較的簽名結(jié)果</param>/// <returns></returns>public static bool VerifyData(byte[] publicKey, byte[] content, byte[] signature){using (var input = new Asn1InputStream(signature)){var seq = (DerSequence)input.ReadObject();if (seq != null){var signer = new ECDsaSigner();var point = Curve.Curve.DecodePoint(publicKey);var pubKey = new ECPublicKeyParameters(Curve.Curve.DecodePoint(point.GetEncoded()), Curve);signer.Init(false, pubKey);return signer.VerifySignature(content, ((DerInteger)seq[0]).Value, ((DerInteger)seq[1]).Value);}}return false;}}就兩個(gè)方法,也就不提供測(cè)試代碼了,反正簽名的邏輯是沒(méi)看懂,只是依樣畫(huà)葫蘆的抄了java的邏輯。
總結(jié)
以上是生活随笔為你收集整理的C#下ECDsa签名、验签的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: hadoop不在sudoers文件中。此
- 下一篇: iOS 增量代码覆盖率检测实践
