如何用PHP中的RSA私钥加密数据?

时间:2017-12-16 11:47:03

标签: php encryption rsa signature private-key

目前正在使用php进行基于财务的应用程序。我与第三方API集成以获取用户数据。

基于集成,他们使用签名和有效负载进行身份验证。

根据文件,我为我的观点做了一切正确的事,但我得到了回应,如

  

  SignatureDoesNotMatch      我们计算的请求签名与您提供的签名不匹配。      

根据文件:

计算签名: 必要时,请求必须由发件人签名。请求的签名计算如下:

  1. 将有效负载XML压缩为一行,如下所示: 2.删除XML声明 3.删除所有评论 4.删除所有换行符 5.Normalise空间
  2. 6)生成压缩有效负载的SHA1摘要。 7)Base16编码生成的SHA1摘要。 8)使用Perfios提供的RSA私钥加密编码的摘要。 9)Base16编码加密的摘要。

    这是我的剧本:

    $condense_payload ='<payload><vendorId>test</vendorId><txnId>dummyApplicationId</txnId><emailId>'.$emailid.'</emailId><destination>netbankingFetch</destination><returnUrl>https://www.google.com</returnUrl></payload>';
    
    
    $condense_payload_updated = trim(preg_replace('/(?<=\>)(\r?\n)|(\r?\n)(?=\<\/)/', $condense_payload));
    
    
    $payload ='
    <payload>
    <vendorId>test</vendorId>
    <txnId>dummyApplicationId</txnId>
    <emailId>'.$emailid.'</emailId>
    <destination>netbankingFetch</destination>
    <returnUrl>https://www.google.com</returnUrl>
    </payload>';
    
    /* Convert to Sha1 */
    $sha1_convert = sha1($condense_payload_updated);
    
    /* Convert to Hex (base16) */
    $first_base16_convert=bin2hex($sha1_convert);
    
    
    $fp =  fopen("private_key", "r");
    
    $private_key_string = fread($fp, 8192);
    
    fclose($fp);
    
    /* Convert to private key */
    $private_key = openssl_get_privatekey($private_key_string);
    
    $encrypted_private="";
    
    
    
    /* Encrypt digest using key */
    
    openssl_private_encrypt($first_base16_convert, $encrypted_private, $private_key, OPENSSL_PKCS1_PADDING);
    /* Convert to Hex (base16) */
    $signature=bin2hex($encrypted_private);
    

    所以这里生成了有效负载和签名,并使用post方法发送url:

    <html>
        <body onload='document.autoform.submit();'>
            <form name='autoform' method='post' action='https://demo.perfios.com/KuberaVault/insights/start'>
                <input type='hidden' name='payload' value='<?php echo $payload; ?>' />
                <input type='hidden' name='signature' value='<?php echo $signature; ?>'/>
        </body>
    </html>
    

    所以最后我得到了:

      

    SignatureDoesNotMatch请求   我们计算的签名与您提供的签名不符。    

    Api支持团队提供了java示例代码,我编译并运行java,它的工作。

    这里是java代码:

       package javaapplication1;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.io.StringReader;
    import java.security.Key;
    import java.security.KeyPair;
    import java.security.MessageDigest;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.security.Security;
    
    import javax.crypto.Cipher;
    
    import org.bouncycastle.openssl.PEMReader;
    import org.bouncycastle.util.encoders.Hex;
    
    public class JavaApplication1 {
        static String email = "test@hello.com";
        static String server = "demo.test.com";
        public static String vendor = "test";
        public static String returnURL = "https://www.google.com";
    
    
        static String privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" + 
                "MIIEpQIBAAKCAQEAx76oeNWYPkAbbSPyvJdfgfdgcPkQvAMvIHCPgY9yNdN/qsYMHytyit\n" + 
                "xdO0aTatgfS/ig4zmqKFVpC9o2YMyQ7E1FYNcl/ev++r4nV+qVXA1OKcsSv4Kbj/\n" + 
                fgdfg"iNwxGmnhBsRDswY/dEZZDN9RisRpo9NRSOskGRv+nLEA2dLgj0/f9SSzykz3cxAP\n" + 
                "bq6bV0unie5C8r6RALj+hTLU7B7QF88SkDsFx0/TYQe4H9QJtFMWJtOnhumY5Ku6\n" + 
                "CL4OdZrtz71y4ji8IxQxv2nyqdfgfd0JwYQ4rG84uzJQeRQf2RaJLhnSdfQnonDkP7L1z\n" + 
                "NSizzi8VgHI+GVIo9FrLj7DVo+fFzMucbsvvNQIDAQABAoIBAQCMVKOR+SYzneBm\n" + 
                "5hmUa2CxW5sVb7qHj54iiwLj4EYY2EnIaljjol+eh56Qrb2fpWiV3FZnQdspn/md\n" + 
                "i7W3JBngYABjwmN0/20UVL3cErVZN/XqgiFtKp2gfhfgI3BgPI/YYIWyVKRNJGt/z6Rf6\n" + 
                "0+zImQLMbUGNHkHlxuSjas+CL93sYrXo52TXqZgkfghgf40gEkQVLE+SLrtXTFiOOlX/s\n" + 
                "WWXyeUY14hl+oVQLmEO6UZd532bxAE0VlIV6Vr2pE3gJqEyaAoGgkT3inxvRPiek\n" + 
                "swRm9OONWZD9frKXYqabJTsd87623Czg5h2WGimsN4fZ+LfyBXul24KKVeMDELHn\n" + 
                "GvdRm95lAoGBAPMQRrb3iB8oYJc+4KwbtWR/vTQW++G69CeyIfD7WM0Ix3Gy0wod\n" + 
                "FwIeKSkYsZ/R5n+9Ucx/RVFv7X86YzYajhH3hl+8/q4c+L1yAGS5hW3m21gIViDt\n" + 
                "k1h3gKLI5o4EKGhCRX1teSoZ+n4G7KlYbJas8h5MX3u81GhKRmiVItr/AoGBANJg\n" + 
                "KoIhxKsyNRccULosYZBGc3vpkFtpHeZ5w0qxbXaGveUIKvqzUqonGy0o3yqVNRrH\n" + 
                "JJREHss+5/HqeuauawKUYLWapCqmVF6IlDc1PxTw+BLDzgzMlX2o43951iTMJXkd\n" + 
                "80MOujsnyTdZq7wAzR7KNR3U/OjDFlcORxhCGrnLAoGBAONZcgtp9NTP+6j8k0Ho\n" + 
                "mP5rzRmP9gHp0L3gjIbPUvxVHdhnn6ZyFzdP5sgd5ObMeoE5H+3bjYbi3o6Gmo3c\n" + 
                "wM5lbDbYnI9XYgIxQ9TzAq8NpFTvV0Btd8jj3lpk9+IWWYVLl5v+bbrHmdmPuIWd\n" + 
                "w9Qb6EwWu6kNss/pyXnBJV0ZAoGAPj+2VEsppn50tyHpwSzgsZAnG8NAs8umzUu6\n" + 
                "PZ/ChA/aoKqKDSSCkVaA9Bvj7PW5gPLsH/MIKZuzhiGbvCZgA6Nj+liHuxb8X/yJ\n" + 
                "3swink+vF95YWfEvSr9ukYm7k6fUbsIt+OmisV5Ua8xcxIR4LWQn02vyae1P7vKK\n" + 
                "luL4hYECgYEAmSiiHa4bSLF8MT/IbL2YIrxK4yatABvVWZLkAV7hiFJWeEhLCmCd\n" + 
                "OKcX8QSq9lT6TbS6NCEfHfCR0FFrny4nZMT3YnyDkgrYOiHhZL/YVfr3Izr62Gcy\n" + 
                "PizNJH/JWoNDonAuFi+eQjgBRNfd894pMeCT4tMu2nE1SOEafzykzPA=\n" + 
                "-----END RSA PRIVATE KEY-----";
    
        static final String DIGEST_ALGO = "SHA-1";
        static final String ENCRYPTION_ALGO = "RSA/ECB/PKCS1Padding";
        static String applicationId = "dummyApplicationId";
        static String perfiosTransactionId = "PLEASE UPDATE ME";
        static String format = "xml";
    
    public static String payloadStatement = "<payload>\n"
                + "<vendorId>"
                + vendor
                + "</vendorId>\n"
                + "<txnId>"
                + applicationId
                + "</txnId>\n"
                + "<yearMonthFrom>2015-06</yearMonthFrom>\n" + 
                "<yearMonthTo>2016-02</yearMonthTo>"
                + "<emailId>#email#</emailId>\n<destination>statement</destination>\n"
                + "<returnUrl>" + returnURL + "</returnUrl>\n" + "</payload>";
    
    
    
    
    
    
    
    
    
        public static void main(String[] args) throws Exception {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            if (args.length > 1 && args[0] != null) {
                if ("encrypt".equals(args[0])) {
                    if (args[1] != null) {
                        String encrypt = encrypt(args[1], ENCRYPTION_ALGO,
                                buildPublicKey(privateKey));
                        System.out.print(encrypt);
                    } else {
                        throw new Exception("Wrong number of arguments provided.");
                    }
                } else if ("signature".equals(args[0])) {
                    if (args[1] != null) {
                        String signature = getSignature(ENCRYPTION_ALGO,
                                DIGEST_ALGO, buildPrivateKey(privateKey), args[1]);
                        System.out.print(signature);
                    } else {
                        throw new Exception("Wrong number of arguments provided.");
                    }
                }
            } else {
                String argPerfiosTxnId = System.getProperty("perfiosTransactionId");
                if (argPerfiosTxnId != null)
                    perfiosTransactionId = argPerfiosTxnId;
    
                String argTxnId = System.getProperty("applicationId");
                if (argTxnId != null)
                    applicationId = argTxnId;
    
                System.out
                        .println("This program helps you try out Perfios APIs to initiate and track Perfios online transactions. "
                                + "\nIt generates HTML files that can be opened in a browser to initiate or track the transaction.");
    
                /** Create a folder for customer */
                File folder = new File(vendor);
                String message = "\n\nnetbanking and statement APIs are the APIs to start the transaction. \n"
                        + "Only integration supported to start the transaction is through autopost form as in the netbanking and statement htmls.\n"
                        + "All other APIs are xml over HTTP and do not need browser to be present.\n"
                        + "You can directly invoke those APIs using other mechanisms.\n\n"
                        + "Trying this program:\n"
                        + "\t(1)First run the program and it will generate the netbanking and statement upload files.\n"
                        + "\t(2)Depending upon whether you have requested these features to be available, you should be able to start the transactions.\n"
                        + "\t(3)netbanking and statment HTML start the perfios transaction using browser to browser integration.\n"
                        + "\t\t(3.1)To start netbanking transaction, open netbanking_* file in your browser..\n"
                        + "\t\t(3.2)To start statement upload transaction, open statement_* file in your browser..\n"
                        + "\t(4)You can then check the status of all transactions using txnstatus API.\n"
                        + "\t\t(4.1)To check the status of transaction, open txnstatus_* file in your browser. \n"
                        + "\t\t\tThis API could also be accessed without a browser. Without autoform load request\n"
                        + "\t(5)To retrieve a report, you will need to re-run the program. Change the value of perfiosTransactionId variable in your program.\n"
                        + "\t\t Compile and run the java program. open retrieve_* file in your browser. This API could also be accessed without a browser.\n"
                        + "\t\t Without autoform load request\n"
                        + "\t(5)To delete the transaction related artifacts, you will need to re-run the program. \n"
                        + "\t\tChange the value of perfiosTransactionId variable in your program. Compile and run the java program. open delete_* \n"
                        + "\t\tfile in your browser. This API could also be accessed without a browser. Without autoform load request\n"
                        + "You can pass applicationId and perfiosTransactionId through command line by providing system properties too. "
                        + "For e.g. java -DperfiosTransactionId=HDJDJ com.perfios.sample.OnlineSampleCapitalInfusionIndia\n"
                        + "\t(6)For more details please refer the API guide.\n";
    
                System.out.println(message);
    
                if (!folder.exists())
                    folder.mkdir();
    
                System.out
                        .println("Your files will be created in the following location: "
                                + folder.getAbsolutePath());
    
                /** Create files for the customer */
                String  myHTML = genericCreateHTML(JavaApplication1.payloadStatement);
                createFile("statement", myHTML);
    
    
    
    
    
    
    
    
            }
        }
    
        private static void createFile(String classification, String myHTML) {
            String filename = vendor + "/" + classification + "_" + server
                    + ".html";
    
            try {
                PrintWriter out = new PrintWriter(filename);
                out.print(myHTML);
                out.close();
                System.out.println("Successfully created file " + filename);
            } catch (Exception e) {
                System.out.println("Error while creating file " + filename);
                e.printStackTrace();
            }
        }
    
        private static String genericCreateHTML(String payload) {
            return genericCreateHTML(payload, null);
        }
    
        private static String genericCreateHTML(String payload, String operation) {
    
            String emailEncrypted = encrypt(email, ENCRYPTION_ALGO,
                    buildPublicKey(privateKey));
            payload = payload.replaceAll("\n", "");
            payload = payload.replaceAll("#email#", emailEncrypted);
    
            String signature = getSignature(ENCRYPTION_ALGO, DIGEST_ALGO,
                    buildPrivateKey(privateKey), payload);
            if (operation == null)
                operation = "start";
            String myHTML = "<html>\n"
                    + " <body onload='document.autoform.submit();'>\n"
                    + "     <form name='autoform' method='post' action='https://"
                    + server + "/KuberaVault/insights/" + operation + "'>\n"
                    + "         <input type='hidden' name='payload' value='" + payload
                    + "'>\n" + "            <input type='hidden' name='signature' value='"
                    + signature + "'>\n" + "        </form>\n" + "  </body>\n"
                    + "</html>\n";
            return myHTML;
        }
    
        public static String getSignature(String encryptAlgo, String digestAlgo,
                Key k, String xml) {
            String dig = makeDigest(xml, digestAlgo);
            return encrypt(dig, encryptAlgo, k);
        }
    
        private static PrivateKey buildPrivateKey(String privateKeySerialized) {
            StringReader reader = new StringReader(privateKeySerialized);
            PrivateKey pKey = null;
            try {
                PEMReader pemReader = new PEMReader(reader);
                KeyPair keyPair = (KeyPair) pemReader.readObject();
                pKey = keyPair.getPrivate();
                pemReader.close();
            } catch (IOException i) {
                i.printStackTrace();
            }
            return pKey;
        }
    
        private static PublicKey buildPublicKey(String privateKeySerialized) {
            StringReader reader = new StringReader(privateKeySerialized);
            PublicKey pKey = null;
            try {
                PEMReader pemReader = new PEMReader(reader);
                KeyPair keyPair = (KeyPair) pemReader.readObject();
                pKey = keyPair.getPublic();
                pemReader.close();
            } catch (IOException i) {
                i.printStackTrace();
            }
            return pKey;
        }
    
        public static String makeDigest(String payload, String digestAlgo) {
            String strDigest = "";
            try {
                MessageDigest md = MessageDigest.getInstance(digestAlgo);
                md.update(payload.getBytes("UTF-8"));
                byte[] digest = md.digest();
                byte[] encoded = Hex.encode(digest);
                strDigest = new String(encoded);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return strDigest;
        }
    
        public static String encrypt(String raw, String encryptAlgo, Key k) {
    
    
    
            String strEncrypted = "";
            try {
                Cipher cipher = Cipher.getInstance(encryptAlgo);
                cipher.init(Cipher.ENCRYPT_MODE, k);
                byte[] encrypted = cipher.doFinal(raw.getBytes("UTF-8"));
                byte[] encoded = Hex.encode(encrypted);
                strEncrypted = new String(encoded);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return strEncrypted;
        }
    
    }
    

    我的观点是,我生成了签名和有效负载,但我不知道我的脚本出了什么问题。任何人都可以指导我吗?

2 个答案:

答案 0 :(得分:1)

它尝试修复代码中的一些可能问题。也许尝试以此为基础。

<?php

$email = "FinmomentaSample@perfios.com";
$server = "demo.perfios.com";
$vendor = "finmomenta";
$returnURL = "https://www.google.com";
$applicationId = "dummyApplicationId";
$perfiosTransactionId = "PLEASE UPDATE ME";
$format = "xml";

$privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" .
    "MIIEpQIBAAKCAQEAx76oeNWYPkAbbSPyvJcPkQvAMvIHCPgY9yNdN/qsYMHytyit\n" .
    "xdO0aTatgfS/ig4zmqKFVpC9o2YMyQ7E1FYNcl/ev++r4nV+qVXA1OKcsSv4Kbj/\n" .
    "iNwxGmnhBsRDswY/dEZZDN9RisRpo9NRSOskGRv+nLEA2dLgj0/f9SSzykz3cxAP\n" .
    "bq6bV0unie5C8r6RALj+hTLU7B7QF88SkDsFx0/TYQe4H9QJtFMWJtOnhumY5Ku6\n" .
    "CL4OdZrtz71y4ji8IxQxv2nyq0JwYQ4rG84uzJQeRQf2RaJLhnSdfQnonDkP7L1z\n" .
    "NSizzi8VgHI+GVIo9FrLj7DVo+fFzMucbsvvNQIDAQABAoIBAQCMVKOR+SYzneBm\n" .
    "5hmUa2CxW5sVb7qHj54iiwLj4EYY2EnIaljjol+eh56Qrb2fpWiV3FZnQdspn/md\n" .
    "i7W3JBngYABjwmN0/20UVL3cErVZN/XqgiFtKp2I3BgPI/YYIWyVKRNJGt/z6Rf6\n" .
    "0+zImQLMbUGNHkHlxuSjas+CL93sYrXo52TXqZgk40gEkQVLE+SLrtXTFiOOlX/s\n" .
    "WWXyeUY14hl+oVQLmEO6UZd532bxAE0VlIV6Vr2pE3gJqEyaAoGgkT3inxvRPiek\n" .
    "swRm9OONWZD9frKXYqabJTsd87623Czg5h2WGimsN4fZ+LfyBXul24KKVeMDELHn\n" .
    "GvdRm95lAoGBAPMQRrb3iB8oYJc+4KwbtWR/vTQW++G69CeyIfD7WM0Ix3Gy0wod\n" .
    "FwIeKSkYsZ/R5n+9Ucx/RVFv7X86YzYajhH3hl+8/q4c+L1yAGS5hW3m21gIViDt\n" .
    "k1h3gKLI5o4EKGhCRX1teSoZ+n4G7KlYbJas8h5MX3u81GhKRmiVItr/AoGBANJg\n" .
    "KoIhxKsyNRccULosYZBGc3vpkFtpHeZ5w0qxbXaGveUIKvqzUqonGy0o3yqVNRrH\n" .
    "JJREHss+5/HqeuauawKUYLWapCqmVF6IlDc1PxTw+BLDzgzMlX2o43951iTMJXkd\n" .
    "80MOujsnyTdZq7wAzR7KNR3U/OjDFlcORxhCGrnLAoGBAONZcgtp9NTP+6j8k0Ho\n" .
    "mP5rzRmP9gHp0L3gjIbPUvxVHdhnn6ZyFzdP5sgd5ObMeoE5H+3bjYbi3o6Gmo3c\n" .
    "wM5lbDbYnI9XYgIxQ9TzAq8NpFTvV0Btd8jj3lpk9+IWWYVLl5v+bbrHmdmPuIWd\n" .
    "w9Qb6EwWu6kNss/pyXnBJV0ZAoGAPj+2VEsppn50tyHpwSzgsZAnG8NAs8umzUu6\n" .
    "PZ/ChA/aoKqKDSSCkVaA9Bvj7PW5gPLsH/MIKZuzhiGbvCZgA6Nj+liHuxb8X/yJ\n" .
    "3swink+vF95YWfEvSr9ukYm7k6fUbsIt+OmisV5Ua8xcxIR4LWQn02vyae1P7vKK\n" .
    "luL4hYECgYEAmSiiHa4bSLF8MT/IbL2YIrxK4yatABvVWZLkAV7hiFJWeEhLCmCd\n" .
    "OKcX8QSq9lT6TbS6NCEfHfCR0FFrny4nZMT3YnyDkgrYOiHhZL/YVfr3Izr62Gcy\n" .
    "PizNJH/JWoNDonAuFi+eQjgBRNfd894pMeCT4tMu2nE1SOEafzykzPA=\n" .
    "-----END RSA PRIVATE KEY-----";


$payloadStatement = "<payload>\n" .
    "<vendorId>" . $vendor . "</vendorId>\n" .
    "<txnId>" . $applicationId . "</txnId>\n" .
    "<yearMonthFrom>2017-06</yearMonthFrom>\n" .
    "<yearMonthTo>2017-12</yearMonthTo>" .
    "<emailId>#email#</emailId>\n<destination>statement</destination>\n" .
    "<returnUrl>" . $returnURL . "</returnUrl>\n" .
    "</payload>";


echo genericCreateHTML($payloadStatement, 'start', $email, $server, $privateKey);

function genericCreateHTML($payload, $operation, $email, $server, $privateKey)
{
    $email = encryptData($email, $privateKey);
    $payload = str_replace("#email#", $email, $payload);

    // Remove all line breaks
    $payload = str_replace("\n", "", $payload);

    $signature = getSignature($payload, $privateKey);

    $html = "<html>\n" . " <body onload='document.autoform.submit();'>\n" .
        "     <form name='autoform' method='post' action='https://" . $server . "/KuberaVault/insights/" . $operation . "'>\n" .
        "         <input type='hidden' name='payload' value='" . $payload . "'>\n" .
        "            <input type='hidden' name='signature' value='" . $signature . "'>\n" .
        "        </form>\n" . "  </body>\n" .
        "</html>\n";

    return $html;
}

function getSignature($data, $privateKey)
{
    // Make digest
    $digest = sha1($data);

    // Encrypt
    return encryptData($digest, $privateKey);
}

function encryptData($raw, $privateKey)
{
    $privateKey = openssl_pkey_get_private($privateKey);

    if (!$privateKey) {
        throw new RuntimeException('Invalid private key or passphrase');
    }

    // Encrypt digest using the key
    $encrypted = "";
    openssl_private_encrypt($raw, $encrypted, $privateKey, OPENSSL_PKCS1_PADDING);

    // Convert to Hex (base16)
    $result = bin2hex($encrypted);

    return $result;
}

答案 1 :(得分:-1)

如果有人希望在go lang中获得与openssl_pkey_get_private等效的选项,则可以引用https://github.com/hixichen/golang_openssl_private_encrypt_rsa/blob/master/openssl_private_encrypt.go