【支付宝小プログラム】PHPはユーザー敏感情報を取得携帯電話番号検査署名解読RSA解読AES解読
57714 ワード
需要
アリペイの小さいプログラムの端、暗号化のユーザーの携帯電話の番号のデータを取得して、サービスの端を通じてデータに対して解読を行う必要があって、ユーザーの携帯電話の番号を得ます
に質問
ユーザ情報は機密情報であり,機密情報暗号化復号方法における方式で復号する必要がある.
サービス側はPHPで、公式に対応するプレゼンテーションdemoがないため、模索テストを経ても、チェックが通らず、復号が成功しない場合があります.
解決プロセス
1.公式javaインスタンスコードの分析
このコードをPHPに直接翻訳し、アリクラウドのSDK呼び出しコアの2つの方法を採用します.
試行錯誤の末、失敗に終わった.
2.アリクラウドSDKソースコードの分析と位置決め
アリクラウドのSDKでは、まず共通の初期化が
検査の結果、
上のコードはそれぞれ対応する流れの中のチェックマーク、復号、チェックを行い、1行1行で実行し、問題を位置決め分析し、最終的に位置決めしたのは
つまりopensslで発生した問題は、デバッグ中のエラーメッセージと一致しています.
3.フォーラム&検索
公式フォーラムの検索と支付宝の公式カスタマーサービスを見つけることで、得られたのは一言だけだ.
では、PHPでAES/CBC/PKCS 5 Paddingを実現するにはどうすればいいのでしょうか.
次のクラスが必要です
しかし、このクラスの
ソリューション
疑問1:微信小プログラムは小プログラムで、支付宝小プログラムも小プログラムで、彼らの間に何か関連がありますか?疑問2:私は微信小プログラムのユーザー情報の取得を終えたばかりで、フロントエンドで暗号化データを取得してサービス側に伝え、サービス側が復号する方法を採用していますが、その間に何か関連がありますか?
これらの疑問を持って、私は再び微信のウィジェットが提供した解析demoを見ました.
同じ点:はいずれもaes鍵復号 を用いる.ソリューションには、オフセットivの異なる点があります. 微信のaes鍵はログインcodeから入手したsessionKeyであり、支付宝は管理バックグラウンドのためにランダムに を生成する.マイクロ信号ウィジェット検証復号により得られたデータ中のwatermark透かし これにより大胆な考えが得られ,それによって修正後の方法が得られる.
親測定が有効で、得られたデータは
返される結果は、公式ドキュメントと一致します.
これにより,アリペイがユーザの機密情報を取得するPHPバックグラウンドの復号化問題が解決された.
参考資料 https://docs.alipay.com/mini/introduce/getphonenumber https://docs.alipay.com/mini/introduce/aes https://www.cnblogs.com/lonmyblog/p/7885974.html
アリペイの小さいプログラムの端、暗号化のユーザーの携帯電話の番号のデータを取得して、サービスの端を通じてデータに対して解読を行う必要があって、ユーザーの携帯電話の番号を得ます
に質問
ユーザ情報は機密情報であり,機密情報暗号化復号方法における方式で復号する必要がある.
サービス側はPHPで、公式に対応するプレゼンテーションdemoがないため、模索テストを経ても、チェックが通らず、復号が成功しない場合があります.
解決プロセス
1.公式javaインスタンスコードの分析
String response = " ";
//1.
Map<String, String> openapiResult = JSON.parseObject(response,
new TypeReference<Map<String, String>>() {
}, Feature.OrderedField);
String signType = StringUtil.defaultIfBlank(openapiResult.get("signType"), "RSA2");
String charset = StringUtil.defaultIfBlank(openapiResult.get("charset"), "UTF-8");
String encryptType = StringUtil.defaultIfBlank(openapiResult.get("encryptType"), "AES");
String sign = openapiResult.get("sign");
String content = openapiResult.get("response");
//
boolean isDataEncrypted = !content.startsWith("{");
boolean signCheckPass = false;
//2.
String signContent = content;
String signVeriKey = " ( appId+signType )";
String encryptType = " ( appId+encryptType )"
//
if (isDataEncrypted) {
signContent = "\"" + signContent + "\"";
}
try {
signCheckPass = AlipaySignature.rsaCheck(signContent, sign, signVeriKey, charset, signType);
} catch (AlipayApiException e) {
// ,
}
if(!signCheckPass) {
// ( ), ( )
throw new Exception(" ");
}
//3.
String plainData = null;
if (isDataEncrypted) {
try {
AlipayEncrypt.decryptContent(content, encryptType, decryptKey, charset);
} catch (AlipayApiException e) {
// ,
throw new Exception(" ");
}
} else {
plainData = content;
}
このコードをPHPに直接翻訳し、アリクラウドのSDK呼び出しコアの2つの方法を採用します.
試行錯誤の末、失敗に終わった.
2.アリクラウドSDKソースコードの分析と位置決め
アリクラウドのSDKでは、まず共通の初期化が
AopClient
で、小さなプログラムのパラメータを初期化していますが、他の操作はありません.検査の結果、
AopClient
の復号と検証のコードは以下の通りである./** rsaCheckV1 & rsaCheckV2
*
* , AopClient 。
* , 。
**/
public function rsaCheckV1($params, $rsaPublicKeyFilePath,$signType='RSA') {
$sign = $params['sign'];
$params['sign_type'] = null;
$params['sign'] = null;
return $this->verify($this->getSignContent($params), $sign, $rsaPublicKeyFilePath,$signType);
}
public function rsaCheckV2($params, $rsaPublicKeyFilePath, $signType='RSA') {
$sign = $params['sign'];
$params['sign'] = null;
return $this->verify($this->getSignContent($params), $sign, $rsaPublicKeyFilePath, $signType);
}
function verify($data, $sign, $rsaPublicKeyFilePath, $signType = 'RSA') {
if($this->checkEmpty($this->alipayPublicKey)){
$pubKey= $this->alipayrsaPublicKey;
$res = "-----BEGIN PUBLIC KEY-----
" .
wordwrap($pubKey, 64, "
", true) .
"
-----END PUBLIC KEY-----";
}else {
//
$pubKey = file_get_contents($rsaPublicKeyFilePath);
// openssl
$res = openssl_get_publickey($pubKey);
}
($res) or die(' RSA 。 ');
// openssl , bool
$result = FALSE;
if ("RSA2" == $signType) {
$result = (openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256)===1);;
} else {
$result = (openssl_verify($data, base64_decode($sign), $res)===1);
}
if(!$this->checkEmpty($this->alipayPublicKey)) {
//
openssl_free_key($res);
}
return $result;
}
/**
* , AopClient 。
* , 。
**/
public function rsaDecrypt($data, $rsaPrivateKeyPem = null, $charset = null) {
if($this->checkEmpty($this->rsaPrivateKeyFilePath)){
//
$priKey=$this->rsaPrivateKey;
$res = "-----BEGIN RSA PRIVATE KEY-----
" .
wordwrap($priKey, 64, "
", true) .
"
-----END RSA PRIVATE KEY-----";
}else {
$priKey = file_get_contents($this->rsaPrivateKeyFilePath);
$res = openssl_get_privatekey($priKey);
}
($res) or die(' , RSA ');
// openssl
$decodes = explode(',', $data);
$strnull = "";
$dcyCont = "";
foreach ($decodes as $n => $decode) {
if (!openssl_private_decrypt($decode, $dcyCont, $res)) {
echo "
" . openssl_error_string() . "
";
}
$strnull .= $dcyCont;
}
return $strnull;
}
function verify($data, $sign, $rsaPublicKeyFilePath, $signType = 'RSA') {
if($this->checkEmpty($this->alipayPublicKey)){
$pubKey= $this->alipayrsaPublicKey;
$res = "-----BEGIN PUBLIC KEY-----
" .
wordwrap($pubKey, 64, "
", true) .
"
-----END PUBLIC KEY-----";
}else {
//
$pubKey = file_get_contents($rsaPublicKeyFilePath);
// openssl
$res = openssl_get_publickey($pubKey);
}
($res) or die(' RSA 。 ');
// openssl , bool
$result = FALSE;
if ("RSA2" == $signType) {
$result = (openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256)===1);;
} else {
$result = (openssl_verify($data, base64_decode($sign), $res)===1);
}
if(!$this->checkEmpty($this->alipayPublicKey)) {
//
openssl_free_key($res);
}
return $result;
}
上のコードはそれぞれ対応する流れの中のチェックマーク、復号、チェックを行い、1行1行で実行し、問題を位置決め分析し、最終的に位置決めしたのは
$result = (openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256)===1);;
if (!openssl_private_decrypt($decode, $dcyCont, $res)) {
echo "
" . openssl_error_string() . "
";
}
つまりopensslで発生した問題は、デバッグ中のエラーメッセージと一致しています.
3.フォーラム&検索
公式フォーラムの検索と支付宝の公式カスタマーサービスを見つけることで、得られたのは一言だけだ.
, : AES/CBC/PKCS5Padding; 0
では、PHPでAES/CBC/PKCS 5 Paddingを実現するにはどうすればいいのでしょうか.
次のクラスが必要です
class MagicCrypt {
private $iv = "0102030405060708";// IV,
private $encryptKey = " 16 key";//AESkey,
//
public function encrypt($encryptStr) {
$localIV = $this->iv;
$encryptKey = $this->encryptKey;
//Open module
$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, $localIV);
//print "module = $module
" ;
mcrypt_generic_init($module, $encryptKey, $localIV);
//Padding
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$pad = $block - (strlen($encryptStr) % $block); //Compute how many characters need to pad
$encryptStr .= str_repeat(chr($pad), $pad); // After pad, the str length must be equal to block or its integer multiples
//encrypt
$encrypted = mcrypt_generic($module, $encryptStr);
//Close
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
return base64_encode($encrypted);
}
//
public function decrypt($encryptStr) {
$localIV = $this->iv;
$encryptKey = $this->encryptKey;
//Open module
$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, $localIV);
//print "module = $module
" ;
mcrypt_generic_init($module, $encryptKey, $localIV);
$encryptedData = base64_decode($encryptStr);
$encryptedData = mdecrypt_generic($module, $encryptedData);
return $encryptedData;
}
}
しかし、このクラスの
mcrypt_module_open
対応の拡張は、PHP 7以上のバージョンでは廃棄されているので、この方法も使えません.( ソリューション
疑問1:微信小プログラムは小プログラムで、支付宝小プログラムも小プログラムで、彼らの間に何か関連がありますか?疑問2:私は微信小プログラムのユーザー情報の取得を終えたばかりで、フロントエンドで暗号化データを取得してサービス側に伝え、サービス側が復号する方法を採用していますが、その間に何か関連がありますか?
これらの疑問を持って、私は再び微信のウィジェットが提供した解析demoを見ました.
class WXBizDataCrypt
{
private $appid;
private $sessionKey;
/**
*
* @param $sessionKey string
* @param $appid string appid
*/
public function __construct( $appid, $sessionKey)
{
$this->sessionKey = $sessionKey;
$this->appid = $appid;
}
/**
* , .
* @param $encryptedData string
* @param $iv string
* @param $data string
*
* @return int 0,
*/
public function decryptData( $encryptedData, $iv, &$data )
{
if (strlen($this->sessionKey) != 24) {
return ErrorCode::$IllegalAesKey;
}
$aesKey=base64_decode($this->sessionKey);
if (strlen($iv) != 24) {
return ErrorCode::$IllegalIv;
}
$aesIV=base64_decode($iv);
$aesCipher=base64_decode($encryptedData);
$result=openssl_decrypt( $aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);
$dataObj=json_decode( $result );
if( $dataObj == NULL )
{
return ErrorCode::$IllegalBuffer;
}
if( $dataObj->watermark->appid != $this->appid )
{
return ErrorCode::$IllegalBuffer;
}
$data = $result;
return ErrorCode::$OK;
}
}
同じ点:
public function decryptData( $encryptedData )
{
$key = ' aesKey';
$aesKey=base64_decode($key);
$iv = 0;
$aesIV=base64_decode($iv);
$aesCipher=base64_decode($encryptedData);
$result=openssl_decrypt( $aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);
return $result;
}
親測定が有効で、得られたデータは
{"code":"10000","msg":"Success","mobile":" "}
返される結果は、公式ドキュメントと一致します.
これにより,アリペイがユーザの機密情報を取得するPHPバックグラウンドの復号化問題が解決された.
参考資料