bunq API Documentation
SDK'sPostman Collection
  • Getting Started
    • Welcome to the bunq API documentation
    • Tools
      • Software Development Kits (SDKs)
        • PHP
          • Usage
          • Tests
          • Exceptions
        • Java
          • Usage
          • Tests
          • Exceptions
        • Python
          • Usage
          • Tests
          • Exceptions
        • C#
          • Usage
          • Tests
          • Exceptions
      • Postman
      • Android Emulator
      • Developers Portal
  • Basics
    • bunq API Objects
      • User
      • Monetary Account
      • Payment
      • RequestInquiry
      • Card
      • Attachment and Note Attachment
    • API Context, Device Installation and Session
    • Authentication
      • API Keys
      • OAuth
    • Pagination
    • Errors
    • Rate Limits
    • Response body formatting
    • Moving to production
    • Headers
  • NOT SO BASICS
    • Signing
      • Python Code Example
        • Full main.py
        • Full bunq_lib.py
        • Full signing.py
      • PHP Code Example
    • Callbacks (Webhooks)
  • PSD2
    • Are you a Third Party Provider (TPP)? Start here!
      • Register as a TPP
      • Change your avatar
    • Account Information Service Provider (AISP)
    • Payment Initiation Service Provider (PISP)
    • Card-Based Payment Instrument Issuer (CBPII)
  • Support
    • FAQ
    • bunq status page
    • Terms and Conditions
  • TUTORIALS
    • Your first payment
      • Introduction
      • Creating a sandbox user and getting an API key
      • Creating the API Context
        • Creating the Installation
        • Device Registration
        • Start a Session
      • Setting up a sandbox user
        • Retrieving my user details
        • Getting sandbox money on the user account
        • Sandbox version of the bunq app
      • First Payments
    • Receiving payments on your website using bunq.me
    • How to manage your cards
      • Introduction
      • Ordering a card
      • Setting the card Limit and changing the PIN code
  • API Reference
    • Start here
    • Additional Transaction Information Category
    • Additional Transaction Information Category User Defined
    • Attachment
    • Attachment Public
    • Avatar
    • Billing Contract Subscription
    • bunqme
      • bunqme Tab
      • bunqme Fundraiser Profile
      • bunqme Tab Response
      • bunqme Fundraiser Result
    • Callback URL OAuth
    • Cards
      • Card
      • Card-Batch
      • Card Credit
      • Card Debit
      • Card Name
      • Card Replace
  • Confirmation Of Funds
  • Content and Exports
  • Currency Cloud
    • Currency cloud Benificiairy
    • Payment Quote
  • Currency Conversion
    • Convert
    • Quotes
  • Customer Statements
  • Devices
  • Draft Payment
  • Event
  • Exports
    • Export Annual Overview
    • Export RIB
    • Export Statement Card
  • Generated CVC2
  • Ideal Merchant Transaction
  • Insights
  • Installation
  • Invoice
  • Invoice Export
  • Legal Name
  • Limit
  • Mastercard Action
  • Monetary Account
    • Monetary Account Bank
    • Monetary Account Card
    • Monetary Account External
    • Monetary Account External Savings
    • Monetary Account Joint
    • Monetary Account Savings
    • Monetary Account Savings External
  • Name
  • Note Text & Attachment
    • Adyen Card Transaction
    • Switch Service Payment
    • bunqme fundraiser result
    • Draft Payment
    • Ideal Merchant Transaction
    • Mastercard Action
    • Open Banking Merchant
    • Payment Batch
    • Payment Delayed
    • Payment
    • Request Inquiry Batch
    • Request Response
    • Schedule Payment
    • Schedule Request
    • Sofort
    • Whitelist Result
  • Notification Filter
    • Notification Filter Email
    • Notification Filter Failure
    • Notification Filter Push
    • Notification Filter URL
  • OAuth
  • Payment
    • Payment
    • Payment Auto Allocate
    • Payment Batch
  • Payment Auto Allocation
  • Payment Service Provider
    • Payment Service Provider Credential
    • Payment Service Provider Draft Payment
    • Payment Service Provider Issuer Transaction
  • Request
    • Request Inquiry
    • Request Inquiry Batch
    • Request Response
  • Sandbox Users
  • Schedule
    • Schedule Instance
    • Schedule Payment
    • Schedule Payment Batch
  • Server Error
  • Server Public Key
  • Session
  • [deprecated] Share Invite Monetary Account Inquiry
  • Share Invite Monetary Account Response
  • Sofort Merchant Transaction
  • Statement
  • Switch Service Payment
  • Token QR Request Sofort
  • Transferwise
    • Transferwise Currency
    • Transferwise Quote
    • Transferwise Recipient
    • Transferwise Recipient Requirement
    • Transferwise Transfer
    • Transferwise Transfer Requirement
    • Transferwise User
  • Tree Progress
  • User
    • User Person
    • User Company
    • User Payment Service Provider
  • Whitelist SSD
    • Whitelist SSD One Off
    • Whitelist SSD Recurring
  • Content
