Thursday, March 31, 2011

All those NASCARs

We did not really make a breakthrough in the last years on the questions of
- Identity Provider Discovery
- security and privacy UI
- Identity in the Browser
- intelligent agents or what ever you call them
- openid UI
- add your favorite here...

Although we did not have a lack of efforts to solve some of these issues
- cardspace
- openinfocard
- azigo's selector
- Kantara login ULX
- openidsamplestore.com
- Janrain's Engage
- ...

We really need browser support. So lets start - again - with: Identity in the Browser.

Requirements:
- user centric
- ask for user consent before leaking information.
- help the users discover the reusable identities they already have.
- don't favor any identity provider.
- not to many user choices. Keep it simple.
- allow the site to detect whether or not identity in the browser is supported or not.

I created a Firefox addon that tries to achieve just that.
http://ignisvulpis.blogspot.com/2011/03/openid-for-firefox4.html
Or at least go in that direction. I concentrated on openid support but I think it is easy to generalize from there.

The DOM level API that allows the site to query the preferred identity provider looks like this:

window.openid.getPreferredOpenidProvider(callback);

The site can detect support by testing for the new child of the window object to be present:
if (window.openid) { don't show the nascar }


Maybe I should not have named this "window.openid" but "window.identity"?!
I guess that is for the W3C to decide. They just added another event to Identity-May:
"W3C Workshop on Identity in the Browser"

I really hope that we get W3C support for Identity. It is not important whether this is called window.openid or navigator.openid or whatever. We have a nice example for another W3C API: Geolocation and I modelled my Identity API suggestion along those lines.

What next?
I) The UI of my addon is not that polished.
addon asking for permission to store openid

a) In this case the file-url is especially ugly and in this case there are not that many alternatives.
In the website case the addon could
- show the site's URL
- show the site's favicon instead of URL
- show the site's icon from the extended validation certificate
- show the site's "other icon" which I don't know how to get in a standardized way
- show the site's name / title from the webpage
- show the site's name from the certificate

b) Should I show which openid the addon is going to provide to the site?
Actually the user does not really care whether this is an openid or whatever.
Here the addon could
- show the user's openid.claimed_id
- show the user's openid.identity
- show the OpenID Provider's (OP) favicon from the openid.op_endpoint
- show the user's icon/image provided by the OP
- let the user add an icon to that openid

II) Should the addon use the Firefox notification-box or the newer notification popups?
The notification box might be to easy to fake by a website but then there is no real point in faking it. Or is it?

III) Learning new OpenIDs notification popup

Here the addon could
- show the user's openid.claimed_id (as seen in the picture above)
- show the user's openid.identity
- show the OpenID Provider's (OP) favicon from the openid.op_endpoint
- show the user's icon/image provided by the OP
- let the user add an icon to that openid

IV) Does the user already have reusable Identities?
- The addon could just open a tab that shows the OpenID Foundation's "get an openid" page.
- I implemented a feature where the browser helps the users find their reusable identities. The browser knows a lot about the sites the user visited and might have stored the user's credentials for some sites. My implementation iterates through all domains with stored credentials and requests the Yadis XRD. If the XRD contains openid information then the domain is shown as an potential "openid you might already have".
This feature is not in the version I have uploaded to Mozilla.
- The addon could use Mozilla's Firefox Sync openid provider. Which would violate the rule not to prefer some identity providers...

V) Mobile support
Firefox mobile is out. The addon currently does not support Firefox mobile. Which brings me to the next point.

VI) The addon could add identities (openids) to form input fields from a context menu. Right click the page or input element and a choice is presented to the user to input the openid into that input field. But on the other hand this should be done better by the site's javascript code after it has detected support through the DOM API.

VII) Support identities issued by mobile operators.
Should be easy... Support mobile wallets.

VII) The openid icon in the url-bar might be too much for other providers. I don't care for now.

Please support Identity in the Browser!

Saturday, March 26, 2011

OpenID for Firefox4

I created an addon for Firefox4 that learns your OpenIDs when you use them.

The addon then asks you whether it may store the discovered openid (claimed_id) shown here at the identity commons site:



Another thing the addon does is that it allows the site to query the DOM for your preferred openid:


This is the source code of the last page:
<html><head><title>JavaScript-Test</title>
<script type="application/javascript">
 function start() {
   try {
    window.openid.getPreferredOpenidProvider(function(preferredOpenidProvider) {
  var p = document.getElementById("id");
  p.textContent = preferredOpenidProvider;
});

   } catch(e) {alert("exception="+e);}
  }
