对方提供的写法是 Java 和 PHP,我想把他变成 Python 的,哪位大神帮忙指点一下。
Java 的写法
import org.apache.commons.codec.binary.Base64;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
public class SHA256WithRSAAlgo {
private static String consumerId = "b68d2a72...."; // Trimmed for security reason
private static String baseUrl = "https://api-gateway.walmart.com/v3/feeds";
private static String privateEncodedStr = "MIICeAIBADANBgkqhkiG9w0BAQEFAA......"; //Trimmed for security reasons
public static void main(String[] args) {
String httpMethod = "GET";
String timestamp = String.valueOf(System.currentTimeMillis());
String stringToSign = consumerId + "\n" + baseUrl + "\n" + httpMethod + "\n" + timestamp + "\n";
String signedString = SHA256WithRSAAlgo.signData(stringToSign, privateEncodedStr);
System.out.println("Signed String: " + signedString);
}
public static String signData(String stringToBeSigned, String encodedPrivateKey) {
String signatureString = null;
try {
byte[] encodedKeyBytes = Base64.decodeBase64(encodedPrivateKey);
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(encodedKeyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey myPrivateKey = kf.generatePrivate(privSpec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(myPrivateKey);
byte[] data = stringToBeSigned.getBytes("UTF-8");
signature.update(data);
byte[] signedBytes = signature.sign();
signatureString = Base64.encodeBase64String(signedBytes);
} catch (Exception e) {
e.printStackTrace();
}
return signatureString;
}
}
PHP 的写法
$URL = //Walmart API URL along with path and query parameters
$RequestMethod = //Request method type i.e GET, POST
$Timestamp = round(microtime(true) * 1000); //Current system timestamp
function _GetWalmartAuthSignature($URL, $RequestMethod, $Timestamp) {
$WalmartPrivateKey = //Your Walmart Private Key;
$WalmartConsumerID = //Your Walmart Comsumer Id;
// CONSTRUCT THE AUTH DATA WE WANT TO SIGN
$AuthData = $WalmartConsumerID."\n";
$AuthData .= $URL."\n";
$AuthData .= $RequestMethod."\n";
$AuthData .= $Timestamp."\n";
// GET AN OPENSSL USABLE PRIVATE KEY FROMM THE WARMART SUPPLIED SECRET
$Pem = _ConvertPkcs8ToPem(base64_decode($WalmartPrivateKey));
$PrivateKey = openssl_pkey_get_private($Pem);
// SIGN THE DATA. USE sha256 HASH
$Hash = defined("OPENSSL_ALGO_SHA256") ? OPENSSL_ALGO_SHA256 : "sha256";
if (!openssl_sign($AuthData, $Signature, $PrivateKey, $Hash))
{ // IF ERROR RETURN NULL return null; }
//ENCODE THE SIGNATURE AND RETURN
return base64_encode($Signature);
}
function _ConvertPkcs8ToPem($der)
{
static $BEGIN_MARKER = "-----BEGIN PRIVATE KEY-----";
static $END_MARKER = "-----END PRIVATE KEY-----";
$key = base64_encode($der);
$pem = $BEGIN_MARKER . "\n";
$pem .= chunk_split($key, 64, "\n");
$pem .= $END_MARKER . "\n";
return $pem;
}
}
1
sudoy OP 如果写出成品,请附上您的打赏码,我会直接打赏,谢谢!
|
2
SakuraSa 2020-07-21 15:56:50 +08:00 1
先安装 Crypto
```bash pip3 install pycryptodome ``` 大概是这样? ```python3 import base64 from Crypto.Signature import PKCS1_v1_5 from Crypto.PublicKey import RSA from Crypto.Hash import SHA256 priv_key = b"MIIJRAIBADANBg..." def sign(key, data): private_key = RSA.importKey(base64.decodebytes(key)) cipher = PKCS1_v1_5.new(private_key) h = SHA256.new(data) signature = cipher.sign(h) return base64.b64encode(signature) def main(): print("signed:", sign(priv_key, b"hello world")) if __name__ == '__main__': main() ``` |
3
Vegetable 2020-07-21 16:06:04 +08:00
java 和 python 涉及到 pkcs8 和 pkcs1 的问题,挺恶心。
|
4
sudoy OP @SakuraSa 谢谢!
根据开发文档,需要( 1 )先将平台提供的 private key 进行 base64 解密得到 pkcs8 格式的密钥,( 2 )然后将该 pkcs8 密钥转化成 PEM,( 3 )然后再通过 openssl 将这个 PEM 转换成 private key,最后( 4 )用这个 private key 去签名请求数据。 您写的这个是第( 4 )步,前面几步搜了半天找不到合适的方法。 我目前搜到的有帮助的相关资料: http://alexfu.cc/get/passage/100 https://gist.github.com/lkdocs/6519366 |
5
SakuraSa 2020-07-21 16:23:03 +08:00 1
|
8
rimutuyuan 2020-07-21 16:28:28 +08:00
可以使用 shell 吗
openssl rsa -in pkcs8.pem -out pkcs1.pem |
9
Vegetable 2020-07-21 16:34:10 +08:00 2
你 DEMO 代码里的证书是单行的 PKCS8,我不确定现在 Py 能不能直接用,所以我给转成 1 的了,同时 Java 代码的 base64 库我也改成了 import java.util.Base64;才能在我的电脑上跑起来。
https://gist.github.com/luliangce/da56d70d0b206eeaa77061877031d8cf |
10
Vegetable 2020-07-21 16:35:48 +08:00
$ python py.py
b'jZpuuj7nfamQqxsMvkWg/eJcnO3a0/2LVmjuRmUfD2CCJgT7LU2iz3Kv0kOT/ADfUBtQ9lIHwi1WfyirtixQ1SKnASRZLpdcY/iufnvydpyqVdZ/6PYMAeYTq1451PpQ0pIR8P8UHaVoCcZoioYXl7pa9IRwDZWyIxP8CWf2xcU=' (sign-lDeXAck8-py3.7) ~/Desktop/proj/sign ⌚ 16:35:06 $ javac SHA256WithRSAAlgo.java (sign-lDeXAck8-py3.7) ~/Desktop/proj/sign ⌚ 16:35:15 $ java SHA256WithRSAAlgo Signed String: jZpuuj7nfamQqxsMvkWg/eJcnO3a0/2LVmjuRmUfD2CCJgT7LU2iz3Kv0kOT/ADfUBtQ9lIHwi1WfyirtixQ1SKnASRZLpdcY/iufnvydpyqVdZ/6PYMAeYTq1451PpQ0pIR8P8UHaVoCcZoioYXl7pa9IRwDZWyIxP8CWf2xcU= |
11
sudoy OP @SakuraSa 嗯,奇怪的是我直接用这个 pkcs8,提示说不支持这个类型的 RSA
raise ValueError("RSA key format is not supported") ValueError: RSA key format is not supported |
12
sudoy OP @Vegetable 非常感谢,我刚才试了一下直接用单行的 PKCS8,签名成功,但是拿这个签名去请求 API 的时候提示 401 错误,也就是验证失败,正在查找原因。
|
14
sudoy OP @rimutuyuan 不支持 shell 呢,不过可以将他给的 key 在 shell 那里转换好,再去代码里面调用
|