Powered by GitBook
On this page

Was this helpful?

  1. NOT SO BASICS
  2. Signing
  3. Python Code Example

Full signing.py

import os
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding, rsa
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key
import base64
import hashlib


# Function to generate RSA key pair
def generate_rsa_key_pair():
    private_key_file = 'private_key.pem'
    public_key_file = 'public_key.pem'
    
    # Check if the key files exist
    if os.path.exists(private_key_file) and os.path.exists(public_key_file):
        # Read the existing keys from the text files
        with open(private_key_file, 'r') as private_file:
            private_key_pem = private_file.read()

        with open(public_key_file, 'r') as public_file:
            public_key_pem = public_file.read()

        print("bunq - using existing keypair")
    else:
        # Generate new RSA keys with 2048 bits as required by Bunq
        private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048,
            backend=default_backend()
        )
        public_key = private_key.public_key()

        # Serialize private key to PEM format (PKCS#8 as required by Bunq)
        private_key_pem = private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.PKCS8,
            encryption_algorithm=serialization.NoEncryption()
        ).decode('utf-8')

        # Serialize public key to PEM format
        public_key_pem = public_key.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo
        ).decode('utf-8')

        # Save the keys to text files
        with open(private_key_file, 'w') as private_file:
            private_file.write(private_key_pem)

        with open(public_key_file, 'w') as public_file:
            public_file.write(public_key_pem)

        print("bunq - creating new keypair [KEEP THESE FILES SAFE]")

    return private_key_pem, public_key_pem


def load_private_key(private_key_pem):
    """Load a private key from PEM format."""
    return load_pem_private_key(private_key_pem.encode(), password=None, backend=default_backend())


def load_public_key(public_key_pem):
    """Load a public key from PEM format."""
    return serialization.load_pem_public_key(
        public_key_pem.encode(),
        backend=default_backend()
    )


def sign_data(data, private_key_pem):
    """Signs the given data with the provided private key using SHA256 and PKCS#1 v1.5 padding.
    
    Args:
        data (str): The data to sign (should be the JSON request body)
        private_key_pem (str): The private key in PEM format
    
    Returns:
        str: Base64 encoded signature
    """
    private_key = load_private_key(private_key_pem)
    
    # Ensure the data is encoded in UTF-8 exactly as it will be sent
    encoded_data = data.encode('utf-8')

    # Debug: Print exact bytes being signed
    print("\n[DEBUG] Signing Data Bytes:", encoded_data)
    print("[DEBUG] SHA256 Hash of Data:", hashlib.sha256(encoded_data).hexdigest())

    # Generate signature using SHA256 and PKCS#1 v1.5 padding as required by Bunq
    signature = private_key.sign(
        encoded_data,
        padding.PKCS1v15(),
        hashes.SHA256()
    )

    # Encode in Base64 (as required by Bunq API)
    encoded_signature = base64.b64encode(signature).decode('utf-8')

    # Debug: Print signature
    print("[DEBUG] Base64 Encoded Signature:", encoded_signature)

    return encoded_signature


def verify_response(response_body, signature, server_public_key_pem):
    """Verifies the server's response signature.
    
    Args:
        response_body (str): The response body as a string
        signature (str): The base64 encoded signature from X-Bunq-Server-Signature header
        server_public_key_pem (str): The server's public key in PEM format
    
    Returns:
        bool: True if signature is valid, False otherwise
    """
    try:
        # Load the server's public key
        public_key = load_public_key(server_public_key_pem)
        
        # Decode the base64 signature
        decoded_signature = base64.b64decode(signature)
        
        # Verify the signature
        public_key.verify(
            decoded_signature,
            response_body.encode('utf-8'),
            padding.PKCS1v15(),
            hashes.SHA256()
        )
        return True
    except Exception as e:
        print(f"[ERROR] Signature verification failed: {e}")
        return False

PreviousFull bunq_lib.pyNextPHP Code Example

Last updated 2 months ago

Was this helpful?