</script>
</head><body>
<form><input type=button value="Start" onClick="start()"></form>
<p id="id">openid</p>
</body></html>



The addon then asks the user for her consent to provide the openid to the site:


Clicking the openid urlbar icon inserts the openid to an appropriate input field on the page. If the addon did not learn an OpenID in the past it opens the OpenID Foundation's "Get An OpenID" page"

Google's openidsamplestore does NOT put an id or name on the input fields making it impossible for the addon to determine the correct input field. Shame on you Google! You can drag the OpenID urlbar icon to the correct field to insert your OpenID into the field.


 



The addon works on Stackoverflow too:


Get Firefox4 now and please try out this new addon!

Thursday, March 03, 2011

AES + Password Based Encryption for JSON Web Tokens

I just committed some new code to the xmldap code repository. WebToken.java signs and encrypts JSON Web Tokens and WebTokenTest.java contains the JUNIT tests. These tests also show how WebToken.java is used.

Today I added Password Based Encryption (PBE) and AES encryption.

PBE uses PBEWithMD5AndDES with DESede.
AES is used in CBC mode.

PBE and RSA encryption yield in a three segment token:
jwtHeaderSegment.jwtKeySegment.jwtCryptoSegment
where
- the header segment describes the algorithm and key used,
- the key segment contains the encrypted key that is actually used to encrypt the payload
- the crypto segment contains the encrypted content.
As always each segment is base64 url encoded.

AES encryption yields in a two segment token:
jwtHeaderSegment.jwtCryptoSegment
The jwtKeySegment is not needed because AES uses a shared secret to encrypt the payload. It makes no sense to put this secret key into the token.

PBE and RSA encryption generate the encryption key and therefore this key is encrypted and send as the jwtKeySegment. JSON WebToken encryption with RSA was explained in yesterdays blog post.

Here are some example tokens (without lengthy explanation):
PBE jwtHeaderSegment: {"alg":"EPBE",
"kid":"iauxBG<9"}
PBE password: password
PBE jwtHeaderSegment base64: eyJhbGciOiJFUEJFIiwNCiAia2lkIjoiaWF1eEJHPDkifQ
PBE jwtKeySegment: {"slt":"PS023Hz4xuI","wrp":"o50kyveiYHrqg6sIPldlU4Fbi4QEnGY99FhpU_G1-zk"}
PBE jwtKeySegment base64: eyJzbHQiOiJQUzAyM0h6NHh1SSIsIndycCI6Im81MGt5dmVpWUhycWc2c0lQbGRsVTRGYmk0UUVuR1k5OUZocFVfRzEtemsifQ
PBE jwtCryptoSegment base64: CZCiieIHmirOHW17xXECoPmvIaT1de8DF5Czw0Uv1ktJ7uDAEaPj7fHM3__vnqtNLD86u2HeR7yV-UnhHn-3wF0tppv1_EJ7

fixed AES192 keybytes
[126, -34, -48, -34, 61, 72, -63, -36, 14, 53, -27, -7, -35, -57, 59, -89, 51, 84, 115, -119, -1, -125, -115, 108]
AES192 jwtCryptoSegment base64: K2xsdGRCb0tzcVdEMk1NNWdmeFlLYzdkY0V3Ry95cU5PclZZYkE0V25XMFZocW5sMVhjeDFzQWhIN2kvMVZGYms2emdHNFVrQXVSNmJjVzNaWmNBbUxtZ08xcEFybnpwYkdSWldJRlpleTRxMGI2KzVQV1hiV2JIUGh2d1kxeEM

The payload is the same as in Mike Jones' draft:
{"iss":"joe",
"exp":1300819380,
"http://example.com/is_root":true}

Enjoy.

Wednesday, March 02, 2011

RSA Encrypting JSON



After I implemented the current draft to sign JSON: "JSON Web Token (JWT) - Claims and Signing" I implemented some simple JSON encryption.

This works by generating a ephemeral symmetric key with a specified keylength (128, 192, 256 bits) that is encrypted using the recipient's public RSA key. The ephemeral symmetric key is used to encrypt the payload using AES in CBC-mode with PKCS7 padding.
Depending on the key length the algorithms are called RE128, RE192 and RE256.

The following is an example of a JSON object that can be encoded to produce a JWT Claims Object:

{"iss":"joe",
"exp":1300819380,
"http://example.com/is_root":true}


The following example JSON header object declares that the encoded object is a JSON Web Token (JWT) and that the JWT Payload Segment is encrypted using the RE256 algorithm and that the RSA public key has the thumbprint of b9E8JDWjYefFiM0X9V9a098Bd6ZsFyemogCEX016uIw:

{"typ":"JWT",
"alg":"RE256",
"x5t":"b9E8JDWjYefFiM0X9V9a098Bd6ZsFyemogCEX016uIw"}


Base64url encoding the JSON header yields the following JWT header segment:
eyJhbGciOiJSRTI1NiIsDQogIng1dCI6ImI5RThKRFdqWWVmRmlNMFg5VjlhMDk4QmQ2WnNGeWVtb2dDRVgwMTZ1SXcifQ

The following byte array contains the UTF-8 characters for an example ephemeral key:
[27, 24, 24, 78, 51, -38, -111, -13, -53, -4, -13, -84, 34, -59, 96, 20, -23, 87, -26, -56, -116, -35, 127, -21, -97, -26, -71, 74, -36, -67, -124, -45]

The RSA key consists of a public part (n, e), and a private exponent d. The values of the RSA key used in this example, presented as the byte arrays representing big endian integers are:
Parameter NameValue
n[161, 248, 22, 10, 226, 227, 201, 180, 101, 206, 141, 45, 101, 98, 99, 54, 43, 146, 125, 190, 41, 225, 240, 36, 119, 252, 22, 37, 204, 144, 161, 54, 227, 139, 217, 52, 151, 197, 182, 234, 99, 221, 119, 17, 230, 124, 116, 41, 249, 86, 176, 251, 138, 143, 8, 154, 220, 75, 105, 137, 60, 193, 51, 63, 83, 237, 208, 25, 184, 119, 132, 37, 47, 236, 145, 79, 228, 133, 119, 105, 89, 75, 234, 66, 128, 211, 44, 15, 85, 191, 98, 148, 79, 19, 3, 150, 188, 110, 155, 223, 110, 189, 210, 189, 163, 103, 142, 236, 160, 198, 104, 247, 1, 179, 141, 191, 251, 56, 200, 52, 44, 226, 254, 109, 39, 250, 222, 74, 90, 72, 116, 151, 157, 212, 185, 207, 154, 222, 196, 199, 91, 5, 133, 44, 44, 15, 94, 248, 165, 193, 117, 3, 146, 249, 68, 232, 237, 100, 193, 16, 198, 182, 71, 96, 154, 164, 120, 58, 235, 156, 108, 154, 215, 85, 49, 48, 80, 99, 139, 131, 102, 92, 111, 111, 122, 130, 163, 150, 112, 42, 31, 100, 27, 130, 211, 235, 242, 57, 34, 25, 73, 31, 182, 134, 135, 44, 87, 22, 245, 10, 248, 53, 141, 154, 139, 157, 23, 195, 64, 114, 143, 127, 135, 216, 154, 24, 216, 252, 171, 103, 173, 132, 89, 12, 46, 207, 117, 147, 57, 54, 60, 7, 3, 77, 111, 96, 111, 158, 33, 224, 84, 86, 202, 229, 233, 161]
e[1, 0, 1]
d[18, 174, 113, 164, 105, 205, 10, 43, 195, 126, 82, 108, 69, 0, 87, 31, 29, 97, 117, 29, 100, 233, 73, 112, 123, 98, 89, 15, 157, 11, 165, 124, 150, 60, 64, 30, 63, 207, 47, 44, 211, 189, 236, 136, 229, 3, 191, 198, 67, 155, 11, 40, 200, 47, 125, 55, 151, 103, 31, 82, 19, 238, 216, 193, 90, 37, 216, 213, 206, 160, 2, 94, 227, 171, 46, 139, 127, 121, 33, 111, 198, 59, 234, 86, 39, 83, 180, 6, 68, 198, 161, 81, 39, 217, 178, 149, 69, 64, 160, 187, 225, 163, 5, 86, 152, 45, 78, 159, 222, 95, 100, 37, 241, 77, 75, 113, 52, 65, 181, 93, 199, 59, 155, 74, 237, 204, 146, 172, 227, 146, 126, 55, 245, 125, 12, 253, 94, 117, 129, 250, 81, 44, 143, 73, 97, 169, 235, 11, 128, 248, 168, 7, 70, 114, 138, 85, 255, 70, 71, 31, 52, 37, 6, 59, 157, 83, 100, 47, 94, 222, 30, 132, 214, 19, 8, 26, 250, 92, 34, 208, 81, 40, 91, 214, 59, 148, 59, 86, 93, 137, 138, 5, 104, 84, 19, 229, 60, 60, 108, 101, 37, 255, 31, 227, 78, 61, 220, 112, 240, 213, 100, 80, 253, 164, 139, 161, 46, 16, 78, 157, 235, 159, 184, 24, 129, 225, 196, 189, 242, 93, 146, 71, 244, 80, 200, 101, 146, 121, 104, 231, 115, 52, 244, 65, 79, 117, 167, 80, 225, 57, 84, 110, 58, 138, 115, 157]


