├── LICENSE ├── README.markdown ├── __init__.py ├── app.py ├── backend.db ├── database.py ├── decorators.py ├── models.py ├── settings.py ├── ssl ├── certs │ ├── myca.crt │ └── server.crt ├── index.txt ├── index.txt.attr ├── index.txt.old ├── newcerts │ └── 01.pem ├── openssl.my.cnf ├── private │ ├── myca.key │ └── server.key ├── serial ├── serial.old └── server.csr └── templates └── statement.html /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Security Compass 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright notice, 9 | this list of conditions and the following disclaimer in the documentation 10 | and/or other materials provided with the distribution. 11 | 3. All advertising materials mentioning features or use of this software 12 | must display the following acknowledgement: 13 | This product includes software developed by Security Compass. 14 | 4. Neither the name of Security Compass nor the names of its contributors may be 15 | used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY Security Compass ''AS IS'' AND ANY EXPRESS OR IMPLIED 19 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 20 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 21 | EVENT SHALL Security Compass BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | b Server 2 | ========== 3 | 4 | This is the server component used with the Android and iPhone security labs available at https://github.com/SecurityCompass/AndroidLabs and https://github.com/SecurityCompass/iPhoneLabs 5 | 6 | Setup 7 | ----- 8 | 9 | You'll need to install: 10 | 11 | * blinker 12 | * cherrypy 13 | * flask 14 | * flask-sqlalchemy 15 | * simplejson 16 | 17 | Run: 18 | 19 | easy_install blinker cherrypy flask flask-sqlalchemy simplejson 20 | 21 | Using 22 | ----- 23 | 24 | To run the HTTP server on port 8080 25 | 26 | python app.py 27 | 28 | To run the HTTPS server on port 8443 29 | 30 | python app.py --ssl --port 8443 31 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SecurityCompass/LabServer/757ce46146b39d1fbaf2f5400ff782a56b6de180/__init__.py -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import getopt 2 | import sys 3 | from flask import Flask, request, render_template, request_started 4 | from cherrypy import wsgiserver 5 | from functools import wraps 6 | from models import User, Account, Session 7 | from database import db_session 8 | import simplejson as json 9 | import datetime 10 | app = Flask(__name__) 11 | 12 | jsonify = json.dumps 13 | 14 | """ 15 | * Error table 16 | | E1 | incorrect username or password | 17 | | E2 | invalid session key | 18 | | E3 | account does not exist | 19 | | E4 | balance too low | 20 | | E5 | forbidden | 21 | | E6 | permission denied | 22 | 23 | * Success table 24 | | S1 | transfer complete | 25 | """ 26 | 27 | DEFAULT_PORT = 8080 28 | 29 | def consolelog(sender): 30 | print " [*] %s [%s] \"%s %s\"" % (request.remote_addr, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), request.method, request.url) 31 | 32 | 33 | @app.errorhandler(500) 34 | def internal_server_error(error): 35 | print " [!]", error 36 | return "Internal Server Error", 500 37 | 38 | def error(text): 39 | return jsonify({"error" : text}) 40 | 41 | def success(text): 42 | return jsonify({"success" : text}) 43 | 44 | def validate_session(f): 45 | @wraps(f) 46 | def decorated_f(*args, **kwargs): 47 | s = Session.get_by_key(request.args["session_key"]) 48 | if s == None: 49 | return error("E2") 50 | return f(s, *args, **kwargs) 51 | return decorated_f 52 | 53 | @app.route('/login', methods=['POST']) 54 | def login(): 55 | u = User.query.filter(User.username == request.form["username"]).first() 56 | if not u or u.password != request.form["password"]: 57 | return error("E1") 58 | 59 | s = Session.get_by_user(u) 60 | if s is not None: 61 | db_session.delete(s) 62 | db_session.commit() 63 | 64 | s = Session(u) 65 | db_session.add(s) 66 | db_session.commit() 67 | 68 | return jsonify(s.values) 69 | 70 | 71 | @app.route('/user', methods = ['GET']) 72 | @validate_session 73 | def user(session): 74 | return jsonify(session.user.values) 75 | 76 | 77 | 78 | @app.route('/accounts') 79 | @validate_session 80 | def account(session): 81 | return jsonify([a.values for a in session.user.accounts]) 82 | 83 | 84 | @app.route('/account/balance') 85 | @validate_session 86 | def balance(session): 87 | return jsonify({"balance" : session.user.account.balance_formatted}) 88 | 89 | @app.route('/statement') 90 | @validate_session 91 | def statement(session): 92 | return render_template('statement.html', accounts=session.user.accounts) 93 | 94 | @app.route('/transfer', methods=['POST']) 95 | @validate_session 96 | def transfer(session): 97 | #set accounts from the request 98 | from_account = Account.query.filter(Account.account_number == int(request.form["from_account"])).first() 99 | to_account = Account.query.filter(Account.account_number == int(request.form["to_account"])).first() 100 | 101 | if not from_account or not to_account: #validate that accounts exist 102 | return error("E3") 103 | 104 | #parse sent value and transform into cents 105 | if request.form["amount"].find('.') != -1: 106 | (dollars, cents) = request.form["amount"].split('.') 107 | total_cents = int(dollars)*100 + int(cents) 108 | else: 109 | total_cents = int(request.form["amount"]) * 100 110 | 111 | #validate that balance is big enough 112 | if from_account.balance < total_cents: 113 | return error("E4") 114 | #transfer money 115 | to_account.balance += total_cents 116 | from_account.balance -= total_cents 117 | db_session.commit() 118 | return success("S1") 119 | 120 | def usage(): 121 | print "Runs the FalseSecure Mobile server" 122 | print "Arguments: " 123 | print " --debug enable debug mode" 124 | print " --port p serve on port p (default 8080)" 125 | print " --ssl enable SSL" 126 | print " --help print this message" 127 | 128 | if __name__ == '__main__': 129 | port = DEFAULT_PORT 130 | ssl = False 131 | opts, args = getopt.getopt(sys.argv[1:], "", ["ssl", "debug", "help", "port="]) 132 | for o, a in opts: 133 | if o == "--help": 134 | usage() 135 | sys.exit(2) 136 | elif o == "--debug": 137 | app.debug = True 138 | elif o == "--ssl": 139 | ssl = True 140 | wsgiserver.CherryPyWSGIServer.ssl_certificate = "ssl/certs/server.crt" 141 | wsgiserver.CherryPyWSGIServer.ssl_private_key = "ssl/private/server.key" 142 | elif o == "--port": 143 | port = int(a) 144 | 145 | d = wsgiserver.WSGIPathInfoDispatcher({'/': app}) 146 | server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', port), d, timeout=200) 147 | 148 | request_started.connect(consolelog, app) 149 | 150 | print "Serving %s on port %d %s" % ("HTTP" if not ssl else "HTTPS", port, "(debug enabled)" if app.debug else "") 151 | 152 | try: 153 | server.start() 154 | except KeyboardInterrupt: 155 | server.stop() 156 | 157 | -------------------------------------------------------------------------------- /backend.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SecurityCompass/LabServer/757ce46146b39d1fbaf2f5400ff782a56b6de180/backend.db -------------------------------------------------------------------------------- /database.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.orm import scoped_session, sessionmaker 3 | from sqlalchemy.ext.declarative import declarative_base 4 | 5 | engine = create_engine('sqlite:///backend.db', convert_unicode=True) 6 | db_session = scoped_session(sessionmaker(autocommit=False, 7 | autoflush=False, 8 | bind=engine)) 9 | Base = declarative_base() 10 | Base.query = db_session.query_property() 11 | 12 | def init_db(): 13 | # import all modules here that might define models so that 14 | # they will be registered properly on the metadata. Otherwise 15 | # you will have to import them first before calling init_db() 16 | import models 17 | Base.metadata.create_all(bind=engine) 18 | -------------------------------------------------------------------------------- /decorators.py: -------------------------------------------------------------------------------- 1 | gfrom flask import request 2 | from models import * 3 | from app import error 4 | def validate_session(func): 5 | s = Session.get_by_key(request.args["session_key"]) 6 | if s == None: 7 | return lambda : error("invalid session key") 8 | return func 9 | -------------------------------------------------------------------------------- /models.py: -------------------------------------------------------------------------------- 1 | import os 2 | import base64 3 | 4 | from datetime import datetime 5 | from sqlalchemy import Column, Integer, String, ForeignKey, DateTime 6 | from sqlalchemy.orm import relationship, backref 7 | from database import Base, db_session 8 | import settings 9 | 10 | class User(Base): 11 | __tablename__ = 'users' 12 | id = Column(Integer, primary_key=True) 13 | username = Column(String(50), unique=True) 14 | password = Column(String(50)) 15 | first_name = Column(String(50)) 16 | last_name = Column(String(50)) 17 | 18 | def __init__(self, username=None, password=None, first_name=None, last_name=None): 19 | self.username = username 20 | self.password = password 21 | self.first_name = first_name 22 | self.last_name = last_name 23 | 24 | def __repr__(self): 25 | return '' % (self.username) 26 | 27 | @property 28 | def values(self): 29 | return {"username" : self.username, 30 | "first_name" : self.first_name, 31 | "last_name" : self.last_name, 32 | } 33 | 34 | class Account(Base): 35 | __tablename__ = 'accounts' 36 | id = Column(Integer, primary_key=True) 37 | account_number = Column(Integer, unique=True) 38 | type = Column(String(50)) 39 | 40 | #this is stored in cents! 41 | balance = Column(Integer) 42 | 43 | #users and accounts have a many to one 44 | user_id = Column(Integer, ForeignKey('users.id')) 45 | user = relationship(User, backref=backref('accounts')) 46 | 47 | def __init__(self, account_number=None, type=type, balance=None, user=None): 48 | self.account_number = account_number 49 | self.type = type 50 | self.balance = balance 51 | self.user = user 52 | 53 | def __repr__(self): 54 | return '' % (self.account_number) 55 | 56 | @property 57 | def balance_formatted(self): 58 | return "%d.%02d" % ((self.balance / 100), (self.balance % 100)) 59 | 60 | @property 61 | def values(self): 62 | return {"account_number" : self.account_number, 63 | "type" : self.type, 64 | "balance" : self.balance_formatted, 65 | } 66 | 67 | 68 | class Session(Base): 69 | __tablename__ = "sessions" 70 | id = Column(Integer, primary_key=True) 71 | user_id = Column(Integer, ForeignKey('users.id'), unique=True) 72 | user = relationship(User) 73 | key = Column(String(50), unique=True) 74 | created = Column(DateTime) 75 | 76 | def __init__(self, user=None): 77 | self.user = user 78 | self.key = base64.encodestring(os.urandom(24)).strip() 79 | self.created = datetime.now() 80 | 81 | def __repr__(self): 82 | return '' % (self.key) 83 | 84 | @property 85 | def values(self): 86 | return {"username" : self.user.username, 87 | "key" : self.key, 88 | "created" : str(self.created), 89 | } 90 | @classmethod 91 | def get_by_key(cls, key): 92 | s = cls.query.filter(cls.key == key).first() 93 | #print datetime.now() - s.created 94 | if s and datetime.now() - s.created > settings.SESSION_LIFETIME: 95 | s = None 96 | return s 97 | 98 | @classmethod 99 | def get_by_user(cls, user): 100 | s = cls.query.filter(cls.user == user).first() 101 | if s and datetime.now() - s.created > settings.SESSION_LIFETIME: 102 | s.query.delete() 103 | db_session.commit() 104 | s = None 105 | return s 106 | 107 | 108 | -------------------------------------------------------------------------------- /settings.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | SESSION_LIFETIME = datetime.timedelta(minutes=5) 3 | -------------------------------------------------------------------------------- /ssl/certs/myca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDKDCCApGgAwIBAgIJAKW647SRHfslMA0GCSqGSIb3DQEBBAUAMGwxFDASBgNV 3 | BAoTC0ZhbHNlU2VjdXJlMRAwDgYDVQQHEwdNeSBUb3duMRwwGgYDVQQIExNTdGF0 4 | ZSBvciBQcm92aWRlbmNlMQswCQYDVQQGEwJVUzEXMBUGA1UEAxMORmFsc2VTZWN1 5 | cmUgQ0EwHhcNMTEwNTE2MTgyMzE4WhcNMTYwNTE0MTgyMzE4WjBsMRQwEgYDVQQK 6 | EwtGYWxzZVNlY3VyZTEQMA4GA1UEBxMHTXkgVG93bjEcMBoGA1UECBMTU3RhdGUg 7 | b3IgUHJvdmlkZW5jZTELMAkGA1UEBhMCVVMxFzAVBgNVBAMTDkZhbHNlU2VjdXJl 8 | IENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDWCIOeWRrOYAza95Ek17Bk 9 | frpW/98thOscs2S0X7ptJjGIifbRfe/tcVT2vYsUkoP5EQJz7toYUPsx3QqlLb68 10 | xtRDznG3tGZLRwT/ejOvRsUMfVnJmfXxi9z0T8Jab6arkyFPw9YIfNu/hkkARXtf 11 | JYnOt13nRpASavxno/V/DwIDAQABo4HRMIHOMAwGA1UdEwQFMAMBAf8wHQYDVR0O 12 | BBYEFBVKEALb7ekfqY+uAipBxniCq0WXMIGeBgNVHSMEgZYwgZOAFBVKEALb7ekf 13 | qY+uAipBxniCq0WXoXCkbjBsMRQwEgYDVQQKEwtGYWxzZVNlY3VyZTEQMA4GA1UE 14 | BxMHTXkgVG93bjEcMBoGA1UECBMTU3RhdGUgb3IgUHJvdmlkZW5jZTELMAkGA1UE 15 | BhMCVVMxFzAVBgNVBAMTDkZhbHNlU2VjdXJlIENBggkApbrjtJEd+yUwDQYJKoZI 16 | hvcNAQEEBQADgYEAWm/YxB3cGYDPfzyBlLGMZjFKzHcXuVGuVdExvsj9qfBHSPRu 17 | vf7GSv5b9dfMEUbkiaNOpJb0r557FrFEn3/jL6HWO/zSsqW+hH4JwfH2kme3VHAp 18 | 4owPH6m41FWCzCPrnq6kjCGCYLB/PynfZOqFleOu6QBEcCe6srQKs9uP3cc= 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /ssl/certs/server.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 1 (0x0) 4 | Serial Number: 1 (0x1) 5 | Signature Algorithm: md5WithRSAEncryption 6 | Issuer: O=FalseSecure, L=My Town, ST=State or Providence, C=US, CN=FalseSecure CA 7 | Validity 8 | Not Before: May 16 18:25:08 2011 GMT 9 | Not After : May 15 18:25:08 2012 GMT 10 | Subject: C=US, ST=State or Providence, O=FalseSecure Mobile, CN=localhost 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | RSA Public Key: (1024 bit) 14 | Modulus (1024 bit): 15 | 00:b6:ef:bf:ce:c7:e7:25:5a:23:d2:23:1c:b6:23: 16 | d6:5e:0e:86:f3:b9:38:d4:b4:67:fb:66:6e:ad:82: 17 | b4:d1:9e:cc:e7:64:7c:44:5f:6b:7e:1c:6e:cb:4d: 18 | db:1e:36:32:ab:eb:3f:a3:a9:0c:c0:9e:83:38:48: 19 | 15:a5:ba:90:01:97:99:f2:87:03:05:ab:5b:35:bd: 20 | bb:40:49:49:9b:b7:e8:ea:b9:0f:00:a1:a2:d8:21: 21 | 78:6d:35:7b:fa:03:4e:44:d3:11:7e:db:a9:fc:3a: 22 | 00:be:2e:72:6a:d3:86:ab:cf:db:e5:cf:e9:ac:4d: 23 | 54:1b:70:aa:56:c2:31:2c:9b 24 | Exponent: 65537 (0x10001) 25 | Signature Algorithm: md5WithRSAEncryption 26 | 91:dc:43:69:78:d9:bb:be:0f:f0:79:2b:af:2f:a8:34:99:24: 27 | ea:49:5c:a8:5b:0f:5a:f5:7d:2e:80:4a:dc:87:a2:b2:99:85: 28 | 9b:b4:38:15:e7:2b:4f:3a:37:6b:61:ea:46:a7:03:09:bf:2a: 29 | 5a:27:ec:aa:02:c8:54:70:c6:0d:10:77:26:a3:4d:f1:f3:f6: 30 | 64:e9:81:f0:6a:ee:be:85:cc:67:21:37:9a:66:bc:f3:fc:1e: 31 | 04:6c:b9:c8:4e:4e:67:4f:2b:88:cf:a4:96:77:90:f1:cf:19: 32 | 60:7a:fd:d8:e5:f2:0d:0d:6d:c6:e1:a1:ab:6a:a4:95:f1:ff: 33 | 93:0c 34 | -----BEGIN CERTIFICATE----- 35 | MIICNzCCAaACAQEwDQYJKoZIhvcNAQEEBQAwbDEUMBIGA1UEChMLRmFsc2VTZWN1 36 | cmUxEDAOBgNVBAcTB015IFRvd24xHDAaBgNVBAgTE1N0YXRlIG9yIFByb3ZpZGVu 37 | Y2UxCzAJBgNVBAYTAlVTMRcwFQYDVQQDEw5GYWxzZVNlY3VyZSBDQTAeFw0xMTA1 38 | MTYxODI1MDhaFw0xMjA1MTUxODI1MDhaMFwxCzAJBgNVBAYTAlVTMRwwGgYDVQQI 39 | ExNTdGF0ZSBvciBQcm92aWRlbmNlMRswGQYDVQQKExJGYWxzZVNlY3VyZSBNb2Jp 40 | bGUxEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC 41 | gYEAtu+/zsfnJVoj0iMctiPWXg6G87k41LRn+2ZurYK00Z7M52R8RF9rfhxuy03b 42 | HjYyq+s/o6kMwJ6DOEgVpbqQAZeZ8ocDBatbNb27QElJm7fo6rkPAKGi2CF4bTV7 43 | +gNORNMRftup/DoAvi5yatOGq8/b5c/prE1UG3CqVsIxLJsCAwEAATANBgkqhkiG 44 | 9w0BAQQFAAOBgQCR3ENpeNm7vg/weSuvL6g0mSTqSVyoWw9a9X0ugErch6KymYWb 45 | tDgV5ytPOjdrYepGpwMJvypaJ+yqAshUcMYNEHcmo03x8/Zk6YHwau6+hcxnITea 46 | Zrzz/B4EbLnITk5nTyuIz6SWd5Dxzxlgev3Y5fINDW3G4aGraqSV8f+TDA== 47 | -----END CERTIFICATE----- 48 | -------------------------------------------------------------------------------- /ssl/index.txt: -------------------------------------------------------------------------------- 1 | V 120515182508Z 01 unknown /C=US/ST=State or Providence/O=FalseSecure Mobile/CN=localhost 2 | -------------------------------------------------------------------------------- /ssl/index.txt.attr: -------------------------------------------------------------------------------- 1 | unique_subject = yes 2 | -------------------------------------------------------------------------------- /ssl/index.txt.old: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SecurityCompass/LabServer/757ce46146b39d1fbaf2f5400ff782a56b6de180/ssl/index.txt.old -------------------------------------------------------------------------------- /ssl/newcerts/01.pem: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 1 (0x0) 4 | Serial Number: 1 (0x1) 5 | Signature Algorithm: md5WithRSAEncryption 6 | Issuer: O=FalseSecure, L=My Town, ST=State or Providence, C=US, CN=FalseSecure CA 7 | Validity 8 | Not Before: May 16 18:25:08 2011 GMT 9 | Not After : May 15 18:25:08 2012 GMT 10 | Subject: C=US, ST=State or Providence, O=FalseSecure Mobile, CN=localhost 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | RSA Public Key: (1024 bit) 14 | Modulus (1024 bit): 15 | 00:b6:ef:bf:ce:c7:e7:25:5a:23:d2:23:1c:b6:23: 16 | d6:5e:0e:86:f3:b9:38:d4:b4:67:fb:66:6e:ad:82: 17 | b4:d1:9e:cc:e7:64:7c:44:5f:6b:7e:1c:6e:cb:4d: 18 | db:1e:36:32:ab:eb:3f:a3:a9:0c:c0:9e:83:38:48: 19 | 15:a5:ba:90:01:97:99:f2:87:03:05:ab:5b:35:bd: 20 | bb:40:49:49:9b:b7:e8:ea:b9:0f:00:a1:a2:d8:21: 21 | 78:6d:35:7b:fa:03:4e:44:d3:11:7e:db:a9:fc:3a: 22 | 00:be:2e:72:6a:d3:86:ab:cf:db:e5:cf:e9:ac:4d: 23 | 54:1b:70:aa:56:c2:31:2c:9b 24 | Exponent: 65537 (0x10001) 25 | Signature Algorithm: md5WithRSAEncryption 26 | 91:dc:43:69:78:d9:bb:be:0f:f0:79:2b:af:2f:a8:34:99:24: 27 | ea:49:5c:a8:5b:0f:5a:f5:7d:2e:80:4a:dc:87:a2:b2:99:85: 28 | 9b:b4:38:15:e7:2b:4f:3a:37:6b:61:ea:46:a7:03:09:bf:2a: 29 | 5a:27:ec:aa:02:c8:54:70:c6:0d:10:77:26:a3:4d:f1:f3:f6: 30 | 64:e9:81:f0:6a:ee:be:85:cc:67:21:37:9a:66:bc:f3:fc:1e: 31 | 04:6c:b9:c8:4e:4e:67:4f:2b:88:cf:a4:96:77:90:f1:cf:19: 32 | 60:7a:fd:d8:e5:f2:0d:0d:6d:c6:e1:a1:ab:6a:a4:95:f1:ff: 33 | 93:0c 34 | -----BEGIN CERTIFICATE----- 35 | MIICNzCCAaACAQEwDQYJKoZIhvcNAQEEBQAwbDEUMBIGA1UEChMLRmFsc2VTZWN1 36 | cmUxEDAOBgNVBAcTB015IFRvd24xHDAaBgNVBAgTE1N0YXRlIG9yIFByb3ZpZGVu 37 | Y2UxCzAJBgNVBAYTAlVTMRcwFQYDVQQDEw5GYWxzZVNlY3VyZSBDQTAeFw0xMTA1 38 | MTYxODI1MDhaFw0xMjA1MTUxODI1MDhaMFwxCzAJBgNVBAYTAlVTMRwwGgYDVQQI 39 | ExNTdGF0ZSBvciBQcm92aWRlbmNlMRswGQYDVQQKExJGYWxzZVNlY3VyZSBNb2Jp 40 | bGUxEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC 41 | gYEAtu+/zsfnJVoj0iMctiPWXg6G87k41LRn+2ZurYK00Z7M52R8RF9rfhxuy03b 42 | HjYyq+s/o6kMwJ6DOEgVpbqQAZeZ8ocDBatbNb27QElJm7fo6rkPAKGi2CF4bTV7 43 | +gNORNMRftup/DoAvi5yatOGq8/b5c/prE1UG3CqVsIxLJsCAwEAATANBgkqhkiG 44 | 9w0BAQQFAAOBgQCR3ENpeNm7vg/weSuvL6g0mSTqSVyoWw9a9X0ugErch6KymYWb 45 | tDgV5ytPOjdrYepGpwMJvypaJ+yqAshUcMYNEHcmo03x8/Zk6YHwau6+hcxnITea 46 | Zrzz/B4EbLnITk5nTyuIz6SWd5Dxzxlgev3Y5fINDW3G4aGraqSV8f+TDA== 47 | -----END CERTIFICATE----- 48 | -------------------------------------------------------------------------------- /ssl/openssl.my.cnf: -------------------------------------------------------------------------------- 1 | # 2 | # OpenSSL configuration file. 3 | # 4 | 5 | # Establish working directory. 6 | 7 | dir = . 8 | 9 | [ ca ] 10 | default_ca = CA_default 11 | 12 | [ CA_default ] 13 | serial = $dir/serial 14 | database = $dir/index.txt 15 | new_certs_dir = $dir/newcerts 16 | certificate = $dir/certs/myca.crt 17 | private_key = $dir/private/myca.key 18 | default_days = 365 19 | default_md = md5 20 | preserve = no 21 | email_in_dn = no 22 | nameopt = default_ca 23 | certopt = default_ca 24 | policy = policy_anything 25 | 26 | [ policy_anything ] 27 | countryName = optional 28 | stateOrProvinceName = optional 29 | organizationName = optional 30 | organizationalUnitName = optional 31 | commonName = optional 32 | emailAddress = optional 33 | 34 | [ req ] 35 | default_bits = 1024 # Size of keys 36 | default_keyfile = key.pem # name of generated keys 37 | default_md = md5 # message digest algorithm 38 | string_mask = nombstr # permitted characters 39 | distinguished_name = req_distinguished_name 40 | req_extensions = v3_req 41 | 42 | [ req_distinguished_name ] 43 | # Variable name Prompt string 44 | #------------------------- ---------------------------------- 45 | 0.organizationName = Organization Name (company) 46 | organizationalUnitName = Organizational Unit Name (department, division) 47 | emailAddress = Email Address 48 | emailAddress_max = 40 49 | localityName = Locality Name (city, district) 50 | stateOrProvinceName = State or Province Name (full name) 51 | countryName = Country Name (2 letter code) 52 | countryName_min = 2 53 | countryName_max = 2 54 | commonName = Common Name (hostname, IP, or your name) 55 | commonName_max = 64 56 | 57 | # Default values for the above, for consistency and less typing. 58 | # Variable name Value 59 | #------------------------ ------------------------------ 60 | 0.organizationName_default = My Company 61 | localityName_default = My Town 62 | stateOrProvinceName_default = State or Providence 63 | countryName_default = US 64 | 65 | [ v3_ca ] 66 | basicConstraints = CA:TRUE 67 | subjectKeyIdentifier = hash 68 | authorityKeyIdentifier = keyid:always,issuer:always 69 | 70 | [ v3_req ] 71 | basicConstraints = CA:FALSE 72 | subjectKeyIdentifier = hash 73 | -------------------------------------------------------------------------------- /ssl/private/myca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,1C2B62E3A1A6F45B 4 | 5 | Xk/Zd+Iq54ofdwA5/51tpB/8syhJVKOY/KbLyZLr9tDtK9ssStQYN7IVrpzVfmeU 6 | MBevkabZWCeFAFgqW75luNHBOW0VFTmeomoCMJG/JZ4IUS6rY5KtsJoitYPwggm2 7 | grNajcInO81/wAbopS32SVW7ckoGGgOu68PjTH8/Pzsn9gsly7ya0l1snsxGSC9b 8 | bW7u96P2AsogoMUhDadxKbUC5TqkcAGIAkN76MRb8Y32nZap7+cFNf23UmNXbPFX 9 | Bt6HDcjd2febZpj3H3cMcF1nNUG8Xg5jTIMT5cL+Fvb7xW5lR2iVlH/FaTs+V3SO 10 | lcKGbt+0Q971Pc82QlySqCVM6D8VTMmnffIgDZMlkcM6ffaPs4ww32DeQOk+BNWs 11 | qVc6NbyZuRjNh7E5CGIHDJhAId1lY7xU8A7ZtChqF8584NAgtq0GlAqKEdlNRfBY 12 | z/g1McpIVena9SgLuwnM7UfyKDFZulOL4xPhxEfST/bVCakNJWg6LQ7eBqdBOP3e 13 | wHBs9fufyGKJDenZrmiTvzVjM35Tl/2uVEr+EW3BBh43ID5hPorKdnUmJN6OweiD 14 | 4ajP7ETUgLd5fA9wvmlzWeWYNYEC5Jzo42q4TzrYFzFv2olVLig0x1YZd6cS/1X8 15 | CmOnCJDo/H7hJTDehg0HkVSdfb9Ym2C2QDW2CWOEUu4sA1Du0kdVyJLuktaHD4b4 16 | 4OE22OfKenWfDMuXQQGOZU7Rc+cEa1z810JXN4417YlVxIZ6chrfIqAOVMMGOJJg 17 | hB4xFH1ZR81BVqnHKZn/fFMMW7S2TcxG7yFfYycg+H0SnRyT5HNvkg== 18 | -----END RSA PRIVATE KEY----- 19 | -------------------------------------------------------------------------------- /ssl/private/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICWwIBAAKBgQC277/Ox+clWiPSIxy2I9ZeDobzuTjUtGf7Zm6tgrTRnsznZHxE 3 | X2t+HG7LTdseNjKr6z+jqQzAnoM4SBWlupABl5nyhwMFq1s1vbtASUmbt+jquQ8A 4 | oaLYIXhtNXv6A05E0xF+26n8OgC+LnJq04arz9vlz+msTVQbcKpWwjEsmwIDAQAB 5 | AoGAO7Icu1OMYCn8CnSex+kF6QeAjkPhAbglPmp8r245pzXU06V8z6r7ntmUZg9v 6 | 83noKyXHTJa7rJeNZm5PchdhKjZGpt+nMI6cZctkP5XFFnjm1fA/vOLPFmoPOxOk 7 | BpNgIGMwMY31WqCyNazWzzGWQkegm+7pbcHNis4LcixzT2ECQQDnwvu/7IJgC0A5 8 | KeV+A+1v9RhAkMFPQli/ZSKNIoLYDXd8/pRSmnkfeafaetVB1gM7iKgo2nOJkzZr 9 | 76fAwQzzAkEAyhGOuhZXv4KQtIt6Omv1meLvCwoMcNqhIEQeEblewaQ1INKHusBC 10 | v/kjH7LHGRs4ldKitnwrMwoG2hZmCPYruQJAAOxI3mHNvx4sWk3taQwQ9QIv34n0 11 | CyHDgrefZJp/lOaw1CGba5zL4LE2VutoUAPPLXyE1uzPyvt4GwPwUhcyuwJAe57u 12 | MI5GtJUOLkY+SsuNf83D7H8DSW+Xr4O82Z8Mh28MSb5C50+EBqpGgLgA8i0jGxW4 13 | Eeg5DYqO0pFtbwIbkQJADv8wQZ2PkF5ikHN1pvg4vvAZs/pRhDVlNCUNc3q55c0S 14 | cpgkHrdJsccsIeGoCO6W2hGMZIMmSFDnT2FJ3gWwdw== 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /ssl/serial: -------------------------------------------------------------------------------- 1 | 02 2 | -------------------------------------------------------------------------------- /ssl/serial.old: -------------------------------------------------------------------------------- 1 | 01 2 | -------------------------------------------------------------------------------- /ssl/server.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIB6TCCAVICAQAwbjEbMBkGA1UEChMSRmFsc2VTZWN1cmUgTW9iaWxlMRAwDgYD 3 | VQQHEwdNeSBUb3duMRwwGgYDVQQIExNTdGF0ZSBvciBQcm92aWRlbmNlMQswCQYD 4 | VQQGEwJVUzESMBAGA1UEAxMJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GN 5 | ADCBiQKBgQC277/Ox+clWiPSIxy2I9ZeDobzuTjUtGf7Zm6tgrTRnsznZHxEX2t+ 6 | HG7LTdseNjKr6z+jqQzAnoM4SBWlupABl5nyhwMFq1s1vbtASUmbt+jquQ8AoaLY 7 | IXhtNXv6A05E0xF+26n8OgC+LnJq04arz9vlz+msTVQbcKpWwjEsmwIDAQABoDsw 8 | OQYJKoZIhvcNAQkOMSwwKjAJBgNVHRMEAjAAMB0GA1UdDgQWBBRAVXwgl08owWsF 9 | sN8bc068EfjsCjANBgkqhkiG9w0BAQQFAAOBgQCGG5VkVS2oiK/faYkELKLeLSoy 10 | WK7ygqym8wCjzXCAa8EexkyR4Gvlw/dHg2L4/c86hI4OufBeje3Z6xdisdgyiUg8 11 | MkraAMw+fd7HrcHXKD7fG3sT05pd8VNX3bXwwDokxrMd1y78EJSykqm8jKZp/nds 12 | 4vakFYkOr7Ug1ce+rA== 13 | -----END CERTIFICATE REQUEST----- 14 | -------------------------------------------------------------------------------- /templates/statement.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Statement 4 |

Statement

5 | {% for a in accounts %} 6 |

7 | {{ a.type|capitalize }} Account: {{ a.account_number }}
8 | Balance: {{ a.balance_formatted }} 9 |

10 | {% endfor %} 11 | --------------------------------------------------------------------------------