Closed ran1990 closed 1 month ago
对方的格式( 按hex来说)是 04 + c1+ c2 + c3, PHP这边的缺省格式是 c1+c3+c2 , 加解密时两个注意点就行,
public function doEncrypt($document, $publicKey, $model = C1C3C2)
public function doDecrypt($encryptData, $privateKey, $trim = true, $model = C1C3C2)
@lpilp 对方返回的是bas64 : $m2EncryptData =BJdx3C2g+B/K2fgQxw6jYpLfPilj9TCu/vTWybALImwzGY82JTC94FeoUI0yJCrk1JehikW6GL+cMlcx7Jb+S+yuF9sBrzU7H4aXgGzY1rrP7fkWm9CpJgovquVnNEB3noFiMbtVhP9eTu/t3nbvj2NLAI+6JsKtybFg60rkHK2X/GOIXSB6bTE3yNOrQx3+6v7jfG390i1BEsyaHBvIXOsXks1Y4JqRjvmiQlu7fr3I93e1hAF6ruRvrUMuxIpuYWbYSmMy6wSVXgWBHzSVRc13F24ZtaX8krf7D64K2TPPBRjS7nS1hgYrytyOEqT+lbSAT3X4u8+MKIFHV66qmlrS2B7Z/wJ+SKWR2Q6KyloTMH4p8ZwMYKQeoyN046316kBVo2UxhLP4CQwp1/7NMzSGBkEc9IYcE85wNi8mheR920f82vGL+Lp/vbNJ5tua5yEK2DzmFbLT0jOhanaGOeo=
$m2EncryptData = bin2hex(base64_decode($m2EncryptData));
$m2DecryptData = $sm2->doDecrypt($m2EncryptData, $this->response_private_key, true, C1C2C3);
还是不对
看代码应该是没有问题的,还有一个确认下 $this->response_private_key 这边使用的是 64个 hex字节的的明文密钥, 是否是使用的是这种密钥 如果都正确的话,只能一步步调试了,也可以你用一对测试密码对, 用XX公司的代码加密一段,然后把公私钥,加密后的都 打出来,我给你看下, 如果上面的就是用的测试的密码对,那把私钥给我下也行,就不用重新生成了
@lpilp 我不确定对方java是否定制化更改过,对方技术似乎说过这样的话语; 流程是,我使用一个平台公钥去签名,传给对方,对方在返回一个加密字符串,使用私钥解密,但是这个私钥和我加密的公钥似乎不是一堆,应该类似支付宝,把公钥转换成过平台公钥。
私钥:7d895253367e6exxxxxxxxxxxxxxxxxxxxxxxx0b4dafb38cbd1bd1e9b1bf1
明文参数: {"certNo":"341126197709218366","certType":"01","sourceIp":"127.000.000.001","personalMandate":"1","appName":"01测试","sceneId":"1","protocolNo":"11","protocolVersion":"11","cardNo":"6228480246125491166","ipType":"04"} 加密结果: BB4HlitUonlL2PhySO/Tq7dMk+goRT27g/blWU698LVabcGPJd/6kW45GuDoPbEEBAXfMX7NbTjmSyUZlNpvM568F9lhe+skvySIlPdUl6d4pDR7UrTYWNBJhZFZqhq93T57RN3tIXv0MsjcFPn7150k500M57v5Zd2or//4FfYv+gdpVKmS/9JbmCflMl/cVuxLcEnU+u5Y2Ck1QqGrS/lRJj7MO6Rglor2TDsrvxZ2rlXmBdyUULoIC/m75HyL2XytOOmMyepjXCGZHjfxBAnG3bBPzbXZi5iAGJD52yiYNp1yLKUGIivFxNKCbxhjKilApGSSPZoVo8/GCP++dWuTPrfLJiJIuRJyQwRjcDZuafMb58Do4/SKlb83IquxQ6uosOxtx9dCogTJaAllJxKDam2drojLqKoI5Q== 上送报文: {"data":"BB4HlitUonlL2PhySO/Tq7dMk+goRT27g/blWU698LVabcGPJd/6kW45GuDoPbEEBAXfMX7NbTjmSyUZlNpvM568F9lhe+skvySIlPdUl6d4pDR7UrTYWNBJhZFZqhq93T57RN3tIXv0MsjcFPn7150k500M57v5Zd2or//4FfYv+gdpVKmS/9JbmCflMl/cVuxLcEnU+u5Y2Ck1QqGrS/lRJj7MO6Rglor2TDsrvxZ2rlXmBdyUULoIC/m75HyL2XytOOmMyepjXCGZHjfxBAnG3bBPzbXZi5iAGJD52yiYNp1yLKUGIivFxNKCbxhjKilApGSSPZoVo8/GCP++dWuTPrfLJiJIuRJyQwRjcDZuafMb58Do4/SKlb83IquxQ6uosOxtx9dCogTJaAllJxKDam2drojLqKoI5Q=="} 响应报文: {"errInfo":"成功","data":"BPo8lvMHr5la+ESqlMwNEcLbT8ER8/a4HL7Kn642Ire0RU18H+e17kgFU1JSQphHcXFWI0yzLit9i9y2YNFEk6z1BusR9cN4EJtgrvqDwN9SezYjFKeaorOt0496LDK1oRsM8XeN3/UtSHvGKRndI6iajYcRXZek33Wv1iCxvFopmgBkv22GHGIxohLPpSpeShzcHKqRbcLrs+x9fMakwB747+AzjuYVe0aTqKHn09Bjj9ou/LVLX/+sV8V8LM/m1nM9WVSrRYA7JgyPnw0dnsITE8DbVJmm2FtMZ4QDruhZJj7MzRBPKjo8zxllc2QCfaPTc9o5Uvd3bxoxRWFnf6yNIYaTuAxny3KkdgDHq/g1hWC2A7Z3sSIXUGW9eFTgQROwjPhPPJsttVXfgB2M4tp51hkQSVbzKDN3V/eeeUu8k7YlzOM+zarJphf+RzNMPB+xwwXv3MdEskYMyDjwleM=","errCode":"20000000"}
试了解不开,最大的可能是出在这里:SM3.toByteArray(ct),这个有个补齐操作,你看下代码有没有补齐到4字节 这个正常应该是 ( php代码) str_pad(chr(ct), 4, chr(0), STR_PAD_LEFT)) , 有个补全操作,比如当ct=1时,这个的16进制就是 00000001
private static byte[] KDF(byte[] Z, int klen) {
int ct = 1;
int end = (int) Math.ceil(klen * 1.0 / 32);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
for (int i = 1; i < end; i++) {
baos.write(sm3hash(Z, SM3.toByteArray(ct)));
ct++;
}
。。。 。。。
是需要改php代码还是java代码呢。java代码对方肯定是不配合的。 @lpilp
如果是对方的问题,那肯定得按对方的格式来修改我们的PHP,从GIT的开源的那个项目来看 SM3.toByteArray(ct) ,好像没有问题, 不知道你那边的有没有修改过,原始是:
static byte[] toByteArray(int i) {
byte[] byteArray = new byte[4];
byteArray[0] = (byte) (i >>> 24);
byteArray[1] = (byte) ((i & 0xFFFFFF) >>> 16);
byteArray[2] = (byte) ((i & 0xFFFF) >>> 8);
byteArray[3] = (byte) (i & 0xFF);
return byteArray;
}
` public class SM3 {
private static char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'A', 'B', 'C', 'D', 'E', 'F'};
private static final String ivHexStr = "7380166f 4914b2b9 172442d7 da8a0600 a96f30bc 163138aa e38dee4d b0fb0e4e";
private static final BigInteger IV = new BigInteger(ivHexStr.replaceAll(" ",
""), 16);
private static final Integer Tj15 = Integer.valueOf("79cc4519", 16);
private static final Integer Tj63 = Integer.valueOf("7a879d8a", 16);
private static final byte[] FirstPadding = {(byte) 0x80};
private static final byte[] ZeroPadding = {(byte) 0x00};
private static int T(int j) {
if (j >= 0 && j <= 15) {
return Tj15.intValue();
} else if (j >= 16 && j <= 63) {
return Tj63.intValue();
} else {
throw new RuntimeException("data invalid");
}
}
private static Integer FF(Integer x, Integer y, Integer z, int j) {
if (j >= 0 && j <= 15) {
return Integer.valueOf(x.intValue() ^ y.intValue() ^ z.intValue());
} else if (j >= 16 && j <= 63) {
return Integer.valueOf((x.intValue() & y.intValue())
| (x.intValue() & z.intValue())
| (y.intValue() & z.intValue()));
} else {
throw new RuntimeException("data invalid");
}
}
private static Integer GG(Integer x, Integer y, Integer z, int j) {
if (j >= 0 && j <= 15) {
return Integer.valueOf(x.intValue() ^ y.intValue() ^ z.intValue());
} else if (j >= 16 && j <= 63) {
return Integer.valueOf((x.intValue() & y.intValue())
| (~x.intValue() & z.intValue()));
} else {
throw new RuntimeException("data invalid");
}
}
private static Integer P0(Integer x) {
return Integer.valueOf(x.intValue()
^ Integer.rotateLeft(x.intValue(), 9)
^ Integer.rotateLeft(x.intValue(), 17));
}
private static Integer P1(Integer x) {
return Integer.valueOf(x.intValue()
^ Integer.rotateLeft(x.intValue(), 15)
^ Integer.rotateLeft(x.intValue(), 23));
}
private static byte[] padding(byte[] source) throws IOException {
if (source.length >= 0x2000000000000000l) {
throw new RuntimeException("src data invalid.");
}
long l = source.length * 8;
long k = 448 - (l + 1) % 512;
if (k < 0) {
k = k + 512;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(source);
baos.write(FirstPadding);
long i = k - 7;
while (i > 0) {
baos.write(ZeroPadding);
i -= 8;
}
baos.write(long2bytes(l));
return baos.toByteArray();
}
private static byte[] long2bytes(long l) {
byte[] bytes = new byte[8];
for (int i = 0; i < 8; i++) {
bytes[i] = (byte) (l >>> ((7 - i) * 8));
}
return bytes;
}
public static byte[] hash(byte[] source) throws IOException {
byte[] m1 = padding(source);
int n = m1.length / (512 / 8);
byte[] b;
byte[] vi = IV.toByteArray();
byte[] vi1 = null;
for (int i = 0; i < n; i++) {
b = Arrays.copyOfRange(m1, i * 64, (i + 1) * 64);
vi1 = CF(vi, b);
vi = vi1;
}
return vi1;
}
private static byte[] CF(byte[] vi, byte[] bi) throws IOException {
int a, b, c, d, e, f, g, h;
a = toInteger(vi, 0);
b = toInteger(vi, 1);
c = toInteger(vi, 2);
d = toInteger(vi, 3);
e = toInteger(vi, 4);
f = toInteger(vi, 5);
g = toInteger(vi, 6);
h = toInteger(vi, 7);
int[] w = new int[68];
int[] w1 = new int[64];
for (int i = 0; i < 16; i++) {
w[i] = toInteger(bi, i);
}
for (int j = 16; j < 68; j++) {
w[j] = P1(w[j - 16] ^ w[j - 9] ^ Integer.rotateLeft(w[j - 3], 15))
^ Integer.rotateLeft(w[j - 13], 7) ^ w[j - 6];
}
for (int j = 0; j < 64; j++) {
w1[j] = w[j] ^ w[j + 4];
}
int ss1, ss2, tt1, tt2;
for (int j = 0; j < 64; j++) {
ss1 = Integer
.rotateLeft(
Integer.rotateLeft(a, 12) + e
+ Integer.rotateLeft(T(j), j), 7);
ss2 = ss1 ^ Integer.rotateLeft(a, 12);
tt1 = FF(a, b, c, j) + d + ss2 + w1[j];
tt2 = GG(e, f, g, j) + h + ss1 + w[j];
d = c;
c = Integer.rotateLeft(b, 9);
b = a;
a = tt1;
h = g;
g = Integer.rotateLeft(f, 19);
f = e;
e = P0(tt2);
}
byte[] v = toByteArray(a, b, c, d, e, f, g, h);
for (int i = 0; i < v.length; i++) {
v[i] = (byte) (v[i] ^ vi[i]);
}
return v;
}
private static int toInteger(byte[] source, int index) {
StringBuilder valueStr = new StringBuilder("");
for (int i = 0; i < 4; i++) {
valueStr.append(hexDigits[(byte) ((source[index * 4 + i] & 0xF0) >> 4)]);
valueStr.append(hexDigits[(byte) (source[index * 4 + i] & 0x0F)]);
}
return Long.valueOf(valueStr.toString(), 16).intValue();
}
private static byte[] toByteArray(int a, int b, int c, int d, int e, int f,
int g, int h) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32);
baos.write(toByteArray(a));
baos.write(toByteArray(b));
baos.write(toByteArray(c));
baos.write(toByteArray(d));
baos.write(toByteArray(e));
baos.write(toByteArray(f));
baos.write(toByteArray(g));
baos.write(toByteArray(h));
return baos.toByteArray();
}
public static byte[] toByteArray(int i) {
byte[] byteArray = new byte[4];
byteArray[0] = (byte) (i >>> 24);
byteArray[1] = (byte) ((i & 0xFFFFFF) >>> 16);
byteArray[2] = (byte) ((i & 0xFFFF) >>> 8);
byteArray[3] = (byte) (i & 0xFF);
return byteArray;
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n = 256 + n;
}
int d1 = n / 16;
int d2 = n % 16;
return "" + hexDigits[d1] + hexDigits[d2];
}
public static String byteArrayToHexString(byte[] b) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++) {
resultSb.append(byteToHexString(b[i]));
}
return resultSb.toString();
}
public static void main(String[] args) throws IOException {
System.out.println(SM3.byteArrayToHexString(SM3.hash("sm3算法测试".getBytes())));
}
}
`
也没有修改,今天刚好有空,我装个java环境试试
感谢大佬
修改 /src/smecc/sm2/chipher.php 的第26行的reset函数,添加了4行,该修改只是匹配了这个java项目,该java有bug, 与其他的项目不匹配,请fork项目后自行修改使用
private function Reset() //注意,加密使用无符号的数组转换,以便与硬件相一致
{
$this->sm3keybase = new SM3Digest();
$this->sm3c3 = new SM3Digest();
$p = array();
$gmp_x = $this->p2->GetX();
$this->sm3keybase->BlockUpdate(array(4), 0, 1); // 添加: 这里添加个04
$x = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_x);
$this->sm3keybase->BlockUpdate($x, 0, sizeof($x));
if($x[0]>127){ // >7F
$this->sm3c3->BlockUpdate(array(0), 0, 1); // 这里做了个判定,补00
}
$this->sm3c3->BlockUpdate($x, 0, sizeof($x));
.....
}
@lpilp 试了一下 还是不行
看着是好使的啊
改完后是这样,是不是哪里没改对
private function Reset() //注意,加密使用无符号的数组转换,以便与硬件相一致
{
$this->sm3keybase = new SM3Digest();
$this->sm3c3 = new SM3Digest();
$p = array();
$gmp_x = $this->p2->GetX();
$this->sm3keybase->BlockUpdate(array(4), 0, 1); // 添加: 这里添加个04
$x = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_x);
$this->sm3keybase->BlockUpdate($x, 0, sizeof($x));
if($x[0]>127){ // >7F
$this->sm3c3->BlockUpdate(array(0), 0, 1); // 这里做了个判定,补00
}
$this->sm3c3->BlockUpdate($x, 0, sizeof($x));
$gmp_y = $this->p2->GetY();
$y = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_y);
$this->sm3keybase->BlockUpdate($y, 0, sizeof($y));
$this->ct = 1;
$this->NextKey();
}
是这样子的,我用发您的这个加密的的确可以解密,但是我用java生成了一个新的,又不能解密了,不能百分百解密。
BPQaO3ISxSZ8A3ycmyMpETc8/G7AZhP/9dg92TmT4BXhH5/l9XovYH0gS8Btp95ofANXnN6MSG25G5VM5k8DLVnF9L1e+HnEqX8OO31bu17sQZsrUs/KQqZ6Ffe3qxU3dz+CfuYzCMCJ587BupNCcjBjeKjTmnXQl4Si+mXtaiG0wsIIP1ZaBdgdjuovN7eFKQByC3sD+YNOwWVHK1Z1ENkQPtq4CC6AptkemStRti3Y8iMq4/s90q1oLuRtHsMZcihAkRIZYoonUw/F/wwwNDkSA6GS2zs=
当时没注意,还有一个y也得处理,/src/smecc/sm2/chipher.php 还是这个文件里,判定一下, java取x,y的值用错了函数,取成生成asn1的bigint的算法,得计算是否大于7f,
public function Dofinal()
{
$c3 = array();
$gmp_p = $this->p2->GetY();
$p = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_p);
///========以下添加的======
if($p[0]>127){ // >7F
$this->sm3c3->BlockUpdate(array(0), 0, 1); // 这里做了个判定,补00
}
///========以上添加的======
$this->sm3c3->BlockUpdate($p, 0, sizeof($p));
$this->sm3c3->DoFinal($c3, 0);
$this->Reset();
return $c3;
}
感谢大佬 @lpilp ,总算可以了,我都打算放弃只有使用java暴露一个api 来负责解密加密了,还好最终ok
问题: 1、php sm2加密的内容 和其他java公司可以正常加密解密,和这个xx公司加密彼此都不通,怀疑对方sm2 修改过。 2、对方加密的内容的base64,我们解密是否只需要base decode 然后再bin2hex就行呢? @lpilp 对方jiava代码如下: ` import com.alibaba.fastjson.JSON; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint;
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.security.SecureRandom; import java.util.Arrays;
public class SM2 {
`