The RSA public (n,e) key and the ephemeral symmetric key are then passed to the RSA OAEP encryption function.
The following byte array contains the UTF-8 characters for the encrypted ephemeral key:
[-106, 115, -121, -62, -123, 54, -119, 65, -90, 8, 65, 115, 53, 22, 74, -88, 27, 29, -120, -76, 122, -113, 69, -63, 90, -22, -29, 78, 1, 66, -59, 62]

Base64url encoding this byte array produces this value for the JWT Key Segment:
lnOHwoU2iUGmCEFzNRZKqBsdiLR6j0XBWurjTgFCxT7eSfGNpni01a3TzuaeZjVc_f3jEiuvJFYFanizkpyk9BGqCNs5LhX2m1h2Qc_llKt3TgGRi67e9p36vX81G8-QccnNQ321vutKYe2jlEvcg0hhWhejhbtK2XjsKkMaJDzEDuULbJmnAFgchSdbcYgz0JK6onX_1tO2FWed0r-EK0v9v7Y65pwz_nrYf2u8f5-j5aX2RUEYVx0sq2oaJZbbp26QmUGVPdnnEgOVI6vpL5-M6Gl1q9j645Ag94Sx9HpQcg8KEUVLfK3BfbLYGnIf-kFP8fROHuIHAMdiPD4ong

Using the symmetric key to AES256 encrypt the payload bytes and base64url-encoding the resulting bytes yields the JWT Crypto Segment:
L2ZFNFVQcCtjdWw1QTVZSGw0bUhGRDZ6NDlkNFFtRWQ1a0VBSGUzNzN3V0txY29MZmRHWkhrRUtYMUJNRWl4dzQ0RHlZcmN6TWg4WWEvN04wdUYrc01UeWlYUXBYdmV6a2JvWWd2aFQzeS9OZkpoZ2doSTN6bmViTnVwZHNZZFI

Combining these segments in the order Header.Key.Crypt with period characters between the segments yields this complete JWT using the JWT Compact Serialization (with line breaks for display purposes only):

eyJhbGciOiJSRTI1NiIsDQogIng1dCI6ImI5RThKRFdqWWVmRmlNMFg5VjlhMDk4QmQ2WnNGeWVtb2dDRVgwMTZ1SXcifQ
.
lnOHwoU2iUGmCEFzNRZKqBsdiLR6j0XBWurjTgFCxT7eSfGNpni01a3TzuaeZjVc_f3jEiuvJFYFanizkpyk9BGqCNs5LhX2m1h2Qc_llKt3TgGRi67e9p36vX81G8-QccnNQ321vutKYe2jlEvcg0hhWhejhbtK2XjsKkMaJDzEDuULbJmnAFgchSdbcYgz0JK6onX_1tO2FWed0r-EK0v9v7Y65pwz_nrYf2u8f5-j5aX2RUEYVx0sq2oaJZbbp26QmUGVPdnnEgOVI6vpL5-M6Gl1q9j645Ag94Sx9HpQcg8KEUVLfK3BfbLYGnIf-kFP8fROHuIHAMdiPD4ong
.
L2ZFNFVQcCtjdWw1QTVZSGw0bUhGRDZ6NDlkNFFtRWQ1a0VBSGUzNzN3V0txY29MZmRHWkhrRUtYMUJNRWl4dzQ0RHlZcmN6TWg4WWEvN04wdUYrc01UeWlYUXBYdmV6a2JvWWd2aFQzeS9OZkpoZ2doSTN6bmViTnVwZHNZZFI

Decoding the JWT from this example requires processing the JWT Header Segment, finding the private key to decrypt the symmetric key and using that symmetric key to decrypt the encrypted payload.