Java
public class SM2Util {
private SM2Util() {
}
private static final BigInteger ECC_P = new BigInteger(
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16);
private static final BigInteger ECC_A = new BigInteger(
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16);
private static final BigInteger ECC_B = new BigInteger(
"28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16);
private static final BigInteger ECC_N = new BigInteger(
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16);
private static final BigInteger ECC_X = new BigInteger(
"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16);
private static final BigInteger ECC_Y = new BigInteger(
"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16);
private static final BigInteger[] ECC_POINT = new BigInteger[] { ECC_X, ECC_Y };
private static final Random random = new Random();
private static final BigInteger THREE = BigInteger.valueOf(3);
public static String[] createkey() {
BigInteger privateKey;
do {
privateKey = new BigInteger(ECC_N.bitLength(), random);
} while (privateKey.compareTo(BigInteger.ONE) <= 0
|| privateKey.compareTo(ECC_N) > 0);
BigInteger[] multiPoint = multiPoints(ECC_POINT, privateKey);
return new String[] { ArrayUtils.bytesToHex(privateKey.toByteArray()),
ArrayUtils.bytesToHex(multiPoint[0].toByteArray()) +
ArrayUtils.bytesToHex(multiPoint[1].toByteArray()) };
}
private static BigInteger[] multiPoints(BigInteger[] pointOnes, BigInteger pointTwo) {
BigInteger point = pointTwo;
BigInteger[] multiPoint = { BigInteger.ZERO, BigInteger.ZERO };
BigInteger[] pointOne = new BigInteger[] { pointOnes[0], pointOnes[1] };
int i = 0;
while (BigInteger.ZERO.compareTo(point) < 0) {
if (point.mod(BigInteger.TWO).equals(BigInteger.ONE)) {
addPoint(multiPoint, pointOne);
}
BigInteger temp = (pointOne[0].pow(2).multiply(THREE)).add(ECC_A)
.multiply(pointOne[1].multiply(BigInteger.TWO)
.modPow(ECC_P.subtract(BigInteger.TWO), ECC_P));
BigInteger x = (temp.pow(2).subtract(pointOne[0].multiply(BigInteger.TWO))).mod(ECC_P);
pointOne[1] = (temp.multiply(pointOne[0].subtract(x)).subtract(pointOne[1]))
.mod(ECC_P);
pointOne[0] = x;
point = point.divide(BigInteger.TWO);
}
return multiPoint;
}
private static void addPoint(BigInteger[] pointOne, BigInteger[] pointTwo) {
if ((BigInteger.ZERO.equals(pointTwo[0]) && BigInteger.ZERO.equals(pointTwo[1]))
|| (BigInteger.ZERO.equals(pointOne[0]) && BigInteger.ZERO.equals(pointOne[1]))) {
pointOne[0] = pointOne[0].add(pointTwo[0]);
pointOne[1] = pointOne[1].add(pointTwo[1]);
} else {
BigInteger temp = pointOne[1].subtract(pointTwo[1])
.multiply((pointOne[0].subtract(pointTwo[0]))
.modPow(ECC_P.subtract(BigInteger.TWO), ECC_P));
BigInteger x = (temp.pow(2).subtract(pointOne[0]).subtract(pointTwo[0])).mod(ECC_P);
pointOne[1] = (temp.multiply(pointOne[0].subtract(x))).subtract(pointOne[1])
.mod(ECC_P);
pointOne[0] = x;
}
}
public boolean validatePublicKey(BigInteger a, BigInteger b, BigInteger p, BigInteger xP, BigInteger yP) {
if (xP.equals(BigInteger.ZERO) && yP.equals(BigInteger.ZERO)) {
return false;
}
if (!xP.mod(p).equals(xP) || !yP.mod(p).equals(yP)) {
return false;
}
BigInteger numerator = (xP.pow(3).add(a.multiply(xP)).add(b)).mod(p);
return numerator.equals(yP.pow(2).mod(p));
}
public static String encryption(String text, String publicKey) {
String texts = ArrayUtils.bytesToHex(text.getBytes(StandardCharsets.UTF_8));
BigInteger texInteger = new BigInteger(texts, 16);
int textLength = texts.length();
int length = publicKey.length();
int lengthTwo = length >> 1;
BigInteger[] publicKeyArr = { new BigInteger(publicKey.substring(0, lengthTwo), 16),
new BigInteger(publicKey.substring(lengthTwo, length), 16) };
BigInteger randomK;
BigInteger num;
BigInteger[] C1Arr = null;
BigInteger[] pointArr = null;
int ecclength = ECC_N.bitLength();
String pointArr_0 = "";
String pointArr_1 = "";
do {
do {
randomK = new BigInteger(ecclength, random);
} while (randomK.compareTo(BigInteger.ONE) <= 0
|| randomK.compareTo(ECC_N) > 0);
C1Arr = multiPoints(ECC_POINT, randomK);
pointArr = multiPoints(publicKeyArr, randomK);
pointArr_0 = pointArr[0].toString(16);
pointArr_1 = pointArr[1].toString(16);
num = new BigInteger(kdf(pointArr_0 + pointArr_1, textLength), 16);
} while (BigInteger.ZERO.equals(num));
String C2 = (texInteger.xor(num)).toString(16);
String C3 = pointArr_0 + texInteger.toString(16) + pointArr_1;
String C11 = C1Arr[0].toString(16);
String C12 = C1Arr[1].toString(16);
return textLength + "" + C11.length() + "" + C12.length() + C11 + C12 + C2 + SM3Util.encryption(C3);
}
public static String kdf(String bitnum, int klen) {
int v = 64;
int count = (int) Math.ceil(klen / (v * 1.0));
StringBuilder k = new StringBuilder();
for (int i = 0; i < count; i++) {
StringBuilder bitString = new StringBuilder(bitnum);
String ks = Integer.toHexString(i + 1);
for (int j = 32 - ks.length(); j >= 0; j--) {
bitString.append("0");
}
bitString.append(ks);
k.append(SM3Util.encryption(bitString.toString()));
}
return k.substring(0, klen);
}
public static String decrypt(String ciphertext, String privateKey) {
Integer bitlength = Integer.parseInt(ciphertext.substring(0, 2));
Integer C11 = Integer.parseInt(ciphertext.substring(2, 4));
Integer C12 = Integer.parseInt(ciphertext.substring(4, 6));
Integer C = C11 + C12;
ciphertext = ciphertext.substring(6);
int length = ciphertext.length() - 64;
String C1 = ciphertext.substring(0, C);
String C2 = ciphertext.substring(C, length);
String C3 = ciphertext.substring(length);
BigInteger[] C1Arr = { new BigInteger(C1.substring(0, C11), 16),
new BigInteger(C1.substring(C11), 16) };
BigInteger left = C1Arr[1].modPow(BigInteger.TWO, ECC_P);
BigInteger right = (C1Arr[0].modPow(THREE, ECC_P).add((ECC_A.multiply(C1Arr[0]))).add(ECC_B))
.mod(ECC_P);
if (!left.equals(right)) {
throw new IllegalArgumentException("验证C1是否满足椭圆曲线方程");
}
BigInteger[] C2Arr = multiPoints(C1Arr, new BigInteger(privateKey, 16));
String C2Arr_0 = C2Arr[0].toString(16);
String C2Arr_1 = C2Arr[1].toString(16);
BigInteger tInteger = new BigInteger(kdf(C2Arr_0 + C2Arr_1, bitlength), 16);
if (tInteger.equals(BigInteger.ZERO)) {
throw new IllegalArgumentException("S是无穷远点");
}
BigInteger Mm = new BigInteger(C2, 16).xor(tInteger);
if (!C3.equals(SM3Util.encryption(C2Arr_0 + Mm.toString(16) + C2Arr_1))) {
throw new IllegalArgumentException("验证失败");
}
return new String(ArrayUtils.hexToByte(Mm.toString(16)), StandardCharsets.UTF_8);
}
public static String sing(String message, String privateKey) {
String msg = ArrayUtils.bytesToHex(message.getBytes(StandardCharsets.UTF_8));
BigInteger privateKeyInt = new BigInteger(privateKey, 16);
BigInteger[] multiPoint = multiPoints(ECC_POINT,
privateKeyInt);
String ZA = SM3Util
.encryption(Integer.toHexString(msg.length()) + msg + ECC_A.toString(16)
+ ECC_B.toString(16)
+ ECC_X.toString(16) + ECC_Y.toString(16)
+ ArrayUtils.bytesToHex(multiPoint[0].toByteArray())
+ ArrayUtils.bytesToHex(multiPoint[1].toByteArray()));
BigInteger e = new BigInteger(SM3Util.encryption(ZA + msg), 16);
BigInteger k;
BigInteger[] multiPointTwo;
BigInteger r;
BigInteger s;
do {
do {
do {
k = new BigInteger(ECC_N.bitLength(), random);
} while (k.compareTo(BigInteger.ONE) <= 0
|| k.compareTo(ECC_N) > 0);
multiPointTwo = multiPoints(ECC_POINT, k);
r = (e.add(multiPointTwo[0])).mod(ECC_N);
} while (BigInteger.ZERO.equals(k) || ECC_N.equals(r.add(k)));
s = BigInteger.ONE.add(privateKeyInt).modInverse(ECC_N)
.multiply(k.subtract(r.multiply(privateKeyInt))).mod(ECC_N);
} while (BigInteger.ZERO.equals(s));
String rString = r.toString(16);
return rString.length() + rString + s.toString(16);
}
public static boolean verify(String message, String sing, String publicKey) {
String msg = ArrayUtils.bytesToHex(message.getBytes(StandardCharsets.UTF_8));
int lengths = Integer.parseInt(sing.substring(0, 2)) + 2;
BigInteger rInt = new BigInteger(sing.substring(2, lengths), 16);
BigInteger sInt = new BigInteger(sing.substring(lengths), 16);
int lengthTwo = publicKey.length() >> 1;
BigInteger[] publicKeyArr = { new BigInteger(publicKey.substring(0, lengthTwo), 16),
new BigInteger(publicKey.substring(lengthTwo), 16) };
String ZA = SM3Util.encryption(Integer.toHexString(msg.length()) + msg + ECC_A.toString(16) +
ECC_B.toString(16) + ECC_X.toString(16) + ECC_Y.toString(16) + publicKey);
BigInteger e = new BigInteger(SM3Util.encryption(ZA + msg), 16);
BigInteger t = (rInt.add(sInt)).mod(ECC_N);
BigInteger[] multiPointTwo1 = multiPoints(ECC_POINT, sInt);
BigInteger[] multiPointTwo2 = multiPoints(publicKeyArr, t);
BigInteger[] result = addPoints(multiPointTwo1, multiPointTwo2);
BigInteger R = e.add(result[0]).mod(ECC_N);
return R.equals(rInt);
}
public static BigInteger[] addPoints(BigInteger[] P1, BigInteger[] P2) {
if (P1[0].equals(BigInteger.ZERO) && P1[1].equals(BigInteger.ZERO)) {
return P2;
}
if (P2[0].equals(BigInteger.ZERO) && P2[1].equals(BigInteger.ZERO)) {
return P1;
}
BigInteger lambda;
if (P1[0].equals(P2[0]) && P1[1].negate().equals(P2[2])) {
return new BigInteger[] { BigInteger.ZERO, BigInteger.ZERO };
} else {
if (P1[0].equals(P2[0])) {
lambda = (P1[1].multiply(P1[1]).multiply(BigInteger.TWO)).modInverse(ECC_P)
.multiply((P1[0].multiply(THREE)).add(ECC_A))
.mod(ECC_P);
} else {
lambda = (P2[1].subtract(P1[1])).multiply(P2[0].subtract(P1[0]).modInverse(ECC_P))
.mod(ECC_P);
}
}
BigInteger x3 = (lambda.multiply(lambda).subtract(P1[0]).subtract(P2[0])).mod(ECC_P);
BigInteger y3 = (lambda.multiply(P1[0].subtract(x3)).subtract(P1[1])).mod(ECC_P);
return new BigInteger[] { x3, y3 };
}
public static String[] consultB1(String dB, String RA, String PA, String PB, String IDA,
String IDB) {
BigInteger rB;
do {
rB = new BigInteger(ECC_N.bitLength(), random);
} while (rB.compareTo(BigInteger.ONE) <= 0
|| rB.compareTo(ECC_N) > 0);
BigInteger[] multiPoint = multiPoints(ECC_POINT, rB);
int w = 127;
BigInteger xBar2 = countX2(multiPoint[0], w);
BigInteger tB = new BigInteger(dB, 16).add(xBar2.multiply(rB)).mod(ECC_N);
BigInteger[] RAPoint = { new BigInteger(RA.substring(0, RA.length() / 2), 16),
new BigInteger(RA.substring(RA.length() / 2), 16) };
BigInteger x1Bar2 = countX2(RAPoint[0], w);
BigInteger[] xRA = multiPoints(RAPoint, x1Bar2);
BigInteger[] PAxRA = addPoints(RAPoint, xRA);
BigInteger[] V = multiPoints(PAxRA, tB);
String pString = ECC_A.toString(16) + ECC_B.toString(16) + ECC_X.toString(16) + ECC_Y.toString(16);
String ZA = SM3Util
.encryption(ArrayUtils.bytesToHex(IDA.getBytes(StandardCharsets.UTF_8)) + pString
+ PA.substring(0, PA.length() / 2)
+ PA.substring(PA.length() / 2));
String ZB = SM3Util
.encryption(ArrayUtils.bytesToHex(IDB.getBytes(StandardCharsets.UTF_8)) + pString
+ PB.substring(0, PB.length() / 2)
+ PB.substring(PB.length() / 2));
String KBString = kdf(V[0].toString(16) + V[1].toString(16) + ZA + ZB, 32);
String k = SM3Util
.encryption(V[0].toString(16) + ZA + ZB + RA + multiPoint[0].toString(16)
+ multiPoint[1].toString(16));
return new String[] { SM3Util.encryption("02" + V[1].toString(16) + k),
multiPoint[0].toString(16) + multiPoint[1].toString(16), KBString };
}
public static BigInteger countX2(BigInteger x, int w) {
BigInteger twoToThePowerOfW = BigInteger.TWO.pow(w);
BigInteger mask = twoToThePowerOfW.subtract(BigInteger.ONE);
BigInteger bitwiseAndResult = x.and(mask);
return twoToThePowerOfW.add(bitwiseAndResult);
}
public static String consultA1(String dA, String rA, String RA, String RB, String PA, String PB, String IDA,
String IDB,
String SB) {
int w = 127;
BigInteger x1 = new BigInteger(RA.substring(0, RA.length() / 2), 16);
BigInteger x1Bar2 = countX2(x1, w);
BigInteger privateKey = new BigInteger(rA, 16);
BigInteger tA = new BigInteger(dA, 16).add(x1Bar2.multiply(privateKey)).mod(ECC_N);
BigInteger[] RBPoint = { new BigInteger(RB.substring(0, RB.length() / 2), 16),
new BigInteger(RB.substring(RB.length() / 2), 16) };
BigInteger xBar2 = countX2(RBPoint[0], w);
BigInteger[] xRB = multiPoints(RBPoint, xBar2);
String PBx = PB.substring(0, PB.length() / 2);
String PBy = PB.substring(PB.length() / 2);
BigInteger[] PAxRA = addPoints(new BigInteger[] { new BigInteger(PBx, 16),
new BigInteger(PBy, 16) }, xRB);
BigInteger[] U = multiPoints(PAxRA, tA);
String pString = ECC_A.toString(16) + ECC_B.toString(16) + ECC_X.toString(16) + ECC_Y.toString(16);
String ZA = SM3Util
.encryption(ArrayUtils.bytesToHex(IDA.getBytes(StandardCharsets.UTF_8)) + pString
+ PA.substring(0, PA.length() / 2)
+ PA.substring(PA.length() / 2));
String ZB = SM3Util.encryption(
ArrayUtils.bytesToHex(IDB.getBytes(StandardCharsets.UTF_8)) + pString + PBx + PBy);
String KBString = kdf(U[0].toString(16) + U[1].toString(16) + ZA + ZB, 32);
String k = SM3Util.encryption(U[0].toString(16) + ZA + ZB + RA + RB);
String S1 = SM3Util.encryption("02" + U[1].toString(16) + k);
if (S1.equals(SB)) {
return KBString;
}
return "";
}
}
TS
export class SM2Utils {
public static ECC_P: bigint =
0xfffffffeffffffffffffffffffffffffffffffff00000000ffffffffffffffffn;
private static ECC_A: bigint =
0xfffffffeffffffffffffffffffffffffffffffff00000000fffffffffffffffcn;
private static ECC_B: bigint =
0x28e9fa9e9d9f5e344d5a9e4bcf6509a7f39789f515ab8f92ddbcbd414d940e93n;
private static ECC_N: bigint =
0xfffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123n;
private static ECC_X: bigint =
0x32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7n;
private static ECC_Y: bigint =
0xbc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0n;
private static ECC_POINT: [bigint, bigint] = [this.ECC_X, this.ECC_Y];
private static ZERO: bigint = 0n;
private static ONE: bigint = 1n;
private static TWO: bigint = 2n;
private static THREE: bigint = 3n;
constructor() { }
public static createKey(): [string, string] {
let privateKey: bigint;
do {
privateKey = BigInt(
Math.floor(Math.random() * (Number(this.ECC_N) - 2)) + 1
);
} while (privateKey <= 1n || privateKey > this.ECC_N - 2n);
const multiPoint: [bigint, bigint] = this.multiPoints(
this.ECC_POINT,
privateKey
);
return [
privateKey.toString(16),
multiPoint[0].toString(16) + multiPoint[1].toString(16),
];
}
private static multiPoints(
pointOnes: [bigint, bigint],
pointTwo: bigint
): [bigint, bigint] {
let point: bigint = pointTwo;
const multiPoint: [bigint, bigint] = [0n, 0n];
const pointOne: [bigint, bigint] = [pointOnes[0], pointOnes[1]];
while (0n < point) {
if (point % 2n === 1n) {
this.addPoint(multiPoint, pointOne);
}
const temp: bigint =
(pointOne[0] ** 2n * 3n + this.ECC_A) *
this.modPow(pointOne[1] * 2n, this.ECC_P - this.TWO, this.ECC_P);
const x: bigint = this.mod(temp ** 2n - pointOne[0] * 2n, this.ECC_P);
pointOne[1] = this.mod(
temp * (pointOne[0] - x) - pointOne[1],
this.ECC_P
);
pointOne[0] = x;
point = point / 2n;
}
return multiPoint;
}
private static addPoint(
pointOne: [bigint, bigint],
pointTwo: [bigint, bigint]
): void {
if (
(pointTwo[0] === this.ZERO && pointTwo[1] === this.ZERO) ||
(pointOne[0] === this.ZERO && pointOne[1] === this.ZERO)
) {
pointOne[0] += pointTwo[0];
pointOne[1] += pointTwo[1];
} else {
const temp1: bigint = pointOne[1] - pointTwo[1];
const temp2: bigint = pointOne[0] - pointTwo[0];
const temp3: bigint = this.modPow(
temp2,
this.ECC_P - this.TWO,
this.ECC_P
);
const temp: bigint = temp1 * temp3;
const x: bigint = this.mod(
temp ** 2n - pointOne[0] - pointTwo[0],
this.ECC_P
);
pointOne[1] = this.mod(
temp * (pointOne[0] - x) - pointOne[1],
this.ECC_P
);
pointOne[0] = x;
}
}
private static mod(base: bigint, modulus: bigint): bigint {
return ((base % modulus) + modulus) % modulus;
}
private static modPow(
base: bigint,
exponent: bigint,
modulus: bigint
): bigint {
let result = BigInt(1);
base %= modulus;
while (exponent > BigInt(0)) {
if (exponent % 2n === BigInt(1)) {
result = this.mod(result * base, modulus);
}
exponent = exponent / 2n;
base = this.mod(base * base, modulus);
}
return result;
}
public static encryption(text: string, publicKey: string): string {
const texts: string = ArrayUtils.bytesToHex(
new Int8Array(new TextEncoder().encode(text))
);
const texInteger = BigInt("0x" + texts);
const textLength = texts.length;
const length = publicKey.length;
const lengthTwo = length >> 1;
const publicKeyArr: [bigint, bigint] = [
BigInt("0x" + publicKey.substring(0, lengthTwo)),
BigInt("0x" + publicKey.substring(lengthTwo, length)),
];
let randomK;
let num;
let C1Arr = null;
let pointArr = null;
let pointArr_0 = "";
let pointArr_1 = "";
do {
do {
randomK = BigInt(
Math.floor(Math.random() * (Number(this.ECC_N) - 2)) + 1
);
} while (randomK <= 1n || randomK > this.ECC_N - 2n);
C1Arr = this.multiPoints(this.ECC_POINT, randomK);
pointArr = this.multiPoints(publicKeyArr, randomK);
pointArr_0 = pointArr[0].toString(16);
pointArr_1 = pointArr[1].toString(16);
num = BigInt("0x" + this.kdf(pointArr_0 + pointArr_1, textLength));
} while (num == 0n);
const C2 = (texInteger ^ num).toString(16);
const C3 = pointArr_0 + texInteger.toString(16) + pointArr_1;
const C11 = C1Arr[0].toString(16);
const C12 = C1Arr[1].toString(16);
return (
textLength +
"" +
C11.length +
"" +
C12.length +
C11 +
C12 +
C2 +
SM3Utils.encryption(C3)
);
}
public static kdf(bitnum: string, klen: number): string {
const v: number = 64;
const count: number = Math.ceil(klen / v);
let k: string = "";
for (let i = 0; i < count; i++) {
let bitString: string = bitnum;
const ks: string = (i + 1).toString(16);
for (let j = 32 - ks.length; j >= 0; j--) {
bitString += "0";
}
bitString += ks;
k += SM3Utils.encryption(bitString);
}
return k.substring(0, klen);
}
public static decrypt(ciphertext: string, privateKey: string): string {
const bitlength: number = parseInt(ciphertext.substring(0, 2), 10);
const C11: number = parseInt(ciphertext.substring(2, 4), 10);
const C12: number = parseInt(ciphertext.substring(4, 6), 10);
const C: number = C11 + C12;
const tempCiphertext: string = ciphertext.substring(6);
const length: number = tempCiphertext.length - 64;
const C1: string = tempCiphertext.substring(0, C);
const C2: string = tempCiphertext.substring(C, length);
const C3: string = tempCiphertext.substring(length);
const C1Arr: [bigint, bigint] = [
BigInt("0x" + C1.substring(0, C11)),
BigInt("0x" + C1.substring(C11)),
];
const left: bigint = this.mod(C1Arr[1] ** 2n, this.ECC_P);
const right: bigint = this.mod(
C1Arr[0] ** 3n + this.ECC_A * C1Arr[0] + this.ECC_B,
this.ECC_P
);
if (left !== right) {
throw new Error("验证C1是否满足椭圆曲线方程");
}
const C2Arr: bigint[] = this.multiPoints(C1Arr, BigInt("0x" + privateKey));
const C2Arr_0: string = C2Arr[0].toString(16);
const C2Arr_1: string = C2Arr[1].toString(16);
const tInteger: bigint = BigInt(
"0x" + this.kdf(C2Arr_0 + C2Arr_1, bitlength)
);
if (tInteger === 0n) {
throw new Error("S是无穷远点");
}
const Mm: bigint = BigInt("0x" + C2) ^ tInteger;
if (C3 !== SM3Utils.encryption(C2Arr_0 + Mm.toString(16) + C2Arr_1)) {
throw new Error("验证失败");
}
return this.bigintToUtf8String(Mm);
}
private static bigintToUtf8String(num: bigint): string {
const bytes = [];
while (num > 0) {
bytes.push(Number(num % 256n));
num = num / 256n;
}
const decoder = new TextDecoder("utf-8");
return decoder.decode(new Uint8Array(bytes.reverse()));
}
public static sign(message: string, privateKey: string): string {
const msg: string = ArrayUtils.bytesToHex(
new Int8Array(new TextEncoder().encode(message))
);
const privateKeyInt = BigInt("0x" + privateKey);
const multiPoint = this.multiPoints(this.ECC_POINT, privateKeyInt);
const ZA = SM3Utils.encryption(
msg.length.toString(16) +
msg +
this.ECC_A.toString(16) +
this.ECC_B.toString(16) +
this.ECC_X.toString(16) +
this.ECC_Y.toString(16) +
multiPoint[0].toString(16) +
multiPoint[1].toString(16)
);
const e = BigInt("0x" + SM3Utils.encryption(ZA + msg));
let k;
let multiPointTwo;
let r;
let s;
do {
do {
do {
k = BigInt(Math.floor(Math.random() * (Number(this.ECC_N) - 2)) + 1);
} while (k <= 1n || k >= this.ECC_N);
multiPointTwo = this.multiPoints(this.ECC_POINT, k);
r = this.mod(e + multiPointTwo[0], this.ECC_N);
} while (r === 0n || r + k === this.ECC_N);
s = this.mod(
this.modInverse(1n + privateKeyInt, this.ECC_N) *
(k - r * privateKeyInt),
this.ECC_N
);
} while (s === 0n);
const rString = r.toString(16);
return rString.length.toString() + rString + s.toString(16);
}
public static modInverse(a: bigint, m: bigint): bigint {
const m0 = m;
let y = 0n;
let x = 1n;
if (m === 1n) return 0n;
while (a > 1n) {
const q = a / m;
let t = m;
m = a % m;
a = t;
t = y;
y = x - q * y;
x = t;
}
if (x < 0n) x += m0;
return x;
}
public static verify(
message: string,
sing: string,
publicKey: string
): boolean {
const msg: string = ArrayUtils.bytesToHex(
new Int8Array(new TextEncoder().encode(message))
);
const lengths: number = parseInt(sing.substring(0, 2)) + 2;
const rInt: bigint = BigInt("0x" + sing.substring(2, lengths));
const sInt: bigint = BigInt("0x" + sing.substring(lengths));
const lengthTwo = publicKey.length >> 1;
const publicKeyArr: [bigint, bigint] = [
BigInt("0x" + publicKey.substring(0, lengthTwo)),
BigInt("0x" + publicKey.substring(lengthTwo)),
];
const ZA: string = SM3Utils.encryption(
msg.length.toString(16) +
msg +
this.ECC_A.toString(16) +
this.ECC_B.toString(16) +
this.ECC_X.toString(16) +
this.ECC_Y.toString(16) +
publicKey
);
const e: bigint = BigInt("0x" + SM3Utils.encryption(ZA + msg));
const t: bigint = this.mod(rInt + sInt, this.ECC_N);
const multiPointTwo1: [bigint, bigint] = this.multiPoints(
this.ECC_POINT,
sInt
);
const multiPointTwo2: [bigint, bigint] = this.multiPoints(publicKeyArr, t);
const result: [bigint, bigint] = this.addPoints(multiPointTwo1, multiPointTwo2);
const R: bigint = this.mod(e + result[0], this.ECC_N);
return R == rInt;
}
public static countX2(x: bigint, w: number): bigint {
const twoToThePowerOfW: bigint = BigInt(2) ** BigInt(w);
const mask: bigint = twoToThePowerOfW - BigInt(1);
const bitwiseAndResult: bigint = x & mask;
return twoToThePowerOfW + bitwiseAndResult;
}
public static addPoints(P1: [bigint, bigint], P2: [bigint, bigint]): [bigint, bigint] {
if (P1[0] == this.ZERO && P1[1] == this.ZERO) {
return P2;
}
if (P2[0] === this.ZERO && P2[1] == this.ZERO) {
return P1;
}
let lambda: bigint;
if (P1[0] == P2[0] && P1[1] == -P2[1]) {
return [BigInt(0), BigInt(0)];
} else {
if (P1[0] == P2[0]) {
lambda = this.mod(this.modInverse(P1[1] * P1[1] * this.TWO, this.ECC_P) * (P1[0] * this.THREE + this.ECC_A), this.ECC_P);
} else {
const temp1: bigint = P1[1] - P2[1];
const temp2: bigint = P1[0] - P2[0];
const temp3: bigint = this.modPow(
temp2,
this.ECC_P - this.TWO,
this.ECC_P
);
lambda = temp1 * temp3;
}
}
const x3: bigint = this.mod(lambda * lambda - P1[0] - P2[0], this.ECC_P);
const y3: bigint = this.mod(lambda * (P1[0] - x3) - P1[1], this.ECC_P);
return [x3, y3];
}
public static consultB1(dB: string, RA: string, PA: string, PB: string, IDA: string,
IDB: string): string[] {
let rB: bigint;
do {
rB = BigInt(
Math.floor(Math.random() * (Number(this.ECC_N) - 2)) + 1
);
} while (rB <= 1n || rB > this.ECC_N - 2n);
const multiPoint: [bigint, bigint] = this.multiPoints(this.ECC_POINT, rB);
const w: number = 127;
const xBar2: bigint = this.countX2(multiPoint[0], w);
const tB: bigint = this.mod(BigInt("0x" + dB) + xBar2 * rB, this.ECC_N);
const RAPoint: [bigint, bigint] = [
BigInt("0x" + RA.slice(0, RA.length / 2)),
BigInt("0x" + RA.slice(RA.length / 2, RA.length))
];
const x1Bar2: bigint = this.countX2(RAPoint[0], w);
const xRA: [bigint, bigint] = this.multiPoints(RAPoint, x1Bar2);
const PAxRA: [bigint, bigint] = this.addPoints(RAPoint, xRA);
const V: [bigint, bigint] = this.multiPoints(PAxRA, tB);
const pString: string = this.ECC_A.toString(16) + this.ECC_B.toString(16) + this.ECC_X.toString(16) + this.ECC_Y.toString(16);
const ZA = SM3Utils
.encryption(ArrayUtils.bytesToHex(new Int8Array(new TextEncoder().encode(IDA))) + pString
+ PA.slice(0, PA.length / 2)
+ PA.slice(PA.length / 2, PA.length));
const ZB = SM3Utils
.encryption(ArrayUtils.bytesToHex(new Int8Array(new TextEncoder().encode(IDB))) + pString
+ PB.slice(0, PB.length / 2)
+ PB.slice(PB.length / 2, PB.length));
const KBString = this.kdf(V[0].toString(16) + V[1].toString(16) + ZA + ZB, 32);
const k = SM3Utils
.encryption(V[0].toString(16) + ZA + ZB + RA + multiPoint[0].toString(16)
+ multiPoint[1].toString(16));
return [
SM3Utils.encryption("02" + V[1].toString(16) + k),
multiPoint[0].toString(16) + multiPoint[1].toString(16), KBString
];
}
public static consultA1(dA: string, rA: string, RA: string, RB: string, PA: string, PB: string, IDA: string,
IDB: string,
SB: string): string {
const w: number = 127;
const x1: bigint = BigInt("0x" + RA.substring(0, RA.length / 2));
const x1Bar2: bigint = this.countX2(x1, w);
const privateKey: bigint = BigInt("0x" + rA);
const tA: bigint = this.mod(BigInt("0x" + dA) + (x1Bar2 * privateKey), this.ECC_N);
const RBPoint: [bigint, bigint] = [
BigInt("0x" + RB.substring(0, RB.length / 2)),
BigInt("0x" + RB.substring(RB.length / 2, RB.length))
];
const xBar2: bigint = this.countX2(RBPoint[0], w);
const xRB: [bigint, bigint] = this.multiPoints(RBPoint, xBar2);
const PBx = PB.substring(0, PB.length / 2);
const PBy = PB.substring(PB.length / 2);
const PAxRA: [bigint, bigint] = this.addPoints([
BigInt("0x" + PBx),
BigInt("0x" + PBy)
], xRB);
const U: [bigint, bigint] = this.multiPoints(PAxRA, tA);
const pString = this.ECC_A.toString(16) + this.ECC_B.toString(16) + this.ECC_X.toString(16) + this.ECC_Y.toString(16);
const ZA = SM3Utils
.encryption(ArrayUtils.bytesToHex(new Int8Array(new TextEncoder().encode(IDA))) + pString
+ PA.substring(0, PA.length / 2)
+ PA.substring(PA.length / 2, PA.length));
const ZB = SM3Utils.encryption(
ArrayUtils.bytesToHex(new Int8Array(new TextEncoder().encode(IDB))) + pString + PBx + PBy);
const KBString = this.kdf(U[0].toString(16) + U[1].toString(16) + ZA + ZB, 32);
const k = SM3Utils.encryption(U[0].toString(16) + ZA + ZB + RA + RB);
const S1 = SM3Utils.encryption("02" + U[1].toString(16) + k);
if (S1 == SB) {
return KBString;
}
return "";
}
}