All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bcos.web3j.crypto.Sign Maven / Gradle / Ivy

There is a newer version: 2.6.6
Show newest version
package org.bcos.web3j.crypto;

import java.math.BigInteger;
import java.security.SignatureException;
import java.util.Arrays;

import org.bcos.web3j.crypto.sm2.SM2Sign;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9IntegerConverter;
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.math.ec.ECAlgorithms;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import org.bouncycastle.math.ec.custom.sec.SecP256K1Curve;

import org.bcos.web3j.utils.Numeric;

import static org.bcos.web3j.utils.Assertions.verifyPrecondition;

/**
 * 

Transaction signing logic.

* *

Adapted from the * * BitcoinJ ECKey implementation. */ public class Sign { private static SignInterface signInterface = new ECDSASign(); public static SignInterface getSignInterface() { return signInterface; } public static void setSignInterface(SignInterface signInterface) { Sign.signInterface = signInterface; } private static final X9ECParameters CURVE_PARAMS = CustomNamedCurves.getByName("secp256k1"); private static final ECDomainParameters CURVE = new ECDomainParameters( CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN(), CURVE_PARAMS.getH()); private static final BigInteger HALF_CURVE_ORDER = CURVE_PARAMS.getN().shiftRight(1); public static SignatureData signMessage(byte[] message, ECKeyPair keyPair) { return signInterface.signMessage(message,keyPair); } public static ECDSASignature sign(byte[] transactionHash, BigInteger privateKey) { ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest())); ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKey, CURVE); signer.init(true, privKey); BigInteger[] components = signer.generateSignature(transactionHash); return new ECDSASignature(components[0], components[1]).toCanonicalised(); } /** *

Given the components of a signature and a selector value, recover and return the public * key that generated the signature according to the algorithm in SEC1v2 section 4.1.6.

* *

The recId is an index from 0 to 3 which indicates which of the 4 possible keys is the * correct one. Because the key recovery operation yields multiple potential keys, the correct * key must either be stored alongside the * signature, or you must be willing to try each recId in turn until you find one that outputs * the key you are expecting.

* *

If this method returns null it means recovery was not possible and recId should be * iterated.

* *

Given the above two points, a correct usage of this method is inside a for loop from * 0 to 3, and if the output is null OR a key that is not the one you expect, you try again * with the next recId.

* * @param recId Which possible key to recover. * @param sig the R and S components of the signature, wrapped. * @param message Hash of the data that was signed. * @return An ECKey containing only the public part, or null if recovery wasn't possible. */ public static BigInteger recoverFromSignature(int recId, ECDSASignature sig, byte[] message) { verifyPrecondition(recId >= 0, "recId must be positive"); verifyPrecondition(sig.r.signum() >= 0, "r must be positive"); verifyPrecondition(sig.s.signum() >= 0, "s must be positive"); verifyPrecondition(message != null, "message cannot be null"); // 1.0 For j from 0 to h (h == recId here and the loop is outside this function) // 1.1 Let x = r + jn BigInteger n = CURVE.getN(); // Curve order. BigInteger i = BigInteger.valueOf((long) recId / 2); BigInteger x = sig.r.add(i.multiply(n)); // 1.2. Convert the integer x to an octet string X of length mlen using the conversion // routine specified in Section 2.3.7, where mlen = ⌈(log2 p)/8⌉ or mlen = ⌈m/8⌉. // 1.3. Convert the octet string (16 set binary digits)||X to an elliptic curve point R // using the conversion routine specified in Section 2.3.4. If this conversion // routine outputs “invalid”, then do another iteration of Step 1. // // More concisely, what these points mean is to use X as a compressed public key. BigInteger prime = SecP256K1Curve.q; if (x.compareTo(prime) >= 0) { // Cannot have point co-ordinates larger than this as everything takes place modulo Q. return null; } // Compressed keys require you to know an extra bit of data about the y-coord as there are // two possibilities. So it's encoded in the recId. ECPoint R = decompressKey(x, (recId & 1) == 1); // 1.4. If nR != point at infinity, then do another iteration of Step 1 (callers // responsibility). if (!R.multiply(n).isInfinity()) { return null; } // 1.5. Compute e from M using Steps 2 and 3 of ECDSA signature verification. BigInteger e = new BigInteger(1, message); // 1.6. For k from 1 to 2 do the following. (loop is outside this function via // iterating recId) // 1.6.1. Compute a candidate public key as: // Q = mi(r) * (sR - eG) // // Where mi(x) is the modular multiplicative inverse. We transform this into the following: // Q = (mi(r) * s ** R) + (mi(r) * -e ** G) // Where -e is the modular additive inverse of e, that is z such that z + e = 0 (mod n). // In the above equation ** is point multiplication and + is point addition (the EC group // operator). // // We can find the additive inverse by subtracting e from zero then taking the mod. For // example the additive inverse of 3 modulo 11 is 8 because 3 + 8 mod 11 = 0, and // -3 mod 11 = 8. BigInteger eInv = BigInteger.ZERO.subtract(e).mod(n); BigInteger rInv = sig.r.modInverse(n); BigInteger srInv = rInv.multiply(sig.s).mod(n); BigInteger eInvrInv = rInv.multiply(eInv).mod(n); ECPoint q = ECAlgorithms.sumOfTwoMultiplies(CURVE.getG(), eInvrInv, R, srInv); byte[] qBytes = q.getEncoded(false); // We remove the prefix return new BigInteger(1, Arrays.copyOfRange(qBytes, 1, qBytes.length)); } /** Decompress a compressed public key (x co-ord and low-bit of y-coord). */ private static ECPoint decompressKey(BigInteger xBN, boolean yBit) { X9IntegerConverter x9 = new X9IntegerConverter(); byte[] compEnc = x9.integerToBytes(xBN, 1 + x9.getByteLength(CURVE.getCurve())); compEnc[0] = (byte)(yBit ? 0x03 : 0x02); return CURVE.getCurve().decodePoint(compEnc); } /** * Given an arbitrary piece of text and an Ethereum message signature encoded in bytes, * returns the public key that was used to sign it. This can then be compared to the expected * public key to determine if the signature was correct. * * @param message RLP encoded message. * @param signatureData The message signature components * @return the public key used to sign the message * @throws SignatureException If the public key could not be recovered or if there was a * signature format error. */ public static BigInteger signedMessageToKey( byte[] message, SignatureData signatureData) throws SignatureException { byte[] r = signatureData.getR(); byte[] s = signatureData.getS(); verifyPrecondition(r != null && r.length == 32, "r must be 32 bytes"); verifyPrecondition(s != null && s.length == 32, "s must be 32 bytes"); int header = signatureData.getV() & 0xFF; // The header byte: 0x1B = first key with even y, 0x1C = first key with odd y, // 0x1D = second key with even y, 0x1E = second key with odd y if (header < 27 || header > 34) { throw new SignatureException("Header byte out of range: " + header); } ECDSASignature sig = new ECDSASignature( new BigInteger(1, signatureData.getR()), new BigInteger(1, signatureData.getS())); byte[] messageHash = Hash.sha3(message); int recId = header - 27; BigInteger key = recoverFromSignature(recId, sig, messageHash); if (key == null) { throw new SignatureException("Could not recover public key from signature"); } return key; } /** * Returns public key from the given private key. * * @param privKey the private key to derive the public key from * @return BigInteger encoded public key */ public static BigInteger publicKeyFromPrivate(BigInteger privKey) { ECPoint point = publicPointFromPrivate(privKey); byte[] encoded = point.getEncoded(false); return new BigInteger(1, Arrays.copyOfRange(encoded, 1, encoded.length)); // remove prefix } /** * Returns public key point from the given private key. */ private static ECPoint publicPointFromPrivate(BigInteger privKey) { /* * TODO: FixedPointCombMultiplier currently doesn't support scalars longer than the group * order, but that could change in future versions. */ if (privKey.bitLength() > CURVE.getN().bitLength()) { privKey = privKey.mod(CURVE.getN()); } return new FixedPointCombMultiplier().multiply(CURVE.getG(), privKey); } public static class ECDSASignature { public final BigInteger r; public final BigInteger s; 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 Sign#HALF_CURVE_ORDER}. See * * BIP62. */ 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; } } } public static class SignatureData { private final byte v; private final byte[] r; private final byte[] s; private final byte[] pub; public SignatureData(byte v, byte[] r, byte[] s) { this.v = v; this.r = r; this.s = s; pub = null; } public SignatureData(byte v, byte[] r, byte[] s,byte[] pub) { this.v = v; this.r = r; this.s = s; this.pub = pub; } public byte getV() { return v; } public byte[] getR() { return r; } public byte[] getS() { return s; } public byte[] getPub() { return pub; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } SignatureData that = (SignatureData) o; if (v != that.v) { return false; } if (!Arrays.equals(r, that.r)) { return false; } return Arrays.equals(s, that.s); } @Override public int hashCode() { int result = (int) v; result = 31 * result + Arrays.hashCode(r); result = 31 * result + Arrays.hashCode(s); result = 63 * result + Arrays.hashCode(pub); return result; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy