locked
Paypal - Encrypted Website Payments (EWP) Solution RRS feed

  • Question

  • User1363794232 posted

    Integrating encrypted paypal buttons is a pain! Hopefully this helps.

    Basically it is a 4 stage process. I have put it together from postings on other sites. None of this is my own work so I can't take credit for it but I'm putting it all here in one place to hopefully save you time.

    The code is provided AS IS. It works for me but it may not work for you. I offer no guarantees and I am unable to offer any help with modifying it. I host my applications at maximumasp.com so I can tell you this works on their VPS servers (Gratuitous plug: Maximumasp are great and my refferal number is DSHD-6726).

    The sites where I found this information are:
    http://www.pdncommunity.com/pdn/board/message?board.id=ewp&thread.id=4
    http://www.paypaldeveloper.com/pdn/board/message?board.id=ewp&message.id=373&query.id=33453#M373
    http://dotnetdiscussion.net/2008/02/27/aspnet-how-to-integrate-both-google-checkout-and-paypal-in-3-steps/
    https://www.paypal.com/IntegrationCenter/ic_button-encryption.html#step2

     

    The p12 certificate

    Download and install Win32 OpenSSL from Shining Light Productions. Then Create a private key: 
        openssl genrsa -out my-prvkey.pem 1024

    Create a public cert using the private key:
        openssl req -new -key my-prvkey.pem -x509 -days 3650 -out my-pubcert.pem

    Export the private key and public cert to PKCS#12 format:
        openssl pkcs12 -export -inkey my-prvkey.pem -in my-pubcert.pem -out mycert.p12

    You will be prompted for creating a password when you export to PKCS#12 format. Remember the password. You will need the .p12 file and its password when you use this class.

    If you are on a shared virtual hosting plan, you may need to contact your hosting company to get them to install the certificate on your server. Send them the .p12 certificate and it's password.

    Upload your certificate to your PayPal account to get your "cert_id" for the code behind, download the PayPal certificate and put it in your App_Data folder, then block non encrypted payments. All here: https://www.paypal.com/IntegrationCenter/ic_button-encryption.html#step2

     

    The Form

    Add these variables between the form tags of your aspx page
    <input type="hidden" name="cmd" value="_s-xclick">
    <asp:Literal runat="server" id="strEncrypt" />

    Add an asp button with the postback url set to either the sandbox or live paypal site
    <asp:Button ID="btnPayPal" runat="server" Text="Pay with PayPal" PostBackUrl="https://www.sandbox.paypal.com/cgi-bin/webscr" />

     

    The code behind

    This encrypts your form variables and writes a hidden variable named "encrypted" to the literal "strEncrypt". Put this where it will be executed before the user can click "btnPayPal" (e.g. Page_Load).

    Add
    Imports com.paypal.demo

    Dim paypalCertPath As String = Server.MapPath("~/App_Data/paypal_cert_pem.txt")
    Dim signerPfxPath As String = Server.MapPath("~/App_Data/mycert.p12")
    Dim signerPfxPassword As String = "mycert.p12 password" 'testing only - get from secure place when live.
    Dim clearText As String = "cmd=_xclick" & vbLf & _
    "business=you@yourbusiness.com" & vbLf & _
    "currency_code=GBP" & vbLf & _
    "item_name=Subscription" & vbLf & _
    "amount=15.00" & vbLf & _
    "no_shipping=2" & vbLf & _
    "no_note=1" & vbLf & _
    "return=https://www.yoursite.com/return.aspx" & vbLf & _
    "cancel_return=https://www.yoursite.com/cancel.aspx" & vbLf & _
    "cert_id=C2XRTSNRF7E2S"
    Dim ewp As New ButtonEncryption()
    ewp.LoadSignerCredential(signerPfxPath, signerPfxPassword)
    ewp.RecipientPublicCertPath = paypalCertPath
    Dim result As String = ewp.SignAndEncrypt(clearText)

    strEncrypt.Text = "<input type=""hidden"" name=""encrypted"" value=""" & result & """ />"

    The class

    This class has been slightly modified from PayPal_HarryX's original to prevent a "System.Security.Cryptography.CryptographicException: The system cannot find the file specified" error. It has also been converted to VB. You can download the original C# version from PayPal_HarryX's post but you need to change:

    _signerCert = new X509Certificate2(signerPfxCertPath, signerPfxCertPassword);

    to

    _signerCert = new X509Certificate2(File.ReadAllBytes(signerPfxCertPath), signerPfxCertPassword, X509KeyStorageFlags.MachineKeySet);

    And add the namespaces
    using system.IO
    using System.Security.Cryptography.X509Certificates

    Here is the original. And here is the fix.

    Save this class in the App_Code folder of your application.

    Here is the modified VB code.

    Imports Microsoft.VisualBasic
    Imports System
    Imports System.Web
    Imports System.Collections.Generic
    Imports System.Text
    Imports System.Security.Cryptography
    Imports Pkcs = System.Security.Cryptography.Pkcs
    Imports X509 = System.Security.Cryptography.X509Certificates
    Imports System.Security.Cryptography.X509Certificates
    Imports System.IO

    Namespace com.paypal.demo

    Public Class ButtonEncryption
    Private _encoding As Encoding = Encoding.[Default]
    Private _recipientPublicCertPath As String
    Private _signerCert As X509.X509Certificate2
    Private _recipientCert As X509.X509Certificate2

    Public Sub New()

    End Sub

    #Region "Properties"
    ''' <summary>
    ''' Character encoding, e.g. UTF-8, Windows-1252
    ''' </summary>

    Public Property Charset() As String
    Get
    Return _encoding.WebName
    End Get
    Set(ByVal value As String)
    If value IsNot Nothing AndAlso value <> "" Then
    _encoding = Encoding.GetEncoding(value)
    End If
    End Set
    End Property

    ''' <summary>
    ''' Path to the recipient's public certificate in PEM format
    ''' </summary>

    Public Property RecipientPublicCertPath() As String
    Get
    Return _recipientPublicCertPath
    End Get
    Set(ByVal value As String)
    _recipientPublicCertPath = value
    _recipientCert =
    New X509.X509Certificate2(_recipientPublicCertPath)
    End Set
    End Property
    #End
    Region

    ''' <summary>
    '''
    ''' </summary>
    ''' <param name="signerPfxCertPath">File path to the signer's public certificate plus private key in PKCS#12 format</param>
    ''' <param name="signerPfxCertPassword">Password for signer's private key</param>

    Public Sub LoadSignerCredential(ByVal signerPfxCertPath As String, ByVal signerPfxCertPassword As String)
    Try
    _signerCert = New X509.X509Certificate2(File.ReadAllBytes(signerPfxCertPath), signerPfxCertPassword, X509KeyStorageFlags.MachineKeySet)
    Catch ex As Exception
    Throw New Exception("<b>Signer Cert Path: " & signerPfxCertPath & "</b><br><br>" & ex.ToString())
    End Try
    End Sub

    ''' <summary>
    ''' Sign a message and encrypt it for the recipient.
    ''' </summary>
    ''' <param name="clearText">Name value pairs must be separated by \n (vbLf or Chr(10)), for example "cmd=_xclick\nbusiness=..."</param>
    ''' <returns></returns>

    Public Function SignAndEncrypt(ByVal clearText As String) As String
    Dim result As String = Nothing
    Dim messageBytes As Byte() = _encoding.GetBytes(clearText)
    Dim signedBytes As Byte() = Sign(messageBytes)
    Dim encryptedBytes As Byte() = Envelope(signedBytes)
    result = Base64Encode(encryptedBytes)
    Return result
    End Function

    Private Function Sign(ByVal messageBytes As Byte()) As Byte()
    Dim content As New Pkcs.ContentInfo(messageBytes)
    Dim signed As New Pkcs.SignedCms(content)
    Dim signer As New Pkcs.CmsSigner(_signerCert)
    signed.ComputeSignature(signer)
    Dim signedBytes As Byte() = signed.Encode()
    Return signedBytes
    End Function

    Private Function Envelope(ByVal contentBytes As Byte()) As Byte()
    Dim content As New Pkcs.ContentInfo(contentBytes)
    Dim envMsg As New Pkcs.EnvelopedCms(content)
    Dim recipient As New Pkcs.CmsRecipient(Pkcs.SubjectIdentifierType.IssuerAndSerialNumber, _recipientCert)
    envMsg.Encrypt(recipient)
    Dim encryptedBytes As Byte() = envMsg.Encode()
    Return encryptedBytes
    End Function

    Private Function Base64Encode(ByVal encoded As Byte()) As String
    Const PKCS7_HEADER As String = "-----BEGIN PKCS7-----"
    Const PKCS7_FOOTER As String = "-----END PKCS7-----"
    Dim base64 As String = Convert.ToBase64String(encoded)
    Dim formatted As New StringBuilder()
    formatted.Append(PKCS7_HEADER)
    formatted.Append(base64)
    formatted.Append(PKCS7_FOOTER)
    Return formatted.ToString()
    End Function

    End Class
    End
    Namespace

    Good Luck!

    If this post was helpful to you, Please "Mark As Answer". 

     

    Friday, March 21, 2008 10:53 PM

All replies

  • User67265616 posted

    Great work! I have integrated your code and released an open source library on Github that takes care of all of this. http://github.com/axefrog/XMerchant

    Monday, August 9, 2010 4:41 AM
  • User524842929 posted

    It is great that you have organized all the information we need to generate EWP for PayPal. I just want to share a FREE software that can encapsulate  the usage of OpenSSL command line utilities within a rich GUI windows form offered by SpiceLogic. Here is the link

    here is the screenshot:

     

    By the way, SpiceLogic offers a complete ASP.NET Web Form Control / MVC Component that can offer you a tight object oriented interface for generating EWP as shown here.

     

     

    Or Programmatically (usually for MVC)

     

     

    Monday, March 14, 2011 9:13 PM