Thursday, November 08, 2012

Javascript API for OpenID

Too long ago I wrote about an Javascript API for openid: all those NASCARs

To repeat the main points:

Sites currently have no easy way to detect support for openid
The site can detect support for openid like so:

   if (window.openid) { don't show the nascar }
The DOM level API that allows the site to query the preferred identity provider looks like this:
   window.openid.getPreferredOpenidProvider(callback);
In a world of oauth2 and openid connect this could be generalized to:
https://openid.net/specs/openid-connect-standard-1_0.html#rf_prep
    var parameters = {};
    parameters.response_type="id_token";
    parameters.client_id="https://server.example.com/seminar/callback.html";
    parameters.request = "eyJhbGciOiJSUzI1NiIsIng1dSI6Imh0dHBzOlwvXC9nYWJ1bm9taS5uZXRcL3NlbWluYXJcL3JzYV9wdWJsaWNfa2V5LnBlbSJ9.ewoJInJlc3BvbnNlX3R5cGUiOiAiaWRfdG9rZW4iLAoJInNjb3BlIjogIm9wZW5pZCIsCgkiY2xpZW50X2lkIjogImh0dHBzOi8vZ2FidW5vbWkubmV0L3NlbWluYXIvY2FsbGJhY2suaHRtbCIsCgkicG9saWN5X3VybCI6ICJodHRwczovL2dhYnVub21pLm5ldC9zZW1pbmFyL3BvbGljeS5odG1sIiwKCSJ1c2VyaW5mbyI6IHsKCQkiY2xhaW1zIjogewoJCQkibmFtZSI6IG51bGwsCgkJCSJlbWFpbCI6IG51bGwsCgkJCSJwaWN0dXJlIjogbnVsbAoJCX0KCX0sCgkicmVnaXN0cmF0aW9uIjogewoJCSJhcHBsaWNhdGlvbl9uYW1lIjogIlNhbXBsZSBTZW1pbmFyIiwKCQkibG9nb191cmwiOiAiaHR0cHM6Ly9nYWJ1bm9taS5uZXQvc2VtaW5hci9sb2dvLnBuZyIsCgkJIng1MDlfdXJsIjogImh0dHBzOi8vZ2FidW5vbWkubmV0L3NlbWluYXIvcnNhX3B1YmxpY19rZXkucGVtIgoJfQp9Cg.Faytuhwb2W4CWVz2-10umSieh-bqR7QXqU0bNF39u_D0mGoBD4e3X2b4jZNqPvPADSnQhlBGSJu189iFM5bwFzchnO-quCpj7T2CK_-wkrpL5LUn_WHYMmYlFadmb-a1p-TEo7exU9azMS9cT70-kHNqmTaJziZyiAMoJ0Q4TtyTt1Xbkknc_CQRug3ilNv3bEXSlOlva3HUOY7jQIbYMB3jDL3QxS1wbVYNAjOxCxCDmiNAUJA-BkYe6Tpyj-DUs57IM4wQSp64sqim8RqirJJfFb4bCbNTkC3G8sYfN2_1-qEDpOnWW7N3gjl174TWHbnzVLAZGg_rZm58-wHOLw";
    parameters.state="509b9cafd3119";
    parameters.nonce="509b9cafd34fd";

    window.openid.connect(parameters, oc_callback);
The callback
oc_callback
would be called with one parameter.
function oc_callback(resp) {
  // resp contains a signed then encrypted id_token in jw-* format
  // https://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
  // https://tools.ietf.org/html/draft-ietf-jose-json-web-signature
  // state and nonce are inside the resp parameter too
  // need a private key to decrypt it so forward it to my own validation endpoint
  $.post("validate.php", { resp: resp },
   function(id_token) {
     alert("returned id_token: " + id_token);
   }); 
}
The general idea is: put all http request parameters which are defined in openid connect into the request object. Put all the http respones parameters into the response object.

I think we need an Javascript API for identity that is supported by browsers. BrowserID/Persona and AccountChooser do something in this direction but not enough.

Friday, June 01, 2012

ECDH-ES for JSON Web Encryption

The JSON WebToken spec RECOMMENDS that ECDH-ES is implemented. Here we go:

Here are the relevant snippets from the JWA spec:


4.1. "alg" (Algorithm) Header Parameter Values for JWE

alg Parameter Value Key Encryption or Agreement Algorithm
ECDH-ES Elliptic Curve Diffie-Hellman Ephemeral Static, as defined in RFC 6090 , and using the Concat KDF, as defined in Section 5.8.1 of NIST.800-56A, where the Digest Method is SHA-256 and all OtherInfo parameters are the empty bit string


4.6. Key Agreement with Elliptic Curve Diffie-Hellman Ephemeral Static (ECDH-ES)


This section defines the specifics of agreeing upon a JWE CMK with Elliptic Curve Diffie-Hellman Ephemeral Static, as defined in RFC 6090, and using the Concat KDF, as defined in Section 5.8.1 of NIST.800-56A, where the Digest Method is SHA-256 and all OtherInfo parameters are the empty bit string. The alg header parameter value ECDH-ES is used in this case. A key of size 160 bits or larger MUST be used for the Elliptic Curve keys used with this algorithm. The output of the Concat KDF MUST be a key of the same length as that used by the enc algorithm. An epk (ephemeral public key) value MUST only be used for a single key agreement transaction.

Appendix B. Encryption Algorithm Identifier Cross-Reference
Algorithm JWE XML ENC JCA
Elliptic Curve Diffie-Hellman Ephemeral Static ECDH-ES http://www.w3.org/2009/xmlenc11#ECDH-ES TBD


I could not find a Java implementation in JavaSE and the Bouncycastle library does not seem to have one neither. Bouncycastle does implement keyderivation functions but not the one from NIST.800-56A. Valuable input came from this webpage "Key Derivation Functions: How many KDFs are there?". Taking the Bouncycasle implementation and converting it into KDFconcat is easy and here it is: https://code.google.com/p/openinfocard/source/browse/trunk/src/org/xmldap/crypto/KDFConcatGenerator.java

The next thing needed are some keypairs for the JUNIT test cases. I generated them using openssl.
openssl ecparam -out key1.pem -name secp256r1 -genkey
and displayed them using
 openssl ec -in key1.pem -text
read EC key
Private-Key: (256 bit)
priv:
    07:2f:23:22:c0:e7:5e:0c:85:17:64:b4:21:81:99:
    67:78:fd:22:59:2f:87:e5:d4:38:36:09:74:29:a1:
    c3:fc
pub:
    04:ed:3c:83:1b:f3:e1:05:9f:12:07:7f:4b:e4:fd:
    fe:90:55:73:d1:c6:76:45:b4:7d:48:64:ea:17:9d:
    de:99:86:a9:a6:ad:34:27:4a:80:fc:94:b3:a5:ef:
    6c:6e:78:2c:22:7a:39:63:a6:a4:26:50:97:6d:a6:
    ad:e9:90:a1:61
ASN1 OID: prime256v1
writing EC key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIAcvIyLA514MhRdktCGBmWd4/SJZL4fl1Dg2CXQpocP8oAoGCCqGSM49
AwEHoUQDQgAE7TyDG/PhBZ8SB39L5P3+kFVz0cZ2RbR9SGTqF53emYappq00J0qA
/JSzpe9sbngsIno5Y6akJlCXbaat6ZChYQ==
-----END EC PRIVATE KEY-----
Too bad that the man page does not go into detail in what format priv and pub are... Read the source, Luke! It seems that the priv key D is just the bytes in hex of the private key BigInteger. The public key seems to be something else but this is no problem because in ECC the public key is G*D where G is a curve parameter.
So the two private keys are now defined here in the JUNIT tests. One is for the sender of the JWE the other for the recipient.
  static final String ec256_a_priv = "072f2322c0e75e0c851764b42181996778fd22592f87e5d43836097429a1c3fc";
  static final String ec256_b_priv = "1a3eda89dc067871530601f934c6428574f837507c578e45bd10a29b2e019bfb";
Now the public keys are computed like this:
  ASN1ObjectIdentifier oid = ECUtil.getNamedCurveOid("secp256r1");
  X9ECParameters x9ECParameters = ECUtil.getNamedCurveByOid(oid);
  byte[] ec256_a_priv_bytes = Hex.decode(ec256_a_priv);
  ec256_a_D = new BigInteger(1, ec256_a_priv_bytes);
  ECPoint pub = x9ECParameters.getG().multiply(ec256_a_D);
  ec256_a_X = pub.getX().toBigInteger();
  ec256_a_Y = pub.getY().toBigInteger();
This gives the following jwtHeader for the first private key: ECDH-ES jwtHeaderSegment:
{"alg":"ECDH-ES",
"enc":"A256GCM",
"iv":"__79_Pv6-fg",
"crv":"secp256r1",
"x":"AO08gxvz4QWfEgd_S-T9_pBVc9HGdkW0fUhk6hed3pmG",
"y":"AKmmrTQnSoD8lLOl72xueCwiejljpqQmUJdtpq3pkKFh"}
The spec says that the senders key pair is ephemeral and the recipient's key pair is static. So code using this code should generate one ephemeral EC key pair. Stuff the private key and the recipients public key into the encrypt function. These key parts are used for the key agreement. See the wikipedia page for a short explanation. I will implement the "epk" header parameter at a later time. For now I use the X and Y format to transfer the ephemeral public key to the recipient.
Base64 encoding the header yields:
eyJhbGciOiJFQ0RILUVTIiwNCiJlbmMiOiJBMjU2R0NNIiwNCiJpdiI6Il9fNzlfUHY2LWZnIiwNCiJjcnYiOiJzZWNwMjU2cjEiLA0KIngiOiJBTzA4Z3h2ejRRV2ZFZ2RfUy1UOV9wQlZjOUhHZGtXMGZVaGs2aGVkM3BtRyIsDQoieSI6IkFLbW1yVFFuU29EOGxMT2w3Mnh1ZUN3aWVqbGpwcVFtVUpkdHBxM3BrS0ZoIn0


The next step in my interpretation of the spec is to generate the content encryption key using the key derivation function defined in the NIST paper. The content encryption is done using the method specified in the enc parameter of the header here: A256GCM
For this method I need a 256bit == 32byte key and a 12 byte IV. So the call to the KDF is:
  ECDHBasicAgreement ecdhBasicAgreement = new ECDHBasicAgreement();
  ecdhBasicAgreement.init(ecPrivateKeyParameters);
  BigInteger z = ecdhBasicAgreement.calculateAgreement(ecPublicKeyParameters);
  byte[] zBytes = BigIntegers.asUnsignedByteArray(z);
  KDFConcatGenerator kdfConcatGenerator = new KDFConcatGenerator(kdfDigest);
  kdfConcatGenerator.init(new KDFParameters(zBytes, null));
  byte[] out = new byte[12 + (keylength / 8)];
  kdfConcatGenerator.generateBytes(out, 0, out.length);
  byte[] secretKeyBytes = new byte[keylength / 8];
  byte[] ivBytes = new byte[12];
  System.arraycopy(out, 0, ivBytes, 0, 12);
  System.arraycopy(out, 12, secretKeyBytes, 0, secretKeyBytes.length);

Now a random 256bit AES key is generated and encrypted using the secret key and IV generated by the KDF.
For this content encryption key (base64url)
ECDH-ES contentEncryptionKey=Y8-fcu11np1l3qlgpGq0XF58Cv2n4DOJ8lkdl2gRXgI
the values of the key segment and the jwe crypto segment are this:
ECDH-ES jwtSymmetricKeySegment base64: _vZ-N5fs3_uJ9o-woDOzKZBQopZRi4EfZVNB4UWqdCjappfoOFCZPTUBDruWAtWY
ECDH-ES jwtCryptoSegment base64: psSi2xa7oY1pmK1m9GGXREr9YB6QUdOtK_Jl_nnKYpmKGHL577tUdadK8H_yacb78bBlyTnrTx51pmxyo6UJMM9c_P-lOfMiMslvS-3t1vD5HiOq0Rg
Please use the IETF JOSE mailing list for comments and suggestions.

Special thanks to Mike Jones for writing most of the JW* specs text and to John Bradley for being a fountain of knowledge in all things crypto and identity management protocols and formats. Not to forget Nat Sakimura for starting the OpenID Artifact Binding WG which now does all the OpenID Connect work. Especially for mobile devices we need simple, light weight protocols and formats. JW* and especially ECDH-ES are important for mobile.

Thursday, May 24, 2012

Playing with Google's Identity Toolkit on openinfocard.org

Today I retried Google's Identity Toolkit.
https://developers.google.com/identity-toolkit/v1/acguide
So I had to undust my rudimentary PHP knowledge and write some scripts and minimal html pages.


Clicking the key hole icon opens the account chooser.

I choose GMail and login to Google.


This is the result page. My site now knows some attributes about me like verifiedEmail, display name and imageUrl etc.

Next task: Repeat and rinse with http://accountchooser.net/

Wednesday, May 02, 2012

Debugging OAuth2 SSL Connections

Debugging SSL protected protocols like oauth2 can be a problem but it is not entirely impossible nor hard to do.

One way to do it is to spoof the certificates the protocol relies on to protect the communication. The certificates are used by the client to verify that the server is the endpoint it is supposed to be talking to and to encrypt the communication. A good description for the Android operating system is given in this blog post (Intercepting and decrypting SSL communications between Android phone and 3rd party server). Nobody can blame Android for being picked here as an example and ways to do this exist for all operating systems. Yes, to install the certs you need root access; but it well may be that you have that and want to help a friend to debug their installed application on your phone. Even if the client is running on a server it may be worthwhile to debug the network traffic to find certain errors in the client implementation. An error specific to an oauth implementation might be that your friend has a typo in the cliend_id or client_secret and the authorization server is rejecting requests because of that.
It might be hard for you to verify client_id and client_secret by analyzing the client. Maybe they are stored on a UICC or stored encrypted in the file system (and the keystore password is not "changeit") and are only decrypted and used when a resource owner uses the client.
By analyzing the SSL traffic you can help to find this kind of bug and all other related to protocol issues.

But maybe you don't have an SSL server to capture the plain text from an SSL connection?! Then another path you might take is to swap the client's SSL implementation with your own. You don't have to change the client's code or analyze the client's memory. Building your own version of most operating systems with your own SSL implementation is not that hard to do. Or maybe you can just register your SSL implementation to be used with all client code? Or you can swap a library?
There are more ways to achieve your goal.

But make sure that you have your friend's permission first. Not everybody might be happy with the fact that you now know the client_id and client_secret.

Have fun!


Saturday, April 28, 2012

Identity Management @ RSA 2012 Europe

Sharpen your keyboard and submit a paper for the Identity Management track at RSA Conference Europe 2012. The leading conference on security and all things you need to know.


From the topic description: Identity Management
Identity Management covers issues of access control, authentication, identification technologies & protocols. Sessions on Identity and Access Management (IAM) fit here, along with sessions on IAM standards and architecture. This topic also covers issues such as credential management, multifactor authentication and new methods of authentication.
The Call for Speakers closes on Friday 18th May

Thursday, April 26, 2012

Myhabit.com unconfused

Here the unconfusing part with was censored in one other post about the same UI.





Note the Amazon favicon and URL.

Sunday, January 22, 2012

OpenID Connect Test Servers


Here are some experimental OpenID Connect server configurations:

 
https://connect-op.heroku.com/.well-known/openid-configuration
 
{
  "version":"3.0",
  "issuer":"https://connect-op.heroku.com",
  "authorization_endpoint":"https://connect-op.heroku.com/authorizations/new",
  "token_endpoint":"https://connect-op.heroku.com/access_tokens",
  "userinfo_endpoint":"https://connect-op.heroku.com/user_info",
  "check_id_endpoint":"https://connect-op.heroku.com/id_token",
  "registration_endpoint":"https://connect-op.heroku.com/connect/client",
  "scopes_supported":[
    "openid",
    "profile",
    "email",
    "address",
    "PPID"
  ],
  "response_types_supported":[
    "code",
    "token",
    "id_token",
    "code token",
    "code id_token",
    "id_token token"
  ],
  "user_id_types_supported":[
    "public",
    "pairwise"
  ],
  "x509_url":"https://connect-op.heroku.com/cert.pem"
}
https://openidconnect.info/.well-known/openid-configuration
 
{
  "version":"3.0",
  "issuer":"https://openidconnect.info/",
  "authorization_endpoint":"https://openidconnect.info/connect/authorize",
  "token_endpoint":"https://openidconnect.info/connect/token",
  "user_info_endpoint":"https://openidconnect.info/connect/userinfo",
  "check_id_endpoint":"https://openidconnect.info/connect/check_session",
  "registration_endpoint":"https://openidconnect.info/connect/register",
  "scopes_supported":[
    "openid",
    "profile",
    "email",
    "address",
    "PPID"
  ],
  "flows_supported":[
    "code",
    "token",
    "code id_token",
    "token id_token"
  ],
  "identifiers_supported":[
    "public",
    "ppid"
  ]
}
https://connect.openid4.us/.well-known/openid-configuration

{
  "version":"3.0",
  "issuer":"https:\/\/connect.openid4.us",
  "authorization_endpoint":"https:\/\/connect.openid4.us\/abop\/op.php\/auth",
  "token_endpoint":"https:\/\/connect.openid4.us\/abop\/op.php\/token",
  "userinfo_endpoint":"https:\/\/connect.openid4.us\/abop\/op.php\/userinfo",
  "check_id_endpoint":"https:\/\/connect.openid4.us\/abop\/op.php\/check_id",
  "refresh_session_endpoint":"https:\/\/connect.openid4.us\/abop\/op.php\/refreshsession",
  "end_session_endpoint":"https:\/\/connect.openid4.us\/abop\/op.php\/endsession",
  "jwk_url":"https:\/\/connect.openid4.us\/connect4us.jwk",
  "jwk_encryption_url":"https:\/\/connect.openid4.us\/connect4us.jwk",
  "x509_url":"https:\/\/connect.openid4.us\/connect4us.pem",
  "x509_encryption_url":"https:\/\/connect.openid4.us\/connect4us.pem",
  "registration_endpoint":"https:\/\/connect.openid4.us\/abop\/op.php\/registration",
  "scopes_supported":[
    "openid",
    "profile",
    "email",
    "address"
  ],
  "response_types_supported":[
    "code",
    "token",
    "id_token"
  ],
  "acrs_supported":[
    "http:\/\/www.idmanagement.gov\/schema\/2009\/05\/icam\/openid-trust-level1.pdf"
  ],
  "user_id_types_supported":[
    "public",
    "pairwise"
  ],
  "userinfo_algs_supported":[
    "HS256",
    "RS256",
    "A128CBC",
    "A256CBC",
    "A128KW",
    "RSA1_5"
  ],
  "id_token_algs_supported":[
    "HS256",
    "RS256",
    "A128CBC",
    "A256CBC",
    "A128KW",
    "RSA1_5"
  ],
  "request_object_algs_supported":[
    "HS256",
    "RS256",
    "A128CBC",
    "A256CBC",
    "A128KW",
    "RSA1_5"
  ],
  "token_endpoint_auth_types_supported":[
    "client_secret_post",
    "client_secret_basic",
    "client_secret_jwt",
    "private_key_jwt"
  ],
  "token_endpoint_auth_algs_supported":[
    "HS256",
    "RS256"
  ]
}
 Happy testing!