Keys and Addresses
Now that we know what a wallet actually is, how do we actually generate one? First we generate a private key, which is simply a 64 character hexadecimal string. This represents a number between the range 0 and 2^{256}(1.15792089e77). From this number, the rest of the “Account” information is derived. For our purposes an account will consist of your Private Key, WIF (Wallet Import Format), public key, and address.
This random source can technically be generated from any source of entropy, but it SHOULD be generated through some form of cryptographic number generation. Most modern programming languages support some form of Private Key Generation via a secure random function available in a standard library.
The first real challenge of any wallet software is deriving all of the account information from the generated private key. Let's go into detail on how each piece of information is derived.
WIF
The WIF is relatively easy to understand. In practice, a private key can end up looking something like this:
It would be nice to have something that is a bit more humanreadable, so we can convert the private key into the WIF, otherwise known as the wallet import format:
Although this is still not entirely readable, it is certainly better than the original string. The WIF also has some basic error checking, so that when you send to an address denominated by WIF format, you are more likely to catch an error. The conversion from the raw private key to the WIF format was done via a Base58 check encoding algorithm.
Base58 check encoding
Base58 is similar to the common Base64 encoding scheme, except that it removes nonalphanumeric characters as well as characters that might look similar to each other to the human eye. For example 0 (zero), O (capital o), I (capital i) and l (lower case L) are all omitted from the Base58 encoding scheme. The full list of available characters in Neo's Base58 encoding is:
A full implementation of Neo's check encoding (written in Go) can be seen below:
The steps to perform the check encoding can be broken down as follows:

Prepend the version byte

Double hash the resulting hex using SHA256

Append the first four bytes of the hash to the prepended version

Convert the hex with prepended version and appended checksum to Base58

If there any leading zeros in the bytes, attach 1
So to go from the original private key described above to the WIF format we can use this simple function:
We can see that WIF is an encoding algorithm of the private key which provides basic error checking, and improves human readability by encoding it into Base58 and attaching a version and checksum.
Public key derivation
Generally cryptocurrencies use the form of cryptography called Ellipticcurve cryptography. It is used to derive public key from private key whilst being computationally infeasible to do the opposite.
The form of elliptic curve equation is the following:
y^{2}= x^{3}+ ax + b
Bitcoin uses an elliptic curve called secp256k1, while Neo uses secp256r1, where k  means Koblitz and r  means Random. Essentially, secp256k1's parameters were chosen in a way that allows more efficient calculation (for a very small security tradeoff), while secp256r1's parameters were chosen randomly.
The secp256k1 equation is:
y^{2}= x^{3}+ 7
The secp256r1 equation is:
y^{2}= x^{3} 3x + b, wherebis41058363725152142129326129780047268409114441015993725554835256314039467401291
Because of the largebused in secp256r1, below we will use secp256k1 for explanation, but in principle it is the same.
This is what the secp256k1 curve looks like:
Firstly we'll explain how to do point addition on an elliptic curve.
Having pointsPandQ, we draw a line that goes through both of them, and find the third point on the intersection of the curve and the line. Then we reflect that point across the xaxis to getR.P + Q = R
But in elliptic cryptography, rather than adding two arbitrary points together, we add the specified base point on the curve to itself. We draw a line tangent to the curve at the pointP, then apply the same rules as above. *P + P = 2 * P*
Remember that the private key is a 256bit number? Basically, the public key is the result ofPadding to itselfxtimes, wherexis our private key.
*X = x * P*, whereXis the public key.
To do the opposite (figure outxfromXandP) we would need to keep addingPto itself until we getX, which would, on average, make us do2^{128}point additions to figure outx, which is computationally infeasible.
ECDSA signing
Elliptic Curve Digital Signature Algorithm (ECDSA) is a simulation of Digital Signature Algorithm (DSA) by ECC algorithm. Its advantages include fast speed, reliable strength, and a short signature.
The brief steps are as follows:
Assume private key, public key, and base point ask,K, andGrespectively. We know that *K = k * G* according to the ECC algorithm.
Signing procedure

Select random numberrand compute point *r * G(x, y)*.

Compute *s = (h + k * x) / r* according to random numberr, messageM's hash valueh, private keyk.

Send messageMand signature {*r * G*,s} to receiver.
Verification procedure

Receiver receives messageMand signature {*r * G(x, y)*,s}.

Compute hashhaccording to received message.

Compute *h * G / s + x * K / s* with sender public keyKand compare with *r * G*. Verification succeeds if both are the same.
Deduction is as follows
Neo Address
A Neo address is generated from the address script, which defines who can spend a transaction output.
Usually the script used is of the form:
PUSHBYTES21opcode (0x21) + compressed public key (33 bytes) +CHECKSIGopcode (0xAC), meaning the output could be spent only by the owner of the private key for the specified public key.
To calculate a Neo address from transaction script: 1. Calculate SHA256 hash of transaction script 2. Calculate RIPEMD160 hash of the previous output (this is known as the script hash) 3. Use Base58 check to encode previous output with the version 0x17 (meaning result will start with A)
Below you will find example code to generate a Neo address from a public key:
The script hash is typically used in smart contracts as the public identifier, as opposed to the address. Since the use of byte arrays is common, it makes a lot more sense as the Base58 encoded versions is meant to be read by humans, not computers!