There seemed to be a great deal of confusion about whether Keystone PKI tokens were encrypted or signed.

I spent a few days digging into this.

Initial Setup: Keystone Creates a Self-Signed Certificate and Becomes the Root CA

Utilizing OpenSSL, Keystone sets itself up as the root CA by generating a self-signed cert:

openssl genrsa -out /etc/keystone/ssl/certs/cakey.pem 1024 -config /etc/keystone/ssl/certs/openssl.conf
openssl req -new -x509 -extensions v3_ca -passin pass:None -key /etc/keystone/ssl/certs/cakey.pem -out /etc/keystone/ssl/certs/ca.pem -days 3650 -config /etc/keystone/ssl/certs/openssl.conf -subj /C=US/ST=Unset/L=Unset/O=Unset/CN=www.example.com

Generate Signing Certificate

Now that Keystone has established itself as the root CA, it is ready to generate legitimate certs. We will generate a signing certificate. This certificate is going to be used to sign our token info:

openssl req -out /etc/keystone/ssl/certs/req.pem -config /etc/keystone/ssl/certs/openssl.conf -subj /C=US/ST=Unset/L=Unset/O=Unset/CN=www.example.com
openssl ca -batch -out /etc/keystone/ssl/certs/signing_cert.pem -config /etc/keystone/ssl/certs/openssl.conf -infiles /etc/keystone/ssl/certs/req.pem

If we did not want to use Keystone as the root CA, we could generate a request here and send it off to an established CA like Verisign. Verisign would then send back our signing cert.

Initial Authentication

When a user initially authenticates with Keystone, it will authenticate the user and grab data about the user from the Keystone DB:

{
 "access": {
     "metadata": {
         "is_admin": 0,
         "roles": [
             "9fe2ff9ee4384b1894a90878d3e92bab",
             "9ccd9139439e41ceac20a5e33e81555b"
         ]
     },
     "serviceCatalog": [
         {
             "endpoints": [
                 {
                     "adminURL": "http://10.0.10.10:8776/v1/5dd0ede8bcc44f26ae8983f03249b4de",
                     "id": "4be86b9f26564ffc9b40fa1f68eb1603",
                     "internalURL": "http://10.0.10.10:8776/v1/5dd0ede8bcc44f26ae8983f03249b4de",
                     "publicURL": "http://10.0.10.10:8776/v1/5dd0ede8bcc44f26ae8983f03249b4de",
                     "region": "RegionOne"
                 }
             ],
             "endpoints_links": [],
             "name": "volume",
             "type": "volume"
         },
         {
             "endpoints": [
                 {
                     "adminURL": "http://10.0.10.10:9292",
                     "id": "5655ca5092b14a63a5a6a1882426bce6",
                     "internalURL": "http://10.0.10.10:9292",
                     "publicURL": "http://10.0.10.10:9292",
                     "region": "RegionOne"
                 }
             ],
             "endpoints_links": [],
             "name": "glance",
             "type": "image"
         },
         {
             "endpoints": [
                 {
                     "adminURL": "http://10.0.10.10:8774/v2/5dd0ede8bcc44f26ae8983f03249b4de",
                     "id": "5721a24df1c3492da5532ed71d13ebe0",
                     "internalURL": "http://10.0.10.10:8774/v2/5dd0ede8bcc44f26ae8983f03249b4de",
                     "publicURL": "http://10.0.10.10:8774/v2/5dd0ede8bcc44f26ae8983f03249b4de",
                     "region": "RegionOne"
                 }
             ],
             "endpoints_links": [],
             "name": "nova",
             "type": "compute"
         },
         {
             "endpoints": [
                 {
                     "adminURL": "http://10.0.10.10:9696",
                     "id": "5ab1bbb07fa644a0b95c1a1cad3061be",
                     "internalURL": "http://10.0.10.10:9696",
                     "publicURL": "http://10.0.10.10:9696",
                     "region": "RegionOne"
                 }
             ],
             "endpoints_links": [],
             "name": "quantum",
             "type": "network"
         },
         {
             "endpoints": [
                 {
                     "adminURL": "http://10.0.10.10:35357/v2.0",
                     "id": "6e89262f99b54900a066d1310c2e4c87",
                     "internalURL": "http://10.0.10.10:5000/v2.0",
                     "publicURL": "http://10.0.10.10:5000/v2.0",
                     "region": "RegionOne"
                 }
             ],
             "endpoints_links": [],
             "name": "keystone",
             "type": "identity"
         }
     ],
     "token": {
         "expires": "2013-10-12T20:02:49Z",
         "id": "placeholder",
         "issued_at": "2013-10-11T20:02:49.623814",
         "tenant": {
             "description": "Default Tenant",
             "enabled": true,
             "id": "5dd0ede8bcc44f26ae8983f03249b4de",
             "name": "demo"
         }
     },
     "user": {
         "id": "a366ed02b00f4cc6ac9af6f6d5990778",
         "name": "admin",
         "roles": [
             {
                 "name": "_member_"
             },
             {
                 "name": "admin"
             }
         ],
         "roles_links": [],
         "username": "admin"
     }
 }
}

Sign & Encode

Keystone then signs the data above with both the signing cert and associated signing private key:

openssl cms -sign -in mytext.json -signer /etc/keystone/ssl/certs/signing_cert.pem -inkey /etc/keystone/ssl/private/signing_key.pem -outform PEM -nosmimecap -nodetach -nocerts -noattr

The result is our signed info encoded, not encrypted, in PEM format:

-----BEGIN CMS-----
MIIJWAYJKoZIhvcNAQcCoIIJSTCCCUUCAQExCTAHBgUrDgMCGjCCCDEGCSqGSIb3
DQEHAaCCCCIEgggeeyJhY2Nlc3MiOiB7InRva2VuIjogeyJpc3N1ZWRfYXQiOiAi
MjAxMy0xMC0xMVQyMDowMjo0OS42MjM4MTQiLCAiZXhwaXJlcyI6ICIyMDEzLTEw
LTEyVDIwOjAyOjQ5WiIsICJpZCI6ICJwbGFjZWhvbGRlciIsICJ0ZW5hbnQiOiB7
ImRlc2NyaXB0aW9uIjogIkRlZmF1bHQgVGVuYW50IiwgImVuYWJsZWQiOiB0cnVl
LCAiaWQiOiAiNWRkMGVkZThiY2M0NGYyNmFlODk4M2YwMzI0OWI0ZGUiLCAibmFt
ZSI6ICJkZW1vIn19LCAic2VydmljZUNhdGFsb2ciOiBbeyJlbmRwb2ludHMiOiBb
eyJhZG1pblVSTCI6ICJodHRwOi8vMTAuMC4xMC4xMDo4Nzc2L3YxLzVkZDBlZGU4
YmNjNDRmMjZhZTg5ODNmMDMyNDliNGRlIiwgInJlZ2lvbiI6ICJSZWdpb25PbmUi
LCAiaW50ZXJuYWxVUkwiOiAiaHR0cDovLzEwLjAuMTAuMTA6ODc3Ni92MS81ZGQw
ZWRlOGJjYzQ0ZjI2YWU4OTgzZjAzMjQ5YjRkZSIsICJpZCI6ICI0YmU4NmI5ZjI2
NTY0ZmZjOWI0MGZhMWY2OGViMTYwMyIsICJwdWJsaWNVUkwiOiAiaHR0cDovLzEw
LjAuMTAuMTA6ODc3Ni92MS81ZGQwZWRlOGJjYzQ0ZjI2YWU4OTgzZjAzMjQ5YjRk
ZSJ9XSwgImVuZHBvaW50c19saW5rcyI6IFtdLCAidHlwZSI6ICJ2b2x1bWUiLCAi
bmFtZSI6ICJ2b2x1bWUifSwgeyJlbmRwb2ludHMiOiBbeyJhZG1pblVSTCI6ICJo
dHRwOi8vMTAuMC4xMC4xMDo5MjkyIiwgInJlZ2lvbiI6ICJSZWdpb25PbmUiLCAi
aW50ZXJuYWxVUkwiOiAiaHR0cDovLzEwLjAuMTAuMTA6OTI5MiIsICJpZCI6ICI1
NjU1Y2E1MDkyYjE0YTYzYTVhNmExODgyNDI2YmNlNiIsICJwdWJsaWNVUkwiOiAi
aHR0cDovLzEwLjAuMTAuMTA6OTI5MiJ9XSwgImVuZHBvaW50c19saW5rcyI6IFtd
LCAidHlwZSI6ICJpbWFnZSIsICJuYW1lIjogImdsYW5jZSJ9LCB7ImVuZHBvaW50
cyI6IFt7ImFkbWluVVJMIjogImh0dHA6Ly8xMC4wLjEwLjEwOjg3NzQvdjIvNWRk
MGVkZThiY2M0NGYyNmFlODk4M2YwMzI0OWI0ZGUiLCAicmVnaW9uIjogIlJlZ2lv
bk9uZSIsICJpbnRlcm5hbFVSTCI6ICJodHRwOi8vMTAuMC4xMC4xMDo4Nzc0L3Yy
LzVkZDBlZGU4YmNjNDRmMjZhZTg5ODNmMDMyNDliNGRlIiwgImlkIjogIjU3MjFh
MjRkZjFjMzQ5MmRhNTUzMmVkNzFkMTNlYmUwIiwgInB1YmxpY1VSTCI6ICJodHRw
Oi8vMTAuMC4xMC4xMDo4Nzc0L3YyLzVkZDBlZGU4YmNjNDRmMjZhZTg5ODNmMDMy
NDliNGRlIn1dLCAiZW5kcG9pbnRzX2xpbmtzIjogW10sICJ0eXBlIjogImNvbXB1
dGUiLCAibmFtZSI6ICJub3ZhIn0sIHsiZW5kcG9pbnRzIjogW3siYWRtaW5VUkwi
OiAiaHR0cDovLzEwLjAuMTAuMTA6OTY5NiIsICJyZWdpb24iOiAiUmVnaW9uT25l
IiwgImludGVybmFsVVJMIjogImh0dHA6Ly8xMC4wLjEwLjEwOjk2OTYiLCAiaWQi
OiAiNWFiMWJiYjA3ZmE2NDRhMGI5NWMxYTFjYWQzMDYxYmUiLCAicHVibGljVVJM
IjogImh0dHA6Ly8xMC4wLjEwLjEwOjk2OTYifV0sICJlbmRwb2ludHNfbGlua3Mi
OiBbXSwgInR5cGUiOiAibmV0d29yayIsICJuYW1lIjogInF1YW50dW0ifSwgeyJl
bmRwb2ludHMiOiBbeyJhZG1pblVSTCI6ICJodHRwOi8vMTAuMC4xMC4xMDozNTM1
Ny92Mi4wIiwgInJlZ2lvbiI6ICJSZWdpb25PbmUiLCAiaW50ZXJuYWxVUkwiOiAi
aHR0cDovLzEwLjAuMTAuMTA6NTAwMC92Mi4wIiwgImlkIjogIjZlODkyNjJmOTli
NTQ5MDBhMDY2ZDEzMTBjMmU0Yzg3IiwgInB1YmxpY1VSTCI6ICJodHRwOi8vMTAu
MC4xMC4xMDo1MDAwL3YyLjAifV0sICJlbmRwb2ludHNfbGlua3MiOiBbXSwgInR5
cGUiOiAiaWRlbnRpdHkiLCAibmFtZSI6ICJrZXlzdG9uZSJ9XSwgInVzZXIiOiB7
InVzZXJuYW1lIjogImFkbWluIiwgInJvbGVzX2xpbmtzIjogW10sICJpZCI6ICJh
MzY2ZWQwMmIwMGY0Y2M2YWM5YWY2ZjZkNTk5MDc3OCIsICJyb2xlcyI6IFt7Im5h
bWUiOiAiX21lbWJlcl8ifSwgeyJuYW1lIjogImFkbWluIn1dLCAibmFtZSI6ICJh
ZG1pbiJ9LCAibWV0YWRhdGEiOiB7ImlzX2FkbWluIjogMCwgInJvbGVzIjogWyI5
ZmUyZmY5ZWU0Mzg0YjE4OTRhOTA4NzhkM2U5MmJhYiIsICI5Y2NkOTEzOTQzOWU0
MWNlYWMyMGE1ZTMzZTgxNTU1YiJdfX19DQoxgf8wgfwCAQEwXDBXMQswCQYDVQQG
EwJVUzEOMAwGA1UECBMFVW5zZXQxDjAMBgNVBAcTBVVuc2V0MQ4wDAYDVQQKEwVV
bnNldDEYMBYGA1UEAxMPd3d3LmV4YW1wbGUuY29tAgEBMAcGBSsOAwIaMA0GCSqG
SIb3DQEBAQUABIGACOF/D6COhzYHNdpJmE+PCz7TxkgnVBuHjxU2z/pV1e8hwv1I
VO2ZTJ+TRIYZl8LxFCtupaDPeTOYEz1fn6YvjGOqSBkmw1OS3mmGov7HaH0BsYk3
vWnb0lXUNCvsuSzqQmoCzv3CbrsXDnp5bDEbzYXM78AHX03homw/PyAPA5M=
-----END CMS-----

View Signature

We can view the signature or "proof" that the message was stamped with Keystone's approval by decoding our token:

base64 -d mytoken.signed

The decoded token is our user metadata along with some scrambled text which represents the cert signaure:

?"{"access": {"token": {"issued_at": "2013-10-11T20:02:49.623814", "expires": "2013-10-12T20:02:49Z", "id": "placeholder"
,"tenant": {"description": "Default Tenant", "enabled": true, "id": "5dd0ede8bcc44f26ae8983f03249b4de", "name": "demo"}},
"serviceCatalog": [{"endpoints": [{"adminURL": "http://10.0.10.10:8776/v1/5dd0ede8bcc44f26ae8983f03249b4de", "region":
"RegionOne", "internalURL": "http://10.0.10.10:8776/v1/5dd0ede8bcc44f26ae8983f03249b4de", "id": "4be86b9f26564ffc9b40fa1f
68e
b1603", "publicURL": "http://10.0.10.10:8776/v1/5dd0ede8bcc44f26ae8983f03249b4de"}], "endpoints_links": [], "type":
"volume", "name": "volume"}, {"endpoints": [{"adminURL": "http://10.0.10.10:9292", "region": "RegionOne", "internalURL":
"http://10.0.10.10:9292", "id": "5655ca5092b14a63a5a6a1882426bce6", "publicURL": "http://10.0.10.10:9292"}], "endpoints_l
ink
s": [], "type": "image", "name": "glance"}, {"endpoints": [{"adminURL": "http://10.0.10.10:8774/v2/5dd0ede8bcc44f26ae8983
f03
249b4de", "region": "RegionOne", "internalURL": "http://10.0.10.10:8774/v2/5dd0ede8bcc44f26ae8983f03249b4de", "id":
"5721a24df1c3492da5532ed71d13ebe0", "publicURL": "http://10.0.10.10:8774/v2/5dd0ede8bcc44f26ae8983f03249b4de"}],
"endpoints_links": [], "type": "compute", "name": "nova"}, {"endpoints": [{"adminURL": "http://10.0.10.10:9696", "region"
: "RegionOne", "internalUR   L": "http://10.0.10.10:9696", "id": "5ab1bbb07fa644a0b95c1a1cad3061be", "publicURL": "http
://10.0.10.10:9696"}], "endpoints_
links": [], "type": "network", "name": "quantum"}, {"endpoints": [{"adminURL": "http://10.0.10.10:35357/v2.0", "region":
"RegionOne", "internalURL": "http://10.0.10.10:5000/v2.0", "id": "6e89262f99b54900a066d1310c2e4c87", "publicURL": "http
://10
.0.10.10:5000/v2.0"}], "endpoints_links": [], "type": "identity", "name": "keystone"}], "user": {"username": "admin",
"roles_links": [], "id": "a366ed02b00f4cc6ac9af6f6d5990778", "roles": [{"name": "_member_"}, {"name": "admin"}], "name":
"admin"}, "metadata": {"is_admin": 0, "roles": ["9fe2ff9ee4384b1894a90878d3e92bab", "9ccd9139439e41ceac20a5e33e81555b"]}}
}
1??0??0\0W1
0UUS10
Unset10
UUnset10
U
?????65?I?O??xample.com0+0
>??H'T?6??U??!??HT?L??D????+n???y3?=_??/?c?H&?S??i????h}??7?i??U?4+?,?Bj???n?zyl1ͅ??_M?l?? ?

Certs Pushed to Services

The signing certificate and Keystone root CA cert are passed down to the service endpoint along with the current list of revoked certificates.

Example:

/var/lib/glance
.
|-- image-cache
|   |-- incomplete
|   |-- invalid
|   `-- queue
|-- images
|   `-- b32c90b4-f30a-4dd0-8898-ee35d67838fd
`-- keystone-signing
   |-- cacert.pem
   |-- revoked.pem
   `-- signing_cert.pem

Token Verification

The token is verified by a service using a signing cert (public key) and Keystone root CA cert (The Keystone root CA cert is needed because it is not from an established CA and we are not likely to have a copy on our local machine):

openssl cms -verify -in cmsnew -certfile /var/lib/glance/keystone-signing/signing_cert.pem -CAfile /var/lib/glance/keystone-signing/cacert.pem -inform PEM -nosmimecap -nodetach -nocerts -noattr

This works by decoding and then verifiying the signature. The scrambled text is removed from our data and we now have our original user json metadata for OpenStack to consume.

Conclusion

If one does not have their SSL endpoints secure, nothing is being encrypted. It is simply being signed and encoded.

To prove this point, it's a cool exercise to just run base64 -d on any PKI token to show that the data inside is not safe! The PEM format is simply for transportation purposes. The "signing" is to prevent spoofing, i.e. modifying a token and adding the admin role.

Encrypting: Using a public key to write a message and someone else using their private key to read it. i.e. You sign into https://gmail.com: You view their certificate by clicking on the SSL lock encryption icon. You can see the public key they are providing to your browser to encrypt the data to them. They are using a hidden private key to decrypt.

Signing: You use your private key to write message's signature. They use your public key to check if it's really yours.

Check out this resource for clarification on the differences between encryption, encoding, and hashing:

http://danielmiessler.com/study/encoding_encryption_hashing/



Comments

comments powered by Disqus