Why b = a is accepte
Because pointers to "non-const T" are implicitly convertible to pointers to "const T". The result of the conversion still points to an object of type T; only the cv-qualifier of the type differs.
while bp = ap rejected?
Because a pointer to "pointer to non-const T" is not implicitly convertible to a pointer to a pointer to "pointer to const T". The result would be a pointer to an object of different type (const T* vs T*), both of which happen to have the same cv-qualifier (non-const).
How if I really want the effect of bp = ap
This depends on what kind of analogy you are wanting. You can create a pointer to const, and then point bp to that:
int const * ap_const = &a;
bp = &ap_const;
But you should really think about why you want the effect. Perhaps it isn't what you need and you should want something else.
Sidenote 1: Dynamically allocating a singular int is quite rarely useful. Avoid unnecessary dynamic allocation.
Sidenote 2: When you do need dynamic allocation, avoid using bare owning pointers such as in the example. Prefer RAII containers and smart pointers instead.