From: Matthew Vernon
+ * This namespace privides following crytpgrahic classes.
+ *
")
+ Response.Write("Logout Raven (remote)
" & _
+ "Access Raven authenticated page")
+ Exit Sub
+ End If
+
+ ' When you first access this page
+ ' the 'Authenticate' function will be called.
+ ' This will typically be called three times
+ ' in total to successfully authenticate the
+ ' user. In the first two iterations of
+ ' 'Authenticate', it will return
+ ' 'AUTHENTICATE_INCOMPLETE' while it
+ ' redirects the user's browser first to
+ ' the Raven WLS and then back to this page.
+ ' On the third iteration of 'Authenticate', it
+ ' will return 'AUTHENTICATE_COMPLETE_AUTHENTICATED'
+ ' or 'AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED'
+ ' if the authentication process has fully
+ ' completed without error.
+
+ Select Case oUcam_Webauth.Authenticate()
+
+ Case oUcam_Webauth.AUTHENTICATE_INCOMPLETE
+
+ ' 'Authenticate' still redirecting pages
+ ' so don't do anything else.
+
+ Exit Sub
+
+ Case oUcam_Webauth.AUTHENTICATE_COMPLETE_AUTHENTICATED
+
+ ' Success so display the 'principal', ie. the user id.
+
+ Response.Write("SUCCESS. You are " & oUcam_Webauth.principal() & "
")
+
+ ' Also display the 'ptags' parameter indicating
+ ' whether the user is 'current' or not.
+
+ Response.Write("Ptags = " & oUcam_Webauth.ptags() & "
")
+
+ ' Display any 'GET variables' to check they
+ ' have carried through from the original
+ ' page request.
+
+ For Each item In Request.QueryString()
+ Response.Write item & "=" & Request.QueryString()(item) & "
"
+ Next
+
+ ' Display a 'Logout' link to make it easy to
+ ' test authentication repeatedly.
+
+ Response.Write("Logout Raven (local)")
+
+ Case Else
+
+ ' Either there was an error or a failed
+ ' authentication so print out the result either way.
+
+ Response.Write("FAIL - " & oUcam_Webauth.status() & ": " & oUcam_Webauth.msg())
+
+ ' Also log the error for debugging purposes.
+
+ oUcam_Webauth.write_log("FAIL - " & oUcam_Webauth.status() & ": " & oUcam_Webauth.msg())
+
+ End Select
+
+End Sub
+
+Call Main
+
+%>
+
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..20c2c4f
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,24 @@
+' This VBScript class implements a Raven v3 agent for the University of Cambridge
+' Web Authentication System.
+'
+' See http://raven.cam.ac.uk/project/ for more details
+'
+' Loosely based on the PHP module for Raven
+' https://wiki.cam.ac.uk/raven/PHP_library
+'
+' Copyright (c) 2004, 2005, 2008, 2014 University of Cambridge
+'
+' This module is free software; you can redistribute it and/or modify
+' it under the terms of the GNU Lesser General Public License as
+' published by the Free Software Foundation; either version 2.1 of the
+' License, or (at your option) any later version.
+'
+' The module is distributed in the hope that it will be useful, but
+' WITHOUT ANY WARRANTY; without even the implied warranty of
+' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+' Lesser General Public License for more details.
+'
+' You should have received a copy of the GNU Lesser General Public
+' License along with this toolkit; if not, write to the Free Software
+' Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+' USA
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f4b1ba3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,395 @@
+University of Cambridge Classic ASP/VBScript Raven Authentication Module - v.1.0 (29/04/2014)
+==========================================================================
+
+Description
+-----------
+The software comprises a VBScript class 'Ucam_Webauth' and sample files that
+provide a Classic ASP/VBScript implementation of a 'Raven' authentication
+module; Raven is the web authentication protocol used by the University of
+Cambridge, UK. The logic and code of the 'Ucam_Webauth' class are loosely
+based on the PHP Raven authentication class provided at
+http://raven.cam.ac.uk/project/.
+- For a full description of the latest Raven specification and an explanation
+of how Raven works, go to http://raven.cam.ac.uk/project/.
+- This software was originally created for the Careers Service, University
+of Cambridge by sh801@cam.ac.uk
+
+Files and folders
+-----------------
+- [certificates]: Temporary location for Raven public key certificates.
+- [js]: Folder containing Javascript cryptography libraries.
+- [docs]: Supporting documentation.
+- [logs]: Possible location for log files created by the module.
+- Default.asp: A sample file showing how the 'Ucam_Webauth' class is used
+to provide Raven authentication.
+- Test.asp: A test file for unit testing the 'Ucam_Webauth' module using the
+'Ucam_RavenWLS' dummy Raven server (separate project, not included).
+- Ucam_Webauth.vbs: The main 'Ucam_Webauth' VBScript class.
+
+Platform requirements
+---------------------
+This module has been tested on IIS7 with .NET Framework set to
+'No managed code', ie. classic ASP.
+
+Installation
+------------
+### Cryptographic functions
+Cryptographic functions are provided by the Javascript libraries within the
+'js' folder. These libraries are versions of the client-side Javascript
+libraries provided at http://kjur.github.io/jsrsasign/index.html
+but modified to run server-side. There is no need to install any
+additional cryptography libraries.
+
+### Install Raven certificates
+The authentication module uses the Raven public key certificate at
+https://raven.cam.ac.uk/project/keys/ to verify authentication responses.
+Download the certificate from https://raven.cam.ac.uk/project/keys/ and copy
+to the 'certificates' folder provided with this authentication module
+download - the 'certificates' folder is a temporary location for the
+certificate while you get the module up and running. You will need to supply
+the full path to the 'certificates' folder as either a 'key_dir' argument
+to the 'Ucam_Webauth' constructor or by modifying the 'Ucam_Webauth'
+variable 'DEFAULT_KEY_DIR' directly.
+
+Once you have everything working, move the 'certificates' folder
+to a new location on your webserver that is not web- or publicly-accessible
+and modify the 'key_dir' string accordingly.
+
+- NOTE: you may have to change the name of the key file from 'pubkey2.crt'
+to '2.crt'.
+
+If you're using the Raven test server
+(http://raven.cam.ac.uk/project/test-demo/) for testing purposes, make sure
+you install the test server keys instead, but ensure you remove these keys
+before using the authentication module in a production environment, as
+recommended by the demo Raven server:
+>> It is vital to keep these demo keys seperate from keys
+>> used with a production service - failure to do so could
+>> allow an attacker to successfully replay a response
+>> from the demonstration server, which anyone can easily
+>> obtain, against a production service.
+
+
+Getting started
+---------------
+
+The 'Ucam_Webauth' VBScript class must be used within an ASP server-side page
+as it interacts directly with a user's browser session. To use the
+'Ucam_Webauth' VBScript class:
+
+- 1. Ensure the 'Ucam_Webauth.vbs' class file and the folder 'js' are in the
+same directory as your ASP script. The folders 'certificates' and 'logs' may
+also be located here temporarily.
+- 2. Include the 'Ucam_Webauth.vbs' class file in the 'head' of your ASP file:
+
+```
+
+
+
+
+
+```
+
+- 3. Set up the initial arguments for the 'Ucam_Webauth' class:
+
+```
+Set args = CreateObject("Scripting.Dictionary")
+args.Add "hostname", "localhost"
+args.Add "auth_service", "https://demo.raven.cam.ac.uk/auth/authenticate.html"
+args.Add "key_dir", "C:/Ucam_Webauth/certificates"
+```
+
+'args' is an associative array of *text* strings so parameter values must
+be converted into strings, ie. numbers and booleans must be supplied within
+quotes as in "23", "TRUE", "FALSE".
+A full list of allowed parameters is provided at the end of this README.
+
+- 4. Create an instance of the 'Ucam_Webauth' class from within your ASP
+server page and initialize with setup variables:
+
+```
+Set oUcam_Webauth = New Ucam_Webauth
+Call oUcam_Webauth(args)
+```
+
+- 5. Call 'Authenticate()' on the Ucam_Webauth object and act according to
+the value returned:
+
+```
+ Select Case oUcam_Webauth.Authenticate()
+
+ Case oUcam_Webauth.AUTHENTICATE_INCOMPLETE
+
+ ...
+ Exit Sub
+
+ Case oUcam_Webauth.AUTHENTICATE_COMPLETE_AUTHENTICATED
+
+ ...
+
+ Case oUcam_Webauth.AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED
+
+ ...
+
+ Case oUcam_Webauth.AUTHENTICATE_COMPLETE_ERROR
+
+ ...
+
+ End Select
+```
+
+The four possible return values of 'Authenticate()' are:
+
+- AUTHENTICATE_INCOMPLETE : The authentication process has yet to complete.
+The user may have been redirected to the Raven server and has yet to enter
+their login details.
+- AUTHENTICATE_COMPLETE_AUTHENTICATED : The authentication process completed
+and the user has been successfully authenticated.
+- AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED : The authentication process
+completed and the user was not successfully authenticated.
+The user may have clicked 'Cancel' at the Raven server.
+- AUTHENTICATE_COMPLETE_ERROR : There was an error during the authentication
+process forcing the authentication cycle to terminate early.
+
+As the 'Authenticate()' function may need to send HTTP headers, it must be
+called before any output (e.g. HTML, HTTP headers) is sent to the browser.
+
+The 'Default.asp' file provided is a sample of a simple server page using
+the 'Ucam_Webauth' VBScript class.
+
+Overview of Raven process
+-------------------------
+A diagram of the Raven authentication process is located within the 'docs'
+folder as [I - Overview of Raven Authentication Process.pdf].
+
+The authentication cycle consists of the following key stages:
+
+#### User first tries to access authenticated page
+User tries to load an authenticated page on a particular website.
+The 'Ucam_Webauth' class is loaded and the 'Authenticate()' function is called.
+If no authentication cookie is found to indicate the user is authenticated,
+the user's browser is redirected to a separate Raven server using a special
+'Authentication Request'. The authentication request consists of a series of
+authentication parameters encoded into the URL redirect as name/value pairs.
+
+#### User is prompted for login information
+The Raven server interprets the authentication request sent by the main
+website and prompts the user for their username and password. The user may
+then be successfully authenticated or may decide to click 'Cancel'. They are
+redirected back to the main website with a series of 'Authentication Response'
+parameters encoded into a 'WLS-Response' GET variable.
+
+#### User redirected back to main webserver
+The user's original page is loaded again and 'Authenticate()' is called a
+second time. 'Ucam_Webauth' processes the new 'WLS-Response' GET value and,
+if it's valid, sets an authentication cookie on the user's browser. The
+user's original page is then loaded again.
+
+#### User redirected back to main webserver again
+With an authentication cookie now set, 'Authenticate()' checks the status
+code contained in the value of the authentication cookie and returns either
+'AUTHENTICATE_COMPLETE_AUTHENTICATED' or
+'AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED'. If
+'AUTHENTICATE_COMPLETE_AUTHENTICATED' is returned , the original page can
+go on to display authenticated content to the user.
+
+Specifics of module
+-------------------
+The 'Authenticate()' function is the overarching authentication function of
+'Ucam_Webauth'. It performs some basic sanity checks using 'CheckSetup()'
+then uses 'GetCurrentState()' to establish the current state of the
+authentication process before branching accordingly. The possible return
+values of 'GetCurrentState()' are:
+
+#### STATE_NEW_AUTHENTICATION
+A completely fresh authentication. 'SendAuthenticationRequest()' [*1*] is
+then triggered which performs some basic data checks, sets the authentication
+cookie to 'AUTHENTICATIONCOOKIE_REDIRECT_WLS' (to record where we are in the
+authentication process) and redirects the user's browser to the Raven
+authentication server with a series of authentication parameters
+encoded as name/value pairs.
+
+#### STATE_WLS_RESPONSE_RECEIVED
+The Raven authentication server has processed the user and has returned the
+user's browser back to the original website with a series of authentication
+response parameters encoded into the 'WLS-Response' GET variable.
+'ProcessAuthenticationResponse()' [*2*] is then called which checks the
+validity of the 'WLS-Response' value, sets an authentication cookie and
+redirects the user back to the original page.
+
+#### STATE_WAA_AUTHENTICATIONCOOKIE_SET
+A valid authentication cookie has been set
+(<> AUTHENTICATIONCOOKIE_REDIRECT_WLS).
+'ProcessAuthenticationCookie()' [*3*] is then called which checks the
+validity of the cookie. If the cookie has expired,
+'SendAuthenticationRequest()' is called again in case the user needs to
+reauthenticate themselves. If the cookie is still valid, an
+'AUTHENTICATE_COMPLETE_XXX' value is returned, indicating that the
+authentication cycle has completed successfully.
+NOTE: this may be true if the user has cancelled the authentication process,
+in which case 'AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED' will be returned.
+
+#### STATE_ERROR
+An error occurred, breaking the authentication cycle and returning
+AUTHENTICATE_COMPLETE_ERROR to 'Authenticate()'. This return value will
+also be generated by the other functions above if they generate an error.
+
+## Accompanying diagrams
+Detailed diagrams of the Raven process flow for (i) a successful
+authentication (ii) a cancelled authentication, are located in the 'docs'
+folder as [II - Ucam_Webauth - Flowchart for Valid Authentication.pdf] and
+[III - Ucam_Webauth - Flowchart for Cancelled Authentication.pdf], respectively.
+
+The numbers on these diagrams correspond to the three key secondary functions
+described above:
+- *1*. SendAuthenticationRequest()
+- *2*. ProcessAuthenticationResponse()
+- *3*. ProcessAuthenticationCookie()
+
+Other important functions include:
+
+### ResetState()
+Attempts to reset state as if a new user has just loaded a fresh browser
+window. This is typically used when a user has experienced an error and we
+want to give them a fresh opportunity to try again.
+
+### check_signature(...)
+Checks the signature provided by the Raven server, when it signed the
+'WLS-Response' variable, is a valid signature for the data. This ensures the
+data has not been tampered with.
+
+### hmac_sha1(...)
+Creates a hash value for signing the local authentication cookie.
+
+### wls_encode/decode(...)
+Encoding/decoding functions to allow base64 signatures to be sent within URLs.
+
+Possible arguments to 'Ucam_Webauth'
+------------------------------------
+(Based on documentation for PHP Raven authentication module)
+
+- log_file :
+The location for a log file that will record progress and track possible
+errors. The folder containing the file must be read/writable by the webserver.
+Default: log.txt
+
+- response_timeout :
+Responses from the central authentication server are time-stamped.
+This parameter sets the period of time in seconds during which these
+responses are considered valid.
+Default: 30 seconds.
+
+- key_dir :
+The name of the directory containing the public key certificate(s) required
+to validate the authentication responses sent by the server.
+Default: '/etc/httpd/conf/webauth_keys'.
+
+- max_session_life :
+The maximum period of time in seconds for which an established session will
+be valid. This may be overriden if the authentication reply contains a
+shorter 'life' parameter. Note that this does NOT define an expiry time for
+the session cookie. Session cookies are always set without an expiry time,
+causing them to expire when the browser session finishes.
+Default: 7200 (2 hours).
+
+- timeout_message :
+A re-authentication by the authentication service will be triggered when an
+established session expires. This option sets a text string which is sent to
+the authentication server to explain to the user why they are being asked to
+authenticate again. HTML markup is suppressed as for the description
+parameter described below.
+Default: 'your login to the site has expired'.
+
+- hostname (required) :
+The fully-qualified TCP/IP hostname that should be used in request URLs
+referencing the Ucam_Webauth-enabled application. This *must* be set, as it
+is needed for multiple reasons - primarily security but also to avoid varying
+hostnames in URLs leading to failed or inconsistent authentication.
+No default.
+
+- cookie_key (required):
+A random key used to protect session cookies from tampering. Any reasonably
+unpredictable string (for example the MD5 checksum of a rapidly changing
+logfile) will be satisfactory. This key must be the same for all uses of the
+web authentication system that will receive the same session cookies (see the
+cookie_name, cookie_path and cookie_domain parameters below).
+No default.
+
+- cookie_name :
+The name used for the session cookie.
+When used for access to resources over HTTPS the string '-S' is appended to
+this name.
+Default: 'Ucam-Webauth-Session'.
+
+- cookie_path :
+The 'Path' attribute for the session cookie. The default is the directory
+component of the path to the script currently being executed. This should
+result in the cookie being returned for future requests for this script and
+for the other resources in the same 'directory'; see the important
+information about the cookie_key parameter above.
+Default: '/'.
+
+- cookie_domain :
+The 'Domain' attribute for the session cookie. By default the 'Domain'
+attribute is omitted when setting the cookie. This should result in the
+cookie being returned only to the server running the script. Be aware that
+some people may treat with suspicion cookies with domain attributes that are
+wider than the host setting the cookie.
+No default.
+
+- auth_service : The full URL for the web login service to be used.
+Default: 'https://raven.cam.ac.uk/auth/authenticate.html'
+
+#### Authentication request properties
+The following setup parameters prefixed with 'authrequest_' relate to
+properties that will be sent to the authentication server as part of an
+authentication request:
+
+- authrequest_desc : A text description of the resource that is requesting
+authentication. This may be displayed to the user by the authentication
+service. It is restricted to printable ASCII characters (0x20 - 0x7e) though
+it may contain HTML entities representing other characters. The characters
+'<' and '>' will be converted into HTML entities before being sent to the
+browser and so this text cannot usefully contain HTML markup.
+No default.
+
+- authrequest_params : Data that will be returned unaltered to the WAA in
+any 'authentication response message' issued as a result of this request.
+This could be used to carry the identity of the resource originally requested
+or other WAA state, or to associate authentication requests with their
+eventual replies. When returned, this data will be protected by the digital
+signature applied to the authentication response message but nothing else is
+done to ensure the integrity or confidentiality of this data - the WAA MUST
+take responsibility for this if necessary.
+No default.
+
+- authrequest_skew : Interpretation of response_timeout is difficult if the
+clocks on the server running the PHP agent and on the authentication server
+are out of step. Both servers should use NTP to synchronize their clocks,
+but if they don't then this parameter should be set to an estimate of the
+maximum expected difference between them (in seconds).
+Default: 0.
+
+- authrequest_fail :
+If TRUE, sets the fail parameter in any authentication request sent to the
+authentication server to 'yes'. This has the effect of requiring the
+authentication server itself to report any errors that it encounters, rather
+than returning an error indication. Note however that even with this parameter
+set errors may be detected by this module that will result in authentication
+failing here.
+Default: FALSE.
+
+- authrequest_iact :
+If TRUE, then the 'iact' parameter provided to the authentication server is
+set to 'yes'. If FALSE, then the 'iact' parameter is set to 'no'. If no value
+is provided for 'authrequest_iact', the 'iact' parameter is left blank.
+The value 'yes' for 'iact' requires that a re-authentication exchange takes
+place with the user. This could be used prior to a sensitive transaction in
+an attempt to ensure that a previously authenticated user is still present
+at the browser. The value 'no' requires that the authentication request will
+only succeed if the user's identity can be returned without interacting with
+the user. This could be used as an optimisation to take advantage of any
+existing authentication but without actively soliciting one. If omitted or
+empty, then a previously established identity may be returned if the WLS
+supports doing so, and if not then the user will be prompted as necessary.
+Default: omitted.
\ No newline at end of file
diff --git a/Test.asp b/Test.asp
new file mode 100644
index 0000000..cfb68e9
--- /dev/null
+++ b/Test.asp
@@ -0,0 +1,219 @@
+
+
+
+
+
+
+<%
+
+Sub Main
+
+ ' Examine the 'UcamWebAuthTestingClient' cookie.
+ ' This cookie is used to store the index number of
+ ' the LAST test and the outcome of that test. This
+ ' cookie is only updated when an authentication
+ ' cycle is complete (which could be due to an
+ ' error) so we use the cookie to determine the
+ ' index of the CURRENT test.
+ '
+ ' NOTE: This cookie is completely separate
+ ' from the 'Authentication cookie' and is
+ ' purely used for testing.
+ '
+ ' The actual value of the cookie consists of
+ ' three elements delimited by '!':
+ '
+ ' 'index!status_code!status_message'
+
+ testing_client_cookie_name = "UcamWebAuthTestingClient"
+ testing_client_cookie_index = 1
+
+ If (Request.Cookies(testing_client_cookie_name) <> "") Then
+ testing_client_cookie_value = Request.Cookies(testing_client_cookie_name)
+ testing_client_cookie_array = Split(testing_client_cookie_value, "!")
+ testing_client_cookie_index = CInt(testing_client_cookie_array(0))
+ testing_client_cookie_index = testing_client_cookie_index + 1
+ End If
+
+ ' Create 'Scripting.Dictionary' object
+ ' to hold the arguments we will supply
+ ' to the 'Ucam_Webauth' object.
+
+ Set args = CreateObject("Scripting.Dictionary")
+
+ ' Add the different arguments to the 'args'
+ ' associative array as name/value pairs.
+ ' Both name and value must be strings
+ ' so integers must be added as "x", eg. "60".
+
+ ' 'auth_service' is the WLS authentication server.
+ ' The following line gives the the demo Raven testing server:
+
+ ' args.Add "auth_service", "https://demo.raven.cam.ac.uk/auth/authenticate.html"
+
+ ' We're testing with our Dummy Raven WLS server so use that:
+ args.Add "auth_service", "http://www2.careers.cam.ac.uk:11812"
+
+ ' 'hostname' must be a domain name and perhaps a
+ ' port but nothing more.
+
+ args.Add "hostname", "localhost:81"
+
+ ' 'log_file' is the location of the logfile
+ ' which must be read/writable by the webserver.
+
+ args.Add "log_file", "C:/wamp/www/raven/vbscriptlog.txt"
+
+ ' 'key_dir' is the directory holding the
+ ' public key certificate.
+
+ args.Add "key_dir", "C:/wamp/www/raven"
+
+ ' 'cookie_key' is the key used to generate
+ ' hash values of the authentication cookie.
+ ' Ideally it should be changed on a regular basis
+ ' but not during sessions.
+
+ args.Add "cookie_key", "Random string"
+
+ ' We add the current iteration of testing in
+ ' 'testing_client_cookie_index' as a parameter
+ ' to Ucam_Webauth which should then be
+ ' included as a parameter in the authentication
+ ' request to the WLS and the subsequent
+ ' authentication response back from the WLS.
+
+ args.Add "authrequest_params", CStr(testing_client_cookie_index)
+
+
+ ' Create new instance of 'Ucam_Webauth'
+ ' and supply arguments.
+ ' We do not need to include 'Request' and 'Response'
+ ' variables (as in C# version), in order to get/set
+ ' cookies and server variables and perform redirects
+ ' as these variables are globally accessible to ASP class.
+
+ Set oUcam_Webauth = New Ucam_Webauth
+ Call oUcam_Webauth(args)
+
+ ' For the purposes of testing, we provide
+ ' a 'Logout' link that removes the local
+ ' authentication cookie and then displays
+ ' a link to easily logout the Raven WLS.
+ ' So we check to see if this 'Action=Logout'
+ ' link has been called and logout/display
+ ' link accordingly.
+
+ If (Request.ServerVariables("QUERY_STRING") = "Action=Logout") Then
+ oUcam_Webauth.ResetState()
+ Response.Write("Logged out of Raven (local)
")
+ Response.Write("Logout Raven (remote)
" & _
+ "Access Raven authenticated page")
+ Exit Sub
+ End If
+
+ ' When you first access this page
+ ' the 'Authenticate' function will be called.
+ ' This will typically be called three times
+ ' in total to successfully authenticate the
+ ' user. In the first two iterations of
+ ' 'Authenticate', it will return
+ ' 'AUTHENTICATE_INCOMPLETE' while it
+ ' redirects the user's browser first to
+ ' the Raven WLS and then back to this page.
+ ' On the third iteration of 'Authenticate', it
+ ' will return 'AUTHENTICATE_COMPLETE_AUTHENTICATED'
+ ' or 'AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED'
+ ' if the authentication process has fully
+ ' completed without error.
+
+ Select Case oUcam_Webauth.Authenticate()
+
+ Case oUcam_Webauth.AUTHENTICATE_INCOMPLETE
+
+ ' 'Authenticate' still redirecting pages
+ ' so don't do anything else.
+
+ Exit Sub
+
+ Case oUcam_Webauth.AUTHENTICATE_COMPLETE_AUTHENTICATED
+
+ ' Success so display the 'principal', ie. the user id.
+
+ Response.Write("SUCCESS. You are " & oUcam_Webauth.principal() & "
")
+
+ ' Also display the 'ptags' parameter indicating
+ ' whether the user is 'current' or not.
+
+ Response.Write("Ptags = " & oUcam_Webauth.ptags() & "
")
+
+ ' Display any 'GET variables' to check they
+ ' have carried through from the original
+ ' page request.
+
+ For Each item In Request.QueryString()
+ Response.Write item & "=" & Request.QueryString()(item) & "
"
+ Next
+
+ ' Display a 'Logout' link to make it easy to
+ ' test authentication repeatedly.
+
+ Response.Write("Logout Raven (local)")
+
+ Case Else
+
+ ' Either there was an error or a failed
+ ' authentication so print out the result either way.
+
+ Response.Write("FAIL - " & oUcam_Webauth.status() & ": " & oUcam_Webauth.msg())
+
+ ' Also log the error for debugging purposes.
+
+ oUcam_Webauth.write_log("FAIL - " & oUcam_Webauth.status() & ": " & oUcam_Webauth.msg())
+
+ End Select
+
+ ' We use a 'UcamWebAuthTestingClient' cookie
+ ' to store the return 'status' of the most recent
+ ' authentication attempt. The Dummy WLS server
+ ' looks at the value of this cookie, compares
+ ' it with its most recent attempt to generate a
+ ' particular status and logs the results.
+ ' Ideally the status/error the Dummy WLS server
+ ' tried to generate should match the status/error
+ ' recorded here.
+ '
+ ' NOTE: The Dummy WLS server only performs the
+ ' comparison of 'actual' and 'expected' when
+ ' it receives a subsequent authentication request.
+ ' ie. when testing is terminated, the final
+ ' authentication attempt comparison may be lost.
+
+ ' Store number of testing iteration, return status and status msg.
+ ' To make it a session cookie, we don't specify 'Expires'.
+
+ Response.Cookies("UcamWebAuthTestingClient") = CStr(testing_client_cookie_index) & "!" & oUcam_Webauth.status() & "!" & oUcam_Webauth.msg()
+
+ ' We intend to perform another authentication attempt
+ ' so reset the state of Ucam_Webauth, ie. remove
+ ' the authentication cookie.
+
+ oUcam_Webauth.ResetState()
+
+
+End Sub
+
+Call Main
+
+%>
+
+
+
+
diff --git a/Ucam_Webauth.vbs b/Ucam_Webauth.vbs
new file mode 100644
index 0000000..d80fbe8
--- /dev/null
+++ b/Ucam_Webauth.vbs
@@ -0,0 +1,1930 @@
+<%
+
+' This VBScript class implements a Raven v3 agent for the University of Cambridge
+' Web Authentication System.
+'
+' See http://raven.cam.ac.uk/project/ for more details
+'
+' Loosely based on the PHP module for Raven
+' https://wiki.cam.ac.uk/raven/PHP_library
+'
+' Copyright (c) 2004, 2005, 2008, 2014 University of Cambridge
+'
+' This module is free software; you can redistribute it and/or modify
+' it under the terms of the GNU Lesser General Public License as
+' published by the Free Software Foundation; either version 2.1 of the
+' License, or (at your option) any later version.
+'
+' The module is distributed in the hope that it will be useful, but
+' WITHOUT ANY WARRANTY; without even the implied warranty of
+' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+' Lesser General Public License for more details.
+'
+' You should have received a copy of the GNU Lesser General Public
+' License along with this toolkit; if not, write to the Free Software
+' Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+' USA
+'
+' $Id: ucam_webauth.vbs, v1.0 2014/04/05 08:13:00 sh801
+'
+' Version 1.0
+
+
+Class Ucam_Webauth
+
+ ' ****************************************
+ '
+ ' ----------------------------------------
+ ' ---------- class Ucam_Webauth ----------
+ ' ----------------------------------------
+ '
+ ' ****************************************
+ '
+ ' Methods are listed in order of importance
+ ' with MAIN functions at top and lower-
+ ' level functions towards the end.
+ '
+ ' ****************************************
+
+
+ ' 'status_codes' is associative array of status codes of the form {"CODE" => "Description", ..}
+
+ Private status_codes
+
+ ' ****************************************
+ ' Default values for key parameters
+ ' ****************************************
+
+ Private PROTOCOL_VERSION
+ Private AUTHENTICATION_RESPONSE_VERSION
+ Private DEFAULT_AUTH_SERVICE
+ Private DEFAULT_KEY_DIR
+ Private DEFAULT_COOKIE_NAME
+ Private DEFAULT_TIMEOUT_MESSAGE
+ Private DEFAULT_HOSTNAME
+ Private WLS_LOGOUT
+ Private DEFAULT_LOG_FILE
+ Private AUTHENTICATIONCOOKIE_REDIRECT_WLS
+
+ ' ****************************************
+ ' Constants for tracking state
+ ' ****************************************
+ '
+ ' The 'Authenticate' function is called multiple
+ ' times for a successful authentication so we
+ ' need to track where we are in the authentication
+ ' process with some constants.
+
+ Private STATE_ERROR
+ Private STATE_NEW_AUTHENTICATION
+ Private STATE_WLS_RESPONSE_RECEIVED
+ private STATE_WAA_AUTHENTICATIONCOOKIE_SET
+
+ ' ****************************************
+ ' Constants for returning state
+ ' ****************************************
+ '
+ ' The 'Authenticate' function needs to
+ ' return relevant information as to where
+ ' it currently is in the authentication process.
+
+ Public AUTHENTICATE_INCOMPLETE
+ Public AUTHENTICATE_COMPLETE_ERROR
+ Public AUTHENTICATE_COMPLETE_AUTHENTICATED
+ Public AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED
+
+ ' ****************************************
+ ' Index numbers for the 'Authentication response' fields
+ ' ****************************************
+ '
+ ' See Raven specification documentation for descriptions of each parameter
+
+ Private AUTHENTICATION_RESPONSE_VER
+ Private AUTHENTICATION_RESPONSE_STATUS
+ Private AUTHENTICATION_RESPONSE_MSG
+ Private AUTHENTICATION_RESPONSE_ISSUE
+ Private AUTHENTICATION_RESPONSE_EXPIRE
+ Private AUTHENTICATION_RESPONSE_ID
+ Private AUTHENTICATION_RESPONSE_PRINCIPAL
+ Private AUTHENTICATION_RESPONSE_PTAGS
+ Private AUTHENTICATION_RESPONSE_AUTH
+ Private AUTHENTICATION_RESPONSE_SSO
+ Private AUTHENTICATION_RESPONSE_PARAMS
+ Private AUTHENTICATION_RESPONSE_SIG
+ Private AUTHENTICATION_RESPONSE_SIZE ' Size of required array
+
+ Private WLS_RESPONSE_VER
+ Private WLS_RESPONSE_STATUS
+ Private WLS_RESPONSE_MSG
+ Private WLS_RESPONSE_ISSUE
+ Private WLS_RESPONSE_ID
+ Private WLS_RESPONSE_URL
+ Private WLS_RESPONSE_PRINCIPAL
+ Private WLS_RESPONSE_PTAGS
+ Private WLS_RESPONSE_AUTH
+ Private WLS_RESPONSE_SSO
+ Private WLS_RESPONSE_LIFE
+ Private WLS_RESPONSE_PARAMS
+ Private WLS_RESPONSE_KID
+ Private WLS_RESPONSE_SIG
+ Private WLS_RESPONSE_SIZE ' Size of required array
+
+ ' ****************************************
+ ' General private member variables
+ ' ****************************************
+
+ Private m_response_timeout
+ Private m_authrequest_skew
+ Private m_max_session_life
+ Private m_redirected
+ Private m_use_authrequest_iact
+ Private m_authrequest_iact
+ Private m_authrequest_fail
+ Private m_authrequest_desc
+ Private m_authrequest_aauth
+ Private m_authrequest_params
+ Private m_authentication_response_string
+ Private m_authentication_cookie
+ Private m_auth_service
+ Private m_hostname
+ Private m_key_dir
+ Private m_timeout_message
+ Private m_cookie_key
+ Private m_cookie_path
+ Private m_cookie_name
+ Private m_cookie_domain
+ Private m_log_file
+ Private m_authentication_response
+
+ ' ****************************************
+ '
+ ' ----------------------------------------
+ ' ----------- MAIN FUNCTIONS -------------
+ ' ----------------------------------------
+ '
+ ' ****************************************
+
+ Private Sub Class_Initialize
+
+ ' Initialize all constants as it's not possible
+ ' to declare initial values in class definition, above.
+
+ PROTOCOL_VERSION = "3"
+ AUTHENTICATION_RESPONSE_VERSION = "3"
+ DEFAULT_AUTH_SERVICE = "https://raven.cam.ac.uk/auth/authenticate.html"
+ DEFAULT_KEY_DIR = "/etc/httpd/conf/webauth_keys"
+ DEFAULT_COOKIE_NAME = "Ucam-WebAuth-Session"
+ DEFAULT_TIMEOUT_MESSAGE = "your logon to the site has expired"
+ DEFAULT_HOSTNAME = "" ' must be supplied explicitly
+ WLS_LOGOUT = "Not-authenticated"
+ DEFAULT_LOG_FILE = "log.txt"
+ AUTHENTICATIONCOOKIE_REDIRECT_WLS = "REDIRECT_WLS"
+
+ STATE_ERROR = -1
+ STATE_NEW_AUTHENTICATION = 0
+ STATE_WLS_RESPONSE_RECEIVED = 1
+ STATE_WAA_AUTHENTICATIONCOOKIE_SET = 2
+
+ AUTHENTICATE_INCOMPLETE = 0
+ AUTHENTICATE_COMPLETE_ERROR = -1
+ AUTHENTICATE_COMPLETE_AUTHENTICATED = 1
+ AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED = 2
+
+ AUTHENTICATION_RESPONSE_VER = 0
+ AUTHENTICATION_RESPONSE_STATUS = 1
+ AUTHENTICATION_RESPONSE_MSG = 2
+ AUTHENTICATION_RESPONSE_ISSUE = 3
+ AUTHENTICATION_RESPONSE_EXPIRE = 4
+ AUTHENTICATION_RESPONSE_ID = 5
+ AUTHENTICATION_RESPONSE_PRINCIPAL = 6
+ AUTHENTICATION_RESPONSE_PTAGS = 7
+ AUTHENTICATION_RESPONSE_AUTH = 8
+ AUTHENTICATION_RESPONSE_SSO = 9
+ AUTHENTICATION_RESPONSE_PARAMS = 10
+ AUTHENTICATION_RESPONSE_SIG = 11
+ AUTHENTICATION_RESPONSE_SIZE = 12 ' Size of required array
+
+ WLS_RESPONSE_VER = 0
+ WLS_RESPONSE_STATUS = 1
+ WLS_RESPONSE_MSG = 2
+ WLS_RESPONSE_ISSUE = 3
+ WLS_RESPONSE_ID = 4
+ WLS_RESPONSE_URL = 5
+ WLS_RESPONSE_PRINCIPAL = 6
+ WLS_RESPONSE_PTAGS = 7
+ WLS_RESPONSE_AUTH = 8
+ WLS_RESPONSE_SSO = 9
+ WLS_RESPONSE_LIFE = 10
+ WLS_RESPONSE_PARAMS = 11
+ WLS_RESPONSE_KID = 12
+ WLS_RESPONSE_SIG = 13
+ WLS_RESPONSE_SIZE = 14 ' Size of required array
+
+ ' Set up 'status_codes' associative array
+ ' See Raven specification documentation for descriptions of each parameter
+
+ Set status_codes = CreateObject("Scripting.Dictionary")
+
+ status_codes.Add "200", "Successful authentication"
+ status_codes.Add "410", "The user cancelled the authentication request"
+ status_codes.Add "510", "No mutually acceptable authentication types available"
+ status_codes.Add "520", "Unsupported protocol version"
+ status_codes.Add "530", "General request parameter error"
+ status_codes.Add "540", "Interaction would be required"
+ status_codes.Add "560", "WAA not authorised"
+ status_codes.Add "570", "Authentication declined"
+
+ ' Set up array for holding authentication response parameters
+
+ ReDim m_authentication_response(AUTHENTICATION_RESPONSE_SIZE)
+
+ End Sub
+
+
+ Public Default Function Construct(ByVal args)
+
+ ' ****************************************
+ ' Constructor for Ucam_Webauth
+ '
+ ' We split the constructor that takes
+ ' parameters from the initializer as
+ ' VBScript doesn't allow initializers
+ ' to take parameters.
+ '
+ ' Arguments
+ ' ================
+ ' args: An associative array of arguments as name,value pairs, eg. {"auth_service" => "http://..", "hostname" => "www...", "log_file" => }.
+ '
+ ' To supply arguments:
+ ' Set args = CreateObject("Scripting.Dictionary")
+ ' args.Add auth_service", "https://..."
+ ' args.Add hostname", "www..."
+ ' args.Add log_file", "C:/logfile.txt"
+ ' args.Add key_dir", "C:/raven"
+ ' args.Add cookie_key", "Random string"
+ '
+ ' ****************************************
+
+
+ ' Set up default values
+
+ log_file = DEFAULT_LOG_FILE
+ response_timeout = 30
+ key_dir = DEFAULT_KEY_DIR
+ max_session_life = 2 * 60 * 60
+ timeout_message = DEFAULT_TIMEOUT_MESSAGE
+ hostname = DEFAULT_HOSTNAME
+ cookie_name = DEFAULT_COOKIE_NAME
+ cookie_path = "" ' *** SHOULD BE PATH RELATIVE PATH TO SCRIPT BY DEFAULT ***
+ cookie_domain = ""
+ auth_service = DEFAULT_AUTH_SERVICE
+ authrequest_skew = 5
+ authrequest_fail = False
+ authrequest_iact = False
+ use_authrequest_iact = False
+
+ ' If specific arguments are provided, then override default values
+
+ If (args.Exists("log_file")) Then log_file = args("log_file")
+ If (args.Exists("response_timeout")) Then response_timeout = Int(args("response_timeout"))
+ If (args.Exists("key_dir")) Then key_dir = args("key_dir")
+ If (args.Exists("max_session_life")) Then max_session_life = Int(args("max_session_life"))
+ If (args.Exists("timeout_message")) Then timeout_message = args("timeout_message")
+ If (args.Exists("hostname")) Then hostname = args("hostname")
+ If (args.Exists("cookie_key")) Then cookie_key = args("cookie_key")
+ If (args.Exists("cookie_name")) Then cookie_name = args("cookie_name")
+ If (args.Exists("cookie_path")) Then cookie_path = args("cookie_path")
+ If (args.Exists("cookie_domain")) Then cookie_domain = args("cookie_domain")
+ If (args.Exists("auth_service")) Then auth_service = args("auth_service")
+ If (args.Exists("authrequest_desc")) Then authrequest_desc = args("authrequest_desc")
+ If (args.Exists("authrequest_params")) Then authrequest_params = args("authrequest_params")
+ If (args.Exists("authrequest_skew")) Then authrequest_skew = Int(args("authrequest_skew"))
+ If (args.Exists("authrequest_fail")) Then authrequest_fail = CBool(args("authrequest_fail"))
+ If (args.Exists("authrequest_iact")) Then
+ authrequest_iact = CBool(args("authrequest_iact"))
+ use_authrequest_iact = True
+ End If
+
+ End Function
+
+ ' ****************************************
+ '
+ ' ----------------------------------------
+ ' -- PRIMARY FUNCTION - Authenticate() ---
+ ' ----------------------------------------
+ '
+ ' ****************************************
+
+ Public Function Authenticate()
+
+ ' ****************************************
+ ' Authenticate()
+ '
+ ' This function is called three times in
+ ' order to complete the authentication process.
+ '
+ ' STEP 1: A fresh user requests an Authenticated
+ ' resource with the URL 'X'. 'Authenticate'
+ ' redirects the user's browser to a Web Login
+ ' Service (WLS) via 'SendAuthenticationRequest()'.
+ '
+ ' STEP 2: The WLS processes the user's user id
+ ' and password and then redirects the user's
+ ' browser back to the client's Web Application
+ ' Agent (WAA) with a specific set of parameters
+ ' in a 'WLS-Response' GET variable.
+ ' 'Authenticate' is called again and validates
+ ' these values using 'ProcessAuthenticationRequest()'.
+ ' If they all check out, it creates a
+ ' local authentication cookie on the user's
+ ' browser and redirects the user's browser
+ ' back to the original URL 'X'.
+ '
+ ' STEP 3: We return to our original page, except
+ ' this time we have a local authentication cookie
+ ' set. 'Authenticate' calls
+ ' 'ProcessAuthenticationCookie' which checks
+ ' this cookie is valid and if the status code
+ ' of the cookie is '200', the user was successfully
+ ' verified at the WLS -
+ ' 'AUTHENTICATE_COMPLETE_AUTHENTICATED' is then
+ ' returned to the user.
+ '
+ ' ****************************************
+
+
+ ' First off, perform a sanity check on basic variables
+
+ If (CheckSetup() = False) Then
+ ResetState()
+ Authenticate = AUTHENTICATE_COMPLETE_ERROR
+ Exit Function
+ End If
+
+ ' Now work out where we are in the authentication process:
+ '
+ ' STATE_NEW_AUTHENTICATION
+ ' A completely fresh authentication attempt.
+ '
+ ' STATE_WLS_RESPONSE_RECEIVED
+ ' The user has already been redirected to the WLS, which has
+ ' then redirected them back here. They may have successfully
+ ' authenticated or simply pressed cancel. As long as it's a
+ ' valid, signed WLS-Response - regardless of whether it's a
+ ' successful authentication or not - we set a new
+ ' authentication cookie which indicates to the code
+ ' that the user has been processed by the WLS.
+ '
+ ' STATE_WAA_AUTHENTICATIONCOOKIE_SET
+ ' We have a valid authentication cookie and just need to
+ ' check what the status of that is. The user may have
+ ' successfully authenticated, the authentication token
+ ' may have expired, or the user may have skipped out
+ ' the authentication process. In the event of expiration,
+ ' 'ProcessAuthenticationCookie' redirects the user back
+ ' to the WLS.
+ '
+ ' STATE_ERROR
+ ' There was an error somewhere along the way.
+ ' If this happens, it's not a simple case of
+ ' an invalid authentication but something more serious
+ ' that falls outside the expected process flow.
+
+ state = GetCurrentState()
+
+ Select Case state
+
+ Case STATE_NEW_AUTHENTICATION
+
+ Authenticate = SendAuthenticationRequest("")
+ Exit Function
+
+ Case STATE_WLS_RESPONSE_RECEIVED
+
+ Authenticate = ProcessAuthenticationResponse()
+ Exit Function
+
+ Case STATE_WAA_AUTHENTICATIONCOOKIE_SET
+
+ Authenticate = ProcessAuthenticationCookie()
+ Exit Function
+
+ Case Else ' This includes the case STATE_ERROR
+
+ write_log("STATE_ERROR received: " & status() & ": " & msg())
+ ResetState()
+ Authenticate = AUTHENTICATE_COMPLETE_ERROR
+ Exit Function
+
+ End Select
+
+ End Function
+
+
+ ' ****************************************
+ '
+ ' ----------------------------------------
+ ' ---- SECONDARY FUNCTIONS TRIGGERED -----
+ ' ------- FROM 'Authenticate()' ----------
+ ' ----------------------------------------
+ '
+ ' 1. SendAuthenticationRequest()
+ ' 2. ProcessAuthenticationResponse()
+ ' 3. ProcessAuthenticationCookie()
+ '
+ ' ****************************************
+
+ Public Function SendAuthenticationRequest(msg)
+
+ ' ****************************************
+ ' 1. SendAuthenticationRequest()
+ '
+ ' Send an authentication request from the WAA to the WLS.
+ '
+ ' ****************************************
+
+
+ ' Write a long line of astericks to make log easier to read.
+
+ write_log("****************************************************************")
+ write_log("SendAuthenticationRequest: Starting...")
+
+ ' If the hostname from the request (Host: header) does not match the
+ ' server's preferred name for itself (which should be what's configured
+ ' as hostname), cookies are likely to break "randomly" (or more
+ ' accurately, the cookie may not be sent by the browser since it"s for
+ ' a different hostname) as a result of following links that use the
+ ' preferred name, or server-level redirects e.g. to fix "directory"
+ ' URLs lacking the trailing "/". Attempt to avoid that by redirecting
+ ' to an equivalent URL using the configured hostname.
+
+ http_host = Request.ServerVariables("HTTP_HOST")
+
+ If ((http_host <> "") And (LCase(hostname) <> LCase(http_host))) Then
+ write_log("SendAuthenticationRequest: Redirect to tidy up hostname mismatch")
+ Redirect(url())
+ SendAuthenticationRequest = AUTHENTICATE_INCOMPLETE
+ Exit Function
+ End If
+
+ ' We set the authentication cookie to 'AUTHENTICATIONCOOKIE_REDIRECT_WLS'
+ ' to keep a track of state.
+
+ write_log("SendAuthenticationRequest: Setting pre-session cookie")
+
+ authentication_cookie = AUTHENTICATIONCOOKIE_REDIRECT_WLS
+
+ ' Build the full 'Authentication Request' URL that will
+ ' redirect the user to the WLS.
+ '
+ ' Full information about each of these parameters can be
+ ' found in Raven v3 documentation.
+
+ wls_redirect = auth_service
+ wls_redirect = wls_redirect & "?ver=" & PROTOCOL_VERSION
+ wls_redirect = wls_redirect & "&url=" & Server.URLEncode(url())
+
+ If (authrequest_desc <> "") Then wls_redirect = wls_redirect & "&desc=" & Server.URLEncode(authrequest_desc)
+ If (authrequest_aauth <> "") Then wls_redirect = wls_redirect & "&aauth=" & Server.URLEncode(authrequest_aauth)
+
+ If use_authrequest_iact Then
+ If authrequest_iact Then
+ wls_redirect = wls_redirect & "&iact=yes"
+ Else
+ wls_redirect = wls_redirect & "&iact=no"
+ End If
+ End If
+
+ If (msg <> "") Then wls_redirect = wls_redirect & "&msg=" & Server.URLEncode(msg)
+ if (authrequest_params <> "") Then wls_redirect = wls_redirect & "¶ms=" & Server.URLEncode(authrequest_params)
+
+ wls_redirect = wls_redirect & "&date=" & Server.URLEncode(time2iso(DateDiff("s", "01/01/1970 00:00:00", UTCNow())))
+
+ ' If (clock_skew <> 0) THen wls_redirect = wls_redirect & "&skew=" & Server.URLEncode(Convert.ToString(clock_skew)) ' 'skew' parameter deprecated in v3
+
+ If (authrequest_fail = True) Then wls_redirect = wls_redirect & "&fail=yes"
+
+ write_log("SendAuthenticationRequest: Redirecting to WLS with URL=" & wls_redirect)
+
+ Redirect(wls_redirect)
+
+ SendAuthenticationRequest = AUTHENTICATE_INCOMPLETE
+
+ End Function
+
+
+ Public Function ProcessAuthenticationResponse()
+
+ ' ****************************************
+ ' 2. ProcessAuthenticationResponse()
+ '
+ ' Process the authentication response received from the WLS.
+ '
+ ' ****************************************
+
+ write_log("ProcessAuthenticationResponse: Starting...")
+ write_log("ProcessAuthenticationResponse: WLS response=" & authentication_response_string)
+
+ wls_response = Split(authentication_response_string, "!")
+
+ set_status "200", ""
+
+
+ If (UBound(wls_response) < 1) Then
+
+ ' Response is too short to have been signed or have a status.
+
+ set_status "620", "WLS response has too few parameters"
+ ResetState()
+ ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR
+ Exit Function
+
+ End If
+
+ ' We only check signature if status = "200".
+
+ If (wls_response(WLS_RESPONSE_STATUS) = "200") Then
+
+ ' Get signature and key_id off the end of the wls_response array
+ ' and collapse the remaining array into a string.
+ ' It's this string that will have been signed by the WLS
+ ' and whose signature we then verify using the WLS
+ ' public certificate on our server.
+
+ signature = wls_response(UBound(wls_response))
+ key_id = wls_response(UBound(wls_response) - 1)
+ Redim Preserve wls_response (UBound(wls_response) - 2)
+ wls_response_signedstring = Join(wls_response, "!")
+
+ ' write_log("Signature=" & signature)
+ ' write_log("Signed string=" & wls_response_signedstring)
+
+ If (check_signature(wls_response_signedstring, signature, key_id) = False) Then
+
+ ' Signature is not correct for the wls_response
+
+ set_status "606", "Invalid WLS wls_response signature"
+ ResetState()
+ ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR
+ Exit Function
+
+ End If
+
+ write_log("ProcessAuthenticationResponse: Signature is correct.")
+
+ End If
+
+
+ ' Expand 'wls_response' array to maximum size just in case it's too small
+ ' and subsequent calls to wls_response(WLS_RESPONSE_XXX) raise 'IndexOutOfRangeException'
+
+ Redim Preserve wls_response (WLS_RESPONSE_SIZE)
+
+
+ ' Remove the query part of our current url
+ ' and the url provided by the WLS and check
+ ' they match.
+
+ Set rgx = New RegExp
+ rgx.Global = True
+ rgx.IgnoreCase = False
+ rgx.Pattern = "\?.*$"
+ this_url = rgx.Replace(url(), "")
+
+ If (wls_response(WLS_RESPONSE_URL) = "") Then
+ set_status "607", "Null URL in response ticket doesn't match this URL: " & this_url
+ ResetState()
+ ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR
+ Exit Function
+ End If
+
+ response_url = wls_response(WLS_RESPONSE_URL)
+ response_url = rgx.Replace(response_url, "")
+
+ If (this_url <> response_url) Then
+ set_status "607", "URL in response ticket doesn't match this URL: " & response_url & " != " & this_url
+ ResetState()
+ ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR
+ Exit Function
+ End If
+
+
+ ' Go through the possible different response codes
+ ' from the WLS and set our status accordingly.
+
+ If (wls_response(WLS_RESPONSE_STATUS) = "410") Then
+
+ set_status "410", status_codes("410")
+
+ ElseIf ((wls_response(WLS_RESPONSE_VER) <> PROTOCOL_VERSION) And (wls_response(WLS_RESPONSE_STATUS) <> "520")) Then
+
+ set_status "608", "Wrong protocol version in authentication service reply"
+
+ ElseIf (wls_response(WLS_RESPONSE_STATUS) <> "200") Then
+
+ ' If status code != "200" then we have an error of some description.
+ ' First off, check that it's an error for which we have an error code / msg.
+
+ If (status_codes.Exists(wls_response(WLS_RESPONSE_STATUS)) = False) Then
+ write_log("No status code for error " & wls_response(WLS_RESPONSE_STATUS))
+ ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR
+ Exit Function
+ End If
+
+ ' If we have an error code / msg for the error, then set status to that code / msg.
+
+ set_status wls_response(WLS_RESPONSE_STATUS), status_codes(wls_response(WLS_RESPONSE_STATUS))
+
+ ' If the authentication response provided a response message then set status msg to that.
+
+ If (wls_response(WLS_RESPONSE_MSG) <> "") Then m_authentication_response(AUTHENTICATION_RESPONSE_MSG) = wls_response(WLS_RESPONSE_MSG)
+
+ Else
+
+ ' Status code must be '200' to get here.
+ ' But we need to check the issue time of the
+ ' authentication response is close to our current time.
+
+ timestamp_now = DateDiff("s", "01/01/1970 00:00:00", UTCNow())
+ timestamp_issue = iso2time(wls_response(WLS_RESPONSE_ISSUE))
+
+ If (timestamp_issue = 0) Then
+
+ set_status "609", "Unable to read issue time in authentication service reply"
+
+ ElseIf (timestamp_issue > (timestamp_now + authrequest_skew + 1)) Then
+
+ set_status "610", "Authentication service reply apparently issued in the future: " & wls_response(WLS_RESPONSE_ISSUE)
+
+ ElseIf ((timestamp_now - authrequest_skew - 1) > (timestamp_issue + response_timeout)) Then
+
+ set_status "611", "Stale authentication service reply issue at " & wls_response(WLS_RESPONSE_ISSUE)
+
+ End If
+
+ End If
+
+
+ ' Calculate session expiry time
+
+ expiry = max_session_life
+
+ If (wls_response(WLS_RESPONSE_LIFE) <> "") Then
+
+ wls_token_life = Int(wls_response(WLS_RESPONSE_LIFE))
+
+ If ((wls_token_life > 0) And (wls_token_life < expiry)) Then expiry = wls_token_life
+
+ End If
+
+
+ ' Populate authentication response with information collected so far
+
+ m_authentication_response(AUTHENTICATION_RESPONSE_ISSUE) = CStr(time2iso(DateDiff("s", "01/01/1970 00:00:00", UTCNow())))
+ m_authentication_response(AUTHENTICATION_RESPONSE_EXPIRE) = CStr(time2iso(DateDiff("s", "01/01/1970 00:00:00", UTCNow()) + expiry))
+ m_authentication_response(AUTHENTICATION_RESPONSE_ID) = wls_response(WLS_RESPONSE_ID)
+ m_authentication_response(AUTHENTICATION_RESPONSE_PRINCIPAL) = wls_response(WLS_RESPONSE_PRINCIPAL)
+ m_authentication_response(AUTHENTICATION_RESPONSE_AUTH) = wls_response(WLS_RESPONSE_AUTH)
+ m_authentication_response(AUTHENTICATION_RESPONSE_SSO) = wls_response(WLS_RESPONSE_SSO)
+ m_authentication_response(AUTHENTICATION_RESPONSE_PARAMS) = wls_response(WLS_RESPONSE_PARAMS)
+ m_authentication_response(AUTHENTICATION_RESPONSE_PTAGS) = wls_response(WLS_RESPONSE_PTAGS)
+ m_authentication_response(AUTHENTICATION_RESPONSE_VER) = AUTHENTICATION_RESPONSE_VERSION
+
+
+ ' Collapse parameters of authentication_response into a string
+ ' then create HMAC hash code of this string and append this code
+ ' onto the string to form the value of our authentication cookie.
+
+ authentication_cookie_value = ""
+
+ For i = 0 To (AUTHENTICATION_RESPONSE_PARAMS - 1)
+ If (m_authentication_response(i) <> "") Then authentication_cookie_value = authentication_cookie_value & m_authentication_response(i)
+ authentication_cookie_value = authentication_cookie_value & "!"
+ Next
+
+ If (m_authentication_response(AUTHENTICATION_RESPONSE_PARAMS) <> "") Then
+ authentication_cookie_value = authentication_cookie_value & m_authentication_response(AUTHENTICATION_RESPONSE_PARAMS)
+ End If
+
+ hmac_signature = hmac_sha1(cookie_key, authentication_cookie_value)
+ authentication_cookie_value = authentication_cookie_value & "!" & hmac_signature
+
+ write_log("ProcessAuthenticationResponse: Setting cookie=" & authentication_cookie_value)
+
+ authentication_cookie = authentication_cookie_value
+
+ ' Now the authentication cookie's in place, we
+ ' can redirect user back to the page they started from.
+ ' As long as our authentication cookie doesn't expire,
+ ' we then only need to call 'ProcessAuthenticationCookie'
+ ' to check the cookie is valid - ie. we don't need to
+ ' keep returning to the WLS for every single
+ ' authentication request.
+
+ write_log("ProcessAuthenticationResponse: Session cookie established, redirecting...")
+
+ Redirect(wls_response(WLS_RESPONSE_URL))
+
+ ProcessAuthenticationResponse = AUTHENTICATE_INCOMPLETE
+
+ End Function
+
+
+ Public Function ProcessAuthenticationCookie()
+
+ ' ****************************************
+ ' 3. ProcessAuthenticationCookie()
+ '
+ ' Check the signature of the cookie is valid and if so,
+ ' process/interpret the values of the authentication cookie.
+ '
+ ' ****************************************
+
+ write_log("ProcessAuthenticationCookie: Starting...")
+
+ ' Do some quick checks to ensure the authentication
+ ' cookie looks like a valid response from WLS.
+
+ If (authentication_cookie = AUTHENTICATIONCOOKIE_REDIRECT_WLS) Then
+ write_log("ProcessAuthenticationCookie: Processing authentication cookie when it's still set to REDIRECT_WLS.")
+ set_status "612", "Processing authentication cookie when its still set to REDIRECT_WLS"
+ ResetState()
+ ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_ERROR
+ Exit Function
+ End If
+
+ If (CheckValidAuthenticationCookie(authentication_cookie) = False) Then
+ write_log("ProcessAuthenticationCookie: Authentication cookie looks invalid.")
+ set_status "613", "Authentication cookie invalid"
+ ResetState()
+ ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_ERROR
+ Exit Function
+ End If
+
+ write_log("ProcessAuthenticationCookie: Interpreting authentication cookie=" & authentication_cookie)
+
+ ' Split up authentication cookie into _authentication_response array
+ ' of response parameters using '!' as the delimiter.
+
+ m_authentication_response = Split(url_decode(authentication_cookie), "!")
+
+ ' ****************************************
+ ' Check authentication cookie has been correctly signed
+ ' ****************************************
+
+ ' Get signature from last element of authentication response array.
+
+ signature = m_authentication_response(UBound(m_authentication_response))
+
+ ' Copy m_authentication_response into a separate array
+ ' then remove the last element of that array
+ ' and bundle together as a string.
+
+ Dim values_for_verify()
+ ReDim values_for_verify(UBound(m_authentication_response) - 1)
+
+ For i = 0 To (UBound(values_for_verify))
+ values_for_verify(i) = m_authentication_response(i)
+ Next
+
+ values_for_verify_string = Join(values_for_verify, "!")
+
+ ' Check whether the hash of this shorter string matches the signature
+ ' we generated in the previous stage of the authentication process.
+
+ If (False = hmac_sha1_verify(cookie_key, values_for_verify_string, signature)) Then
+ write_log("ProcessAuthenticationCookie: AUTHENTICATION FAILED, session cookie signature invalid")
+ set_status "614", "Session cookie signature invalid"
+ ResetState()
+ ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_ERROR
+ Exit Function
+ End If
+
+ write_log("ProcessAuthenticationCookie: Existing authentication cookie verified")
+
+ ' ****************************************
+ ' Check authentication cookie hasn't expired
+ ' ****************************************
+
+ timestamp_issue = iso2time(m_authentication_response(AUTHENTICATION_RESPONSE_ISSUE))
+ timestamp_expire = iso2time(m_authentication_response(AUTHENTICATION_RESPONSE_EXPIRE))
+ timestamp_now = DateDiff("s", "01/01/1970 00:00:00", UTCNow()) ' Get current time as Unix timestamp
+
+ If ((timestamp_issue > timestamp_now) Or (timestamp_now >= timestamp_expire)) Then
+
+ ' Session has expired so send new authentication request to WLS.
+
+ write_log("ProcessAuthenticationCookie: Local session cookie expired. Issue/now/expire: " & Cstr(timestamp_issue) & "/" & CStr(timestamp_now) & "/" & CStr(timestamp_expire))
+
+ ' this.ResetState(); This won't work as we need to set the cookie during 'SendAuthenticationRequest'
+
+ ProcessAuthenticationCookie = SendAuthenticationRequest(timeout_message)
+ Exit Function
+
+ End If
+
+ ' ****************************************
+ ' Authentication process is COMPLETE
+ ' though the user may not have been
+ ' successfully authenticated.
+ ' ****************************************
+
+ If (m_authentication_response(AUTHENTICATION_RESPONSE_STATUS) <> "200") Then
+ write_log("ProcessAuthenticationCookie: AUTHENTICATION COMPLETE but not successfully authenticated.")
+ write_log("****************************************************************")
+ ResetState()
+ ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED
+ Exit Function
+ End If
+
+ write_log("ProcessAuthenticationCookie: AUTHENTICATION COMPLETE and user authenticated.")
+ write_log("****************************************************************")
+ ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_AUTHENTICATED
+
+ End Function
+
+
+ ' ****************************************
+ '
+ ' ----------------------------------------
+ ' --- AUTHENTICATE()-SPECIFIC FUNCTIONS --
+ ' ----------------------------------------
+ '
+ ' ****************************************
+
+ Public Function CheckSetup()
+
+ ' ****************************************
+ ' CheckSetup()
+ '
+ ' Check the essential variables are in place.
+ '
+ ' ****************************************
+
+ request_method = Request.ServerVariables("REQUEST_METHOD")
+
+ ' Check 'cookie_key' is defined, the key we will be using to create hash of cookie value.
+
+ If (cookie_key = "") Then
+ set_status "601", "No key defined for session cookie"
+ CheckSetup = False
+ Exit Function
+ End If
+
+ ' Log a warning if being used to authenticate POST requests.
+
+ If (request_method = "POST") Then
+ write_log("Ucam_Webauth agent invoked for POST request, which it doesn't really support")
+ End If
+
+ ' Check that the hostname is set explicitly (since we cannot trust
+ ' the Host: header); if it returns false (i.e. not set).
+
+ If (hostname = "") Then
+ write_log("hostname not set in Ucam_Webauth object, but is mandatory")
+ set_status "602", "Ucam_Webauth configuration error - mandatory hostname not defined"
+ CheckSetup = False
+ Exit Function
+ End If
+
+ ' If we have got this far without problems, then everything is good.
+
+ CheckSetup = True
+
+ End Function
+
+
+ Public Sub ResetState()
+
+ ' Reset state as if a new user has just loaded a
+ ' fresh browser window. Unfortunately we can't reset
+ ' any cookies that the remote WLS may have set.
+ ' This will not completely log you out of Raven as
+ ' the Raven WLS also stores a session cookie that
+ ' you cannot delete remotely. So accessing Raven
+ ' after logout may elicit a "You are already logged
+ ' in" from the Raven WLS.
+ '
+ ' As both local and WLS cookies are session cookies, the safest
+ ' thing to do is to quit the browser to remove both session cookies.
+ ' Note that some browsers with 'Restore session' functionality,
+ ' eg. Firefox, may not remove session cookies properly.
+ '
+ *
+ * NOTE: Please ignore method summary and document of this namespace. This caused by a bug of jsdoc2.
+ *
{'ecpubhex': '041f..', 'eccurvename': 'secp256r1'}
)
+ * {'ecprvhex': '1d3f..', 'eccurvename': 'secp256r1'}
)>(p+=this.DB-k);
+ }
+ else {
+ d = (this[i]>>(p-=k))&km;
+ if(p <= 0) { p += this.DB; --i; }
+ }
+ if(d > 0) m = true;
+ if(m) r += int2char(d);
+ }
+ }
+ return m?r:"0";
+}
+
+// (public) -this
+function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }
+
+// (public) |this|
+function bnAbs() { return (this.s<0)?this.negate():this; }
+
+// (public) return + if this > a, - if this < a, 0 if equal
+function bnCompareTo(a) {
+ var r = this.s-a.s;
+ if(r != 0) return r;
+ var i = this.t;
+ r = i-a.t;
+ if(r != 0) return (this.s<0)?-r:r;
+ while(--i >= 0) if((r=this[i]-a[i]) != 0) return r;
+ return 0;
+}
+
+// returns bit length of the integer x
+function nbits(x) {
+ var r = 1, t;
+ if((t=x>>>16) != 0) { x = t; r += 16; }
+ if((t=x>>8) != 0) { x = t; r += 8; }
+ if((t=x>>4) != 0) { x = t; r += 4; }
+ if((t=x>>2) != 0) { x = t; r += 2; }
+ if((t=x>>1) != 0) { x = t; r += 1; }
+ return r;
+}
+
+// (public) return the number of bits in "this"
+function bnBitLength() {
+ if(this.t <= 0) return 0;
+ return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));
+}
+
+// (protected) r = this << n*DB
+function bnpDLShiftTo(n,r) {
+ var i;
+ for(i = this.t-1; i >= 0; --i) r[i+n] = this[i];
+ for(i = n-1; i >= 0; --i) r[i] = 0;
+ r.t = this.t+n;
+ r.s = this.s;
+}
+
+// (protected) r = this >> n*DB
+function bnpDRShiftTo(n,r) {
+ for(var i = n; i < this.t; ++i) r[i-n] = this[i];
+ r.t = Math.max(this.t-n,0);
+ r.s = this.s;
+}
+
+// (protected) r = this << n
+function bnpLShiftTo(n,r) {
+ var bs = n%this.DB;
+ var cbs = this.DB-bs;
+ var bm = (1< >(p+=this.DB-8);
+ }
+ else {
+ d = (this[i]>>(p-=8))&0xff;
+ if(p <= 0) { p += this.DB; --i; }
+ }
+ if((d&0x80) != 0) d |= -256;
+ if(k == 0 && (this.s&0x80) != (d&0x80)) ++k;
+ if(k > 0 || d != this.s) r[k++] = d;
+ }
+ }
+ return r;
+}
+
+function bnEquals(a) { return(this.compareTo(a)==0); }
+function bnMin(a) { return(this.compareTo(a)<0)?this:a; }
+function bnMax(a) { return(this.compareTo(a)>0)?this:a; }
+
+// (protected) r = this op a (bitwise)
+function bnpBitwiseTo(a,op,r) {
+ var i, f, m = Math.min(a.t,this.t);
+ for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]);
+ if(a.t < this.t) {
+ f = a.s&this.DM;
+ for(i = m; i < this.t; ++i) r[i] = op(this[i],f);
+ r.t = this.t;
+ }
+ else {
+ f = this.s&this.DM;
+ for(i = m; i < a.t; ++i) r[i] = op(f,a[i]);
+ r.t = a.t;
+ }
+ r.s = op(this.s,a.s);
+ r.clamp();
+}
+
+// (public) this & a
+function op_and(x,y) { return x&y; }
+function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; }
+
+// (public) this | a
+function op_or(x,y) { return x|y; }
+function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; }
+
+// (public) this ^ a
+function op_xor(x,y) { return x^y; }
+function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; }
+
+// (public) this & ~a
+function op_andnot(x,y) { return x&~y; }
+function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; }
+
+// (public) ~this
+function bnNot() {
+ var r = nbi();
+ for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i];
+ r.t = this.t;
+ r.s = ~this.s;
+ return r;
+}
+
+// (public) this << n
+function bnShiftLeft(n) {
+ var r = nbi();
+ if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r);
+ return r;
+}
+
+// (public) this >> n
+function bnShiftRight(n) {
+ var r = nbi();
+ if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r);
+ return r;
+}
+
+// return index of lowest 1-bit in x, x < 2^31
+function lbit(x) {
+ if(x == 0) return -1;
+ var r = 0;
+ if((x&0xffff) == 0) { x >>= 16; r += 16; }
+ if((x&0xff) == 0) { x >>= 8; r += 8; }
+ if((x&0xf) == 0) { x >>= 4; r += 4; }
+ if((x&3) == 0) { x >>= 2; r += 2; }
+ if((x&1) == 0) ++r;
+ return r;
+}
+
+// (public) returns index of lowest 1-bit (or -1 if none)
+function bnGetLowestSetBit() {
+ for(var i = 0; i < this.t; ++i)
+ if(this[i] != 0) return i*this.DB+lbit(this[i]);
+ if(this.s < 0) return this.t*this.DB;
+ return -1;
+}
+
+// return number of 1 bits in x
+function cbit(x) {
+ var r = 0;
+ while(x != 0) { x &= x-1; ++r; }
+ return r;
+}
+
+// (public) return number of set bits
+function bnBitCount() {
+ var r = 0, x = this.s&this.DM;
+ for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x);
+ return r;
+}
+
+// (public) true iff nth bit is set
+function bnTestBit(n) {
+ var j = Math.floor(n/this.DB);
+ if(j >= this.t) return(this.s!=0);
+ return((this[j]&(1<<(n%this.DB)))!=0);
+}
+
+// (protected) this op (1<
+ * @name signString
+ * @memberOf RSAKey
+ * @function
+ * @param {String} s message string to be signed.
+ * @param {String} hashAlg hash algorithm name for signing.
+ * @return returns hexadecimal string of signature value.
+ */
+function _rsasign_signString(s, hashAlg) {
+ var hashFunc = function(s) { return KJUR.crypto.Util.hashString(s, hashAlg); };
+ var sHashHex = hashFunc(s);
+
+ return this.signWithMessageHash(sHashHex, hashAlg);
+}
+
+/**
+ * sign hash value of message to be signed with RSA private key.
+ * @name signWithMessageHash
+ * @memberOf RSAKey
+ * @function
+ * @param {String} sHashHex hexadecimal string of hash value of message to be signed.
+ * @param {String} hashAlg hash algorithm name for signing.
+ * @return returns hexadecimal string of signature value.
+ * @since rsasign 1.2.6
+ */
+function _rsasign_signWithMessageHash(sHashHex, hashAlg) {
+ var hPM = KJUR.crypto.Util.getPaddedDigestInfoHex(sHashHex, hashAlg, this.n.bitLength());
+ var biPaddedMessage = parseBigInt(hPM, 16);
+ var biSign = this.doPrivate(biPaddedMessage);
+ var hexSign = biSign.toString(16);
+ return _zeroPaddingOfSignature(hexSign, this.n.bitLength());
+}
+
+function _rsasign_signStringWithSHA1(s) {
+ return _rsasign_signString.call(this, s, 'sha1');
+}
+
+function _rsasign_signStringWithSHA256(s) {
+ return _rsasign_signString.call(this, s, 'sha256');
+}
+
+// PKCS#1 (PSS) mask generation function
+function pss_mgf1_str(seed, len, hash) {
+ var mask = '', i = 0;
+
+ while (mask.length < len) {
+ mask += hextorstr(hash(rstrtohex(seed + String.fromCharCode.apply(String, [
+ (i & 0xff000000) >> 24,
+ (i & 0x00ff0000) >> 16,
+ (i & 0x0000ff00) >> 8,
+ i & 0x000000ff]))));
+ i += 1;
+ }
+
+ return mask;
+}
+
+/**
+ * sign for a message string with RSA private key by PKCS#1 PSS signing.
+ * @name signStringPSS
+ * @memberOf RSAKey
+ * @function
+ * @param {String} s message string to be signed.
+ * @param {String} hashAlg hash algorithm name for signing.
+ * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
+ * There are two special values:
+ *
+ *
+ * DEFAULT is -1. (NOTE: OpenSSL's default is -2.)
+ * @return returns hexadecimal string of signature value.
+ */
+function _rsasign_signStringPSS(s, hashAlg, sLen) {
+ var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); }
+ var hHash = hashFunc(rstrtohex(s));
+
+ if (sLen === undefined) sLen = -1;
+ return this.signWithMessageHashPSS(hHash, hashAlg, sLen);
+}
+
+/**
+ * sign hash value of message with RSA private key by PKCS#1 PSS signing.
+ * @name signWithMessageHashPSS
+ * @memberOf RSAKey
+ * @function
+ * @param {String} hHash hexadecimal hash value of message to be signed.
+ * @param {String} hashAlg hash algorithm name for signing.
+ * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
+ * There are two special values:
+ *
+ *
+ * DEFAULT is -1. (NOTE: OpenSSL's default is -2.)
+ * @return returns hexadecimal string of signature value.
+ * @since rsasign 1.2.6
+ */
+function _rsasign_signWithMessageHashPSS(hHash, hashAlg, sLen) {
+ var mHash = hextorstr(hHash);
+ var hLen = mHash.length;
+ var emBits = this.n.bitLength() - 1;
+ var emLen = Math.ceil(emBits / 8);
+ var i;
+ var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); }
+
+ if (sLen === -1 || sLen === undefined) {
+ sLen = hLen; // same as hash length
+ } else if (sLen === -2) {
+ sLen = emLen - hLen - 2; // maximum
+ } else if (sLen < -2) {
+ throw "invalid salt length";
+ }
+
+ if (emLen < (hLen + sLen + 2)) {
+ throw "data too long";
+ }
+
+ var salt = '';
+
+ if (sLen > 0) {
+ salt = new Array(sLen);
+ new SecureRandom().nextBytes(salt);
+ salt = String.fromCharCode.apply(String, salt);
+ }
+
+ var H = hextorstr(hashFunc(rstrtohex('\x00\x00\x00\x00\x00\x00\x00\x00' + mHash + salt)));
+ var PS = [];
+
+ for (i = 0; i < emLen - sLen - hLen - 2; i += 1) {
+ PS[i] = 0x00;
+ }
+
+ var DB = String.fromCharCode.apply(String, PS) + '\x01' + salt;
+ var dbMask = pss_mgf1_str(H, DB.length, hashFunc);
+ var maskedDB = [];
+
+ for (i = 0; i < DB.length; i += 1) {
+ maskedDB[i] = DB.charCodeAt(i) ^ dbMask.charCodeAt(i);
+ }
+
+ var mask = (0xff00 >> (8 * emLen - emBits)) & 0xff;
+ maskedDB[0] &= ~mask;
+
+ for (i = 0; i < hLen; i++) {
+ maskedDB.push(H.charCodeAt(i));
+ }
+
+ maskedDB.push(0xbc);
+
+ return _zeroPaddingOfSignature(this.doPrivate(new BigInteger(maskedDB)).toString(16),
+ this.n.bitLength());
+}
+
+// ========================================================================
+// Signature Verification
+// ========================================================================
+
+function _rsasign_getDecryptSignatureBI(biSig, hN, hE) {
+ var rsa = new RSAKey();
+ rsa.setPublic(hN, hE);
+ var biDecryptedSig = rsa.doPublic(biSig);
+ return biDecryptedSig;
+}
+
+function _rsasign_getHexDigestInfoFromSig(biSig, hN, hE) {
+ var biDecryptedSig = _rsasign_getDecryptSignatureBI(biSig, hN, hE);
+ var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
+ return hDigestInfo;
+}
+
+function _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo) {
+ for (var algName in KJUR.crypto.Util.DIGESTINFOHEAD) {
+ var head = KJUR.crypto.Util.DIGESTINFOHEAD[algName];
+ var len = head.length;
+ if (hDigestInfo.substring(0, len) == head) {
+ var a = [algName, hDigestInfo.substring(len)];
+ return a;
+ }
+ }
+ return [];
+}
+
+function _rsasign_verifySignatureWithArgs(sMsg, biSig, hN, hE) {
+ var hDigestInfo = _rsasign_getHexDigestInfoFromSig(biSig, hN, hE);
+ var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
+ if (digestInfoAry.length == 0) return false;
+ var algName = digestInfoAry[0];
+ var diHashValue = digestInfoAry[1];
+ var ff = function(s) { return KJUR.crypto.Util.hashString(s, algName); };
+ var msgHashValue = ff(sMsg);
+ return (diHashValue == msgHashValue);
+}
+
+function _rsasign_verifyHexSignatureForMessage(hSig, sMsg) {
+ var biSig = parseBigInt(hSig, 16);
+ var result = _rsasign_verifySignatureWithArgs(sMsg, biSig,
+ this.n.toString(16),
+ this.e.toString(16));
+ return result;
+}
+
+/**
+ * verifies a sigature for a message string with RSA public key.
+ * @name verifyString
+ * @memberOf RSAKey#
+ * @function
+ * @param {String} sMsg message string to be verified.
+ * @param {String} hSig hexadecimal string of siganture.
+ * non-hexadecimal charactors including new lines will be ignored.
+ * @return returns 1 if valid, otherwise 0
+ */
+function _rsasign_verifyString(sMsg, hSig) {
+ hSig = hSig.replace(_RE_HEXDECONLY, '');
+ hSig = hSig.replace(/[ \n]+/g, "");
+ var biSig = parseBigInt(hSig, 16);
+ if (biSig.bitLength() > this.n.bitLength()) return 0;
+ var biDecryptedSig = this.doPublic(biSig);
+ var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
+ var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
+
+ if (digestInfoAry.length == 0) return false;
+ var algName = digestInfoAry[0];
+ var diHashValue = digestInfoAry[1];
+ var ff = function(s) { return KJUR.crypto.Util.hashString(s, algName); };
+ var msgHashValue = ff(sMsg);
+ return (diHashValue == msgHashValue);
+}
+
+/**
+ * verifies a sigature for a message string with RSA public key.
+ * @name verifyWithMessageHash
+ * @memberOf RSAKey
+ * @function
+ * @param {String} sHashHex hexadecimal hash value of message to be verified.
+ * @param {String} hSig hexadecimal string of siganture.
+ * non-hexadecimal charactors including new lines will be ignored.
+ * @return returns 1 if valid, otherwise 0
+ * @since rsasign 1.2.6
+ */
+function _rsasign_verifyWithMessageHash(sHashHex, hSig) {
+ hSig = hSig.replace(_RE_HEXDECONLY, '');
+ hSig = hSig.replace(/[ \n]+/g, "");
+ var biSig = parseBigInt(hSig, 16);
+ if (biSig.bitLength() > this.n.bitLength()) return 0;
+ var biDecryptedSig = this.doPublic(biSig);
+ var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
+ var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
+
+ if (digestInfoAry.length == 0) return false;
+ var algName = digestInfoAry[0];
+ var diHashValue = digestInfoAry[1];
+ return (diHashValue == sHashHex);
+}
+
+/**
+ * verifies a sigature for a message string with RSA public key by PKCS#1 PSS sign.
+ * @name verifyStringPSS
+ * @memberOf RSAKey
+ * @function
+ * @param {String} sMsg message string to be verified.
+ * @param {String} hSig hexadecimal string of signature value
+ * @param {String} hashAlg hash algorithm name
+ * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
+ * There are two special values:
+ *
+ *
+ * DEFAULT is -1. (NOTE: OpenSSL's default is -2.)
+ * @return returns true if valid, otherwise false
+ */
+function _rsasign_verifyStringPSS(sMsg, hSig, hashAlg, sLen) {
+ var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); };
+ var hHash = hashFunc(rstrtohex(sMsg));
+
+ if (sLen === undefined) sLen = -1;
+ return this.verifyWithMessageHashPSS(hHash, hSig, hashAlg, sLen);
+}
+
+/**
+ * verifies a sigature for a hash value of message string with RSA public key by PKCS#1 PSS sign.
+ * @name verifyWithMessageHashPSS
+ * @memberOf RSAKey
+ * @function
+ * @param {String} hHash hexadecimal hash value of message string to be verified.
+ * @param {String} hSig hexadecimal string of signature value
+ * @param {String} hashAlg hash algorithm name
+ * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
+ * There are two special values:
+ *
+ *
+ * DEFAULT is -1 (NOTE: OpenSSL's default is -2.)
+ * @return returns true if valid, otherwise false
+ * @since rsasign 1.2.6
+ */
+function _rsasign_verifyWithMessageHashPSS(hHash, hSig, hashAlg, sLen) {
+ var biSig = new BigInteger(hSig, 16);
+
+ if (biSig.bitLength() > this.n.bitLength()) {
+ return false;
+ }
+
+ var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); };
+ var mHash = hextorstr(hHash);
+ var hLen = mHash.length;
+ var emBits = this.n.bitLength() - 1;
+ var emLen = Math.ceil(emBits / 8);
+ var i;
+
+ if (sLen === -1 || sLen === undefined) {
+ sLen = hLen; // same as hash length
+ } else if (sLen === -2) {
+ sLen = emLen - hLen - 2; // recover
+ } else if (sLen < -2) {
+ throw "invalid salt length";
+ }
+
+ if (emLen < (hLen + sLen + 2)) {
+ throw "data too long";
+ }
+
+ var em = this.doPublic(biSig).toByteArray();
+
+ for (i = 0; i < em.length; i += 1) {
+ em[i] &= 0xff;
+ }
+
+ while (em.length < emLen) {
+ em.unshift(0);
+ }
+
+ if (em[emLen -1] !== 0xbc) {
+ throw "encoded message does not end in 0xbc";
+ }
+
+ em = String.fromCharCode.apply(String, em);
+
+ var maskedDB = em.substr(0, emLen - hLen - 1);
+ var H = em.substr(maskedDB.length, hLen);
+
+ var mask = (0xff00 >> (8 * emLen - emBits)) & 0xff;
+
+ if ((maskedDB.charCodeAt(0) & mask) !== 0) {
+ throw "bits beyond keysize not zero";
+ }
+
+ var dbMask = pss_mgf1_str(H, maskedDB.length, hashFunc);
+ var DB = [];
+
+ for (i = 0; i < maskedDB.length; i += 1) {
+ DB[i] = maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i);
+ }
+
+ DB[0] &= ~mask;
+
+ var checkLen = emLen - hLen - sLen - 2;
+
+ for (i = 0; i < checkLen; i += 1) {
+ if (DB[i] !== 0x00) {
+ throw "leftmost octets not zero";
+ }
+ }
+
+ if (DB[checkLen] !== 0x01) {
+ throw "0x01 marker not found";
+ }
+
+ return H === hextorstr(hashFunc(rstrtohex('\x00\x00\x00\x00\x00\x00\x00\x00' + mHash +
+ String.fromCharCode.apply(String, DB.slice(-sLen)))));
+}
+
+RSAKey.prototype.signWithMessageHash = _rsasign_signWithMessageHash;
+RSAKey.prototype.signString = _rsasign_signString;
+RSAKey.prototype.signStringWithSHA1 = _rsasign_signStringWithSHA1;
+RSAKey.prototype.signStringWithSHA256 = _rsasign_signStringWithSHA256;
+RSAKey.prototype.sign = _rsasign_signString;
+RSAKey.prototype.signWithSHA1 = _rsasign_signStringWithSHA1;
+RSAKey.prototype.signWithSHA256 = _rsasign_signStringWithSHA256;
+
+RSAKey.prototype.signWithMessageHashPSS = _rsasign_signWithMessageHashPSS;
+RSAKey.prototype.signStringPSS = _rsasign_signStringPSS;
+RSAKey.prototype.signPSS = _rsasign_signStringPSS;
+RSAKey.SALT_LEN_HLEN = -1;
+RSAKey.SALT_LEN_MAX = -2;
+
+RSAKey.prototype.verifyWithMessageHash = _rsasign_verifyWithMessageHash;
+RSAKey.prototype.verifyString = _rsasign_verifyString;
+RSAKey.prototype.verifyHexSignatureForMessage = _rsasign_verifyHexSignatureForMessage;
+RSAKey.prototype.verify = _rsasign_verifyString;
+RSAKey.prototype.verifyHexSignatureForByteArrayMessage = _rsasign_verifyHexSignatureForMessage;
+
+RSAKey.prototype.verifyWithMessageHashPSS = _rsasign_verifyWithMessageHashPSS;
+RSAKey.prototype.verifyStringPSS = _rsasign_verifyStringPSS;
+RSAKey.prototype.verifyPSS = _rsasign_verifyStringPSS;
+RSAKey.SALT_LEN_RECOVER = -2;
+
+/**
+ * @name RSAKey
+ * @class key of RSA public key algorithm
+ * @description Tom Wu's RSA Key class and extension
+ */
diff --git a/js/sha1.js b/js/sha1.js
new file mode 100644
index 0000000..01861f4
--- /dev/null
+++ b/js/sha1.js
@@ -0,0 +1,136 @@
+/*
+CryptoJS v3.1.2
+code.google.com/p/crypto-js
+(c) 2009-2013 by Jeff Mott. All rights reserved.
+code.google.com/p/crypto-js/wiki/License
+*/
+(function () {
+ // Shortcuts
+ var C = CryptoJS;
+ var C_lib = C.lib;
+ var WordArray = C_lib.WordArray;
+ var Hasher = C_lib.Hasher;
+ var C_algo = C.algo;
+
+ // Reusable object
+ var W = [];
+
+ /**
+ * SHA-1 hash algorithm.
+ */
+ var SHA1 = C_algo.SHA1 = Hasher.extend({
+ _doReset: function () {
+ this._hash = new WordArray.init([
+ 0x67452301, 0xefcdab89,
+ 0x98badcfe, 0x10325476,
+ 0xc3d2e1f0
+ ]);
+ },
+
+ _doProcessBlock: function (M, offset) {
+ // Shortcut
+ var H = this._hash.words;
+
+ // Working variables
+ var a = H[0];
+ var b = H[1];
+ var c = H[2];
+ var d = H[3];
+ var e = H[4];
+
+ // Computation
+ for (var i = 0; i < 80; i++) {
+ if (i < 16) {
+ W[i] = M[offset + i] | 0;
+ } else {
+ var n = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];
+ W[i] = (n << 1) | (n >>> 31);
+ }
+
+ var t = ((a << 5) | (a >>> 27)) + e + W[i];
+ if (i < 20) {
+ t += ((b & c) | (~b & d)) + 0x5a827999;
+ } else if (i < 40) {
+ t += (b ^ c ^ d) + 0x6ed9eba1;
+ } else if (i < 60) {
+ t += ((b & c) | (b & d) | (c & d)) - 0x70e44324;
+ } else /* if (i < 80) */ {
+ t += (b ^ c ^ d) - 0x359d3e2a;
+ }
+
+ e = d;
+ d = c;
+ c = (b << 30) | (b >>> 2);
+ b = a;
+ a = t;
+ }
+
+ // Intermediate hash value
+ H[0] = (H[0] + a) | 0;
+ H[1] = (H[1] + b) | 0;
+ H[2] = (H[2] + c) | 0;
+ H[3] = (H[3] + d) | 0;
+ H[4] = (H[4] + e) | 0;
+ },
+
+ _doFinalize: function () {
+ // Shortcuts
+ var data = this._data;
+ var dataWords = data.words;
+
+ var nBitsTotal = this._nDataBytes * 8;
+ var nBitsLeft = data.sigBytes * 8;
+
+ // Add padding
+ dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
+ dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);
+ dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal;
+ data.sigBytes = dataWords.length * 4;
+
+ // Hash final blocks
+ this._process();
+
+ // Return final computed hash
+ return this._hash;
+ },
+
+ clone: function () {
+ var clone = Hasher.clone.call(this);
+ clone._hash = this._hash.clone();
+
+ return clone;
+ }
+ });
+
+ /**
+ * Shortcut function to the hasher's object interface.
+ *
+ * @param {WordArray|string} message The message to hash.
+ *
+ * @return {WordArray} The hash.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var hash = CryptoJS.SHA1('message');
+ * var hash = CryptoJS.SHA1(wordArray);
+ */
+ C.SHA1 = Hasher._createHelper(SHA1);
+
+ /**
+ * Shortcut function to the HMAC's object interface.
+ *
+ * @param {WordArray|string} message The message to hash.
+ * @param {WordArray|string} key The secret key.
+ *
+ * @return {WordArray} The HMAC.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var hmac = CryptoJS.HmacSHA1(message, key);
+ */
+ C.HmacSHA1 = Hasher._createHmacHelper(SHA1);
+}());
diff --git a/js/sha256.js b/js/sha256.js
new file mode 100644
index 0000000..16cee0c
--- /dev/null
+++ b/js/sha256.js
@@ -0,0 +1,185 @@
+/*
+CryptoJS v3.1.2
+code.google.com/p/crypto-js
+(c) 2009-2013 by Jeff Mott. All rights reserved.
+code.google.com/p/crypto-js/wiki/License
+*/
+(function (Math) {
+ // Shortcuts
+ var C = CryptoJS;
+ var C_lib = C.lib;
+ var WordArray = C_lib.WordArray;
+ var Hasher = C_lib.Hasher;
+ var C_algo = C.algo;
+
+ // Initialization and round constants tables
+ var H = [];
+ var K = [];
+
+ // Compute constants
+ (function () {
+ function isPrime(n) {
+ var sqrtN = Math.sqrt(n);
+ for (var factor = 2; factor <= sqrtN; factor++) {
+ if (!(n % factor)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function getFractionalBits(n) {
+ return ((n - (n | 0)) * 0x100000000) | 0;
+ }
+
+ var n = 2;
+ var nPrime = 0;
+ while (nPrime < 64) {
+ if (isPrime(n)) {
+ if (nPrime < 8) {
+ H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2));
+ }
+ K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3));
+
+ nPrime++;
+ }
+
+ n++;
+ }
+ }());
+
+ // Reusable object
+ var W = [];
+
+ /**
+ * SHA-256 hash algorithm.
+ */
+ var SHA256 = C_algo.SHA256 = Hasher.extend({
+ _doReset: function () {
+ this._hash = new WordArray.init(H.slice(0));
+ },
+
+ _doProcessBlock: function (M, offset) {
+ // Shortcut
+ var H = this._hash.words;
+
+ // Working variables
+ var a = H[0];
+ var b = H[1];
+ var c = H[2];
+ var d = H[3];
+ var e = H[4];
+ var f = H[5];
+ var g = H[6];
+ var h = H[7];
+
+ // Computation
+ for (var i = 0; i < 64; i++) {
+ if (i < 16) {
+ W[i] = M[offset + i] | 0;
+ } else {
+ var gamma0x = W[i - 15];
+ var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^
+ ((gamma0x << 14) | (gamma0x >>> 18)) ^
+ (gamma0x >>> 3);
+
+ var gamma1x = W[i - 2];
+ var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^
+ ((gamma1x << 13) | (gamma1x >>> 19)) ^
+ (gamma1x >>> 10);
+
+ W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16];
+ }
+
+ var ch = (e & f) ^ (~e & g);
+ var maj = (a & b) ^ (a & c) ^ (b & c);
+
+ var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22));
+ var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25));
+
+ var t1 = h + sigma1 + ch + K[i] + W[i];
+ var t2 = sigma0 + maj;
+
+ h = g;
+ g = f;
+ f = e;
+ e = (d + t1) | 0;
+ d = c;
+ c = b;
+ b = a;
+ a = (t1 + t2) | 0;
+ }
+
+ // Intermediate hash value
+ H[0] = (H[0] + a) | 0;
+ H[1] = (H[1] + b) | 0;
+ H[2] = (H[2] + c) | 0;
+ H[3] = (H[3] + d) | 0;
+ H[4] = (H[4] + e) | 0;
+ H[5] = (H[5] + f) | 0;
+ H[6] = (H[6] + g) | 0;
+ H[7] = (H[7] + h) | 0;
+ },
+
+ _doFinalize: function () {
+ // Shortcuts
+ var data = this._data;
+ var dataWords = data.words;
+
+ var nBitsTotal = this._nDataBytes * 8;
+ var nBitsLeft = data.sigBytes * 8;
+
+ // Add padding
+ dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
+ dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);
+ dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal;
+ data.sigBytes = dataWords.length * 4;
+
+ // Hash final blocks
+ this._process();
+
+ // Return final computed hash
+ return this._hash;
+ },
+
+ clone: function () {
+ var clone = Hasher.clone.call(this);
+ clone._hash = this._hash.clone();
+
+ return clone;
+ }
+ });
+
+ /**
+ * Shortcut function to the hasher's object interface.
+ *
+ * @param {WordArray|string} message The message to hash.
+ *
+ * @return {WordArray} The hash.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var hash = CryptoJS.SHA256('message');
+ * var hash = CryptoJS.SHA256(wordArray);
+ */
+ C.SHA256 = Hasher._createHelper(SHA256);
+
+ /**
+ * Shortcut function to the HMAC's object interface.
+ *
+ * @param {WordArray|string} message The message to hash.
+ * @param {WordArray|string} key The secret key.
+ *
+ * @return {WordArray} The HMAC.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var hmac = CryptoJS.HmacSHA256(message, key);
+ */
+ C.HmacSHA256 = Hasher._createHmacHelper(SHA256);
+}(Math));
diff --git a/js/sha512.js b/js/sha512.js
new file mode 100644
index 0000000..4c6940c
--- /dev/null
+++ b/js/sha512.js
@@ -0,0 +1,309 @@
+/*
+CryptoJS v3.1.2
+code.google.com/p/crypto-js
+(c) 2009-2013 by Jeff Mott. All rights reserved.
+code.google.com/p/crypto-js/wiki/License
+*/
+(function () {
+ // Shortcuts
+ var C = CryptoJS;
+ var C_lib = C.lib;
+ var Hasher = C_lib.Hasher;
+ var C_x64 = C.x64;
+ var X64Word = C_x64.Word;
+ var X64WordArray = C_x64.WordArray;
+ var C_algo = C.algo;
+
+ function X64Word_create() {
+ return X64Word.create.apply(X64Word, arguments);
+ }
+
+ // Constants
+ var K = [
+ X64Word_create(0x428a2f98, 0xd728ae22), X64Word_create(0x71374491, 0x23ef65cd),
+ X64Word_create(0xb5c0fbcf, 0xec4d3b2f), X64Word_create(0xe9b5dba5, 0x8189dbbc),
+ X64Word_create(0x3956c25b, 0xf348b538), X64Word_create(0x59f111f1, 0xb605d019),
+ X64Word_create(0x923f82a4, 0xaf194f9b), X64Word_create(0xab1c5ed5, 0xda6d8118),
+ X64Word_create(0xd807aa98, 0xa3030242), X64Word_create(0x12835b01, 0x45706fbe),
+ X64Word_create(0x243185be, 0x4ee4b28c), X64Word_create(0x550c7dc3, 0xd5ffb4e2),
+ X64Word_create(0x72be5d74, 0xf27b896f), X64Word_create(0x80deb1fe, 0x3b1696b1),
+ X64Word_create(0x9bdc06a7, 0x25c71235), X64Word_create(0xc19bf174, 0xcf692694),
+ X64Word_create(0xe49b69c1, 0x9ef14ad2), X64Word_create(0xefbe4786, 0x384f25e3),
+ X64Word_create(0x0fc19dc6, 0x8b8cd5b5), X64Word_create(0x240ca1cc, 0x77ac9c65),
+ X64Word_create(0x2de92c6f, 0x592b0275), X64Word_create(0x4a7484aa, 0x6ea6e483),
+ X64Word_create(0x5cb0a9dc, 0xbd41fbd4), X64Word_create(0x76f988da, 0x831153b5),
+ X64Word_create(0x983e5152, 0xee66dfab), X64Word_create(0xa831c66d, 0x2db43210),
+ X64Word_create(0xb00327c8, 0x98fb213f), X64Word_create(0xbf597fc7, 0xbeef0ee4),
+ X64Word_create(0xc6e00bf3, 0x3da88fc2), X64Word_create(0xd5a79147, 0x930aa725),
+ X64Word_create(0x06ca6351, 0xe003826f), X64Word_create(0x14292967, 0x0a0e6e70),
+ X64Word_create(0x27b70a85, 0x46d22ffc), X64Word_create(0x2e1b2138, 0x5c26c926),
+ X64Word_create(0x4d2c6dfc, 0x5ac42aed), X64Word_create(0x53380d13, 0x9d95b3df),
+ X64Word_create(0x650a7354, 0x8baf63de), X64Word_create(0x766a0abb, 0x3c77b2a8),
+ X64Word_create(0x81c2c92e, 0x47edaee6), X64Word_create(0x92722c85, 0x1482353b),
+ X64Word_create(0xa2bfe8a1, 0x4cf10364), X64Word_create(0xa81a664b, 0xbc423001),
+ X64Word_create(0xc24b8b70, 0xd0f89791), X64Word_create(0xc76c51a3, 0x0654be30),
+ X64Word_create(0xd192e819, 0xd6ef5218), X64Word_create(0xd6990624, 0x5565a910),
+ X64Word_create(0xf40e3585, 0x5771202a), X64Word_create(0x106aa070, 0x32bbd1b8),
+ X64Word_create(0x19a4c116, 0xb8d2d0c8), X64Word_create(0x1e376c08, 0x5141ab53),
+ X64Word_create(0x2748774c, 0xdf8eeb99), X64Word_create(0x34b0bcb5, 0xe19b48a8),
+ X64Word_create(0x391c0cb3, 0xc5c95a63), X64Word_create(0x4ed8aa4a, 0xe3418acb),
+ X64Word_create(0x5b9cca4f, 0x7763e373), X64Word_create(0x682e6ff3, 0xd6b2b8a3),
+ X64Word_create(0x748f82ee, 0x5defb2fc), X64Word_create(0x78a5636f, 0x43172f60),
+ X64Word_create(0x84c87814, 0xa1f0ab72), X64Word_create(0x8cc70208, 0x1a6439ec),
+ X64Word_create(0x90befffa, 0x23631e28), X64Word_create(0xa4506ceb, 0xde82bde9),
+ X64Word_create(0xbef9a3f7, 0xb2c67915), X64Word_create(0xc67178f2, 0xe372532b),
+ X64Word_create(0xca273ece, 0xea26619c), X64Word_create(0xd186b8c7, 0x21c0c207),
+ X64Word_create(0xeada7dd6, 0xcde0eb1e), X64Word_create(0xf57d4f7f, 0xee6ed178),
+ X64Word_create(0x06f067aa, 0x72176fba), X64Word_create(0x0a637dc5, 0xa2c898a6),
+ X64Word_create(0x113f9804, 0xbef90dae), X64Word_create(0x1b710b35, 0x131c471b),
+ X64Word_create(0x28db77f5, 0x23047d84), X64Word_create(0x32caab7b, 0x40c72493),
+ X64Word_create(0x3c9ebe0a, 0x15c9bebc), X64Word_create(0x431d67c4, 0x9c100d4c),
+ X64Word_create(0x4cc5d4be, 0xcb3e42b6), X64Word_create(0x597f299c, 0xfc657e2a),
+ X64Word_create(0x5fcb6fab, 0x3ad6faec), X64Word_create(0x6c44198c, 0x4a475817)
+ ];
+
+ // Reusable objects
+ var W = [];
+ (function () {
+ for (var i = 0; i < 80; i++) {
+ W[i] = X64Word_create();
+ }
+ }());
+
+ /**
+ * SHA-512 hash algorithm.
+ */
+ var SHA512 = C_algo.SHA512 = Hasher.extend({
+ _doReset: function () {
+ this._hash = new X64WordArray.init([
+ new X64Word.init(0x6a09e667, 0xf3bcc908), new X64Word.init(0xbb67ae85, 0x84caa73b),
+ new X64Word.init(0x3c6ef372, 0xfe94f82b), new X64Word.init(0xa54ff53a, 0x5f1d36f1),
+ new X64Word.init(0x510e527f, 0xade682d1), new X64Word.init(0x9b05688c, 0x2b3e6c1f),
+ new X64Word.init(0x1f83d9ab, 0xfb41bd6b), new X64Word.init(0x5be0cd19, 0x137e2179)
+ ]);
+ },
+
+ _doProcessBlock: function (M, offset) {
+ // Shortcuts
+ var H = this._hash.words;
+
+ var H0 = H[0];
+ var H1 = H[1];
+ var H2 = H[2];
+ var H3 = H[3];
+ var H4 = H[4];
+ var H5 = H[5];
+ var H6 = H[6];
+ var H7 = H[7];
+
+ var H0h = H0.high;
+ var H0l = H0.low;
+ var H1h = H1.high;
+ var H1l = H1.low;
+ var H2h = H2.high;
+ var H2l = H2.low;
+ var H3h = H3.high;
+ var H3l = H3.low;
+ var H4h = H4.high;
+ var H4l = H4.low;
+ var H5h = H5.high;
+ var H5l = H5.low;
+ var H6h = H6.high;
+ var H6l = H6.low;
+ var H7h = H7.high;
+ var H7l = H7.low;
+
+ // Working variables
+ var ah = H0h;
+ var al = H0l;
+ var bh = H1h;
+ var bl = H1l;
+ var ch = H2h;
+ var cl = H2l;
+ var dh = H3h;
+ var dl = H3l;
+ var eh = H4h;
+ var el = H4l;
+ var fh = H5h;
+ var fl = H5l;
+ var gh = H6h;
+ var gl = H6l;
+ var hh = H7h;
+ var hl = H7l;
+
+ // Rounds
+ for (var i = 0; i < 80; i++) {
+ // Shortcut
+ var Wi = W[i];
+
+ // Extend message
+ if (i < 16) {
+ var Wih = Wi.high = M[offset + i * 2] | 0;
+ var Wil = Wi.low = M[offset + i * 2 + 1] | 0;
+ } else {
+ // Gamma0
+ var gamma0x = W[i - 15];
+ var gamma0xh = gamma0x.high;
+ var gamma0xl = gamma0x.low;
+ var gamma0h = ((gamma0xh >>> 1) | (gamma0xl << 31)) ^ ((gamma0xh >>> 8) | (gamma0xl << 24)) ^ (gamma0xh >>> 7);
+ var gamma0l = ((gamma0xl >>> 1) | (gamma0xh << 31)) ^ ((gamma0xl >>> 8) | (gamma0xh << 24)) ^ ((gamma0xl >>> 7) | (gamma0xh << 25));
+
+ // Gamma1
+ var gamma1x = W[i - 2];
+ var gamma1xh = gamma1x.high;
+ var gamma1xl = gamma1x.low;
+ var gamma1h = ((gamma1xh >>> 19) | (gamma1xl << 13)) ^ ((gamma1xh << 3) | (gamma1xl >>> 29)) ^ (gamma1xh >>> 6);
+ var gamma1l = ((gamma1xl >>> 19) | (gamma1xh << 13)) ^ ((gamma1xl << 3) | (gamma1xh >>> 29)) ^ ((gamma1xl >>> 6) | (gamma1xh << 26));
+
+ // W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16]
+ var Wi7 = W[i - 7];
+ var Wi7h = Wi7.high;
+ var Wi7l = Wi7.low;
+
+ var Wi16 = W[i - 16];
+ var Wi16h = Wi16.high;
+ var Wi16l = Wi16.low;
+
+ var Wil = gamma0l + Wi7l;
+ var Wih = gamma0h + Wi7h + ((Wil >>> 0) < (gamma0l >>> 0) ? 1 : 0);
+ var Wil = Wil + gamma1l;
+ var Wih = Wih + gamma1h + ((Wil >>> 0) < (gamma1l >>> 0) ? 1 : 0);
+ var Wil = Wil + Wi16l;
+ var Wih = Wih + Wi16h + ((Wil >>> 0) < (Wi16l >>> 0) ? 1 : 0);
+
+ Wi.high = Wih;
+ Wi.low = Wil;
+ }
+
+ var chh = (eh & fh) ^ (~eh & gh);
+ var chl = (el & fl) ^ (~el & gl);
+ var majh = (ah & bh) ^ (ah & ch) ^ (bh & ch);
+ var majl = (al & bl) ^ (al & cl) ^ (bl & cl);
+
+ var sigma0h = ((ah >>> 28) | (al << 4)) ^ ((ah << 30) | (al >>> 2)) ^ ((ah << 25) | (al >>> 7));
+ var sigma0l = ((al >>> 28) | (ah << 4)) ^ ((al << 30) | (ah >>> 2)) ^ ((al << 25) | (ah >>> 7));
+ var sigma1h = ((eh >>> 14) | (el << 18)) ^ ((eh >>> 18) | (el << 14)) ^ ((eh << 23) | (el >>> 9));
+ var sigma1l = ((el >>> 14) | (eh << 18)) ^ ((el >>> 18) | (eh << 14)) ^ ((el << 23) | (eh >>> 9));
+
+ // t1 = h + sigma1 + ch + K[i] + W[i]
+ var Ki = K[i];
+ var Kih = Ki.high;
+ var Kil = Ki.low;
+
+ var t1l = hl + sigma1l;
+ var t1h = hh + sigma1h + ((t1l >>> 0) < (hl >>> 0) ? 1 : 0);
+ var t1l = t1l + chl;
+ var t1h = t1h + chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0);
+ var t1l = t1l + Kil;
+ var t1h = t1h + Kih + ((t1l >>> 0) < (Kil >>> 0) ? 1 : 0);
+ var t1l = t1l + Wil;
+ var t1h = t1h + Wih + ((t1l >>> 0) < (Wil >>> 0) ? 1 : 0);
+
+ // t2 = sigma0 + maj
+ var t2l = sigma0l + majl;
+ var t2h = sigma0h + majh + ((t2l >>> 0) < (sigma0l >>> 0) ? 1 : 0);
+
+ // Update working variables
+ hh = gh;
+ hl = gl;
+ gh = fh;
+ gl = fl;
+ fh = eh;
+ fl = el;
+ el = (dl + t1l) | 0;
+ eh = (dh + t1h + ((el >>> 0) < (dl >>> 0) ? 1 : 0)) | 0;
+ dh = ch;
+ dl = cl;
+ ch = bh;
+ cl = bl;
+ bh = ah;
+ bl = al;
+ al = (t1l + t2l) | 0;
+ ah = (t1h + t2h + ((al >>> 0) < (t1l >>> 0) ? 1 : 0)) | 0;
+ }
+
+ // Intermediate hash value
+ H0l = H0.low = (H0l + al);
+ H0.high = (H0h + ah + ((H0l >>> 0) < (al >>> 0) ? 1 : 0));
+ H1l = H1.low = (H1l + bl);
+ H1.high = (H1h + bh + ((H1l >>> 0) < (bl >>> 0) ? 1 : 0));
+ H2l = H2.low = (H2l + cl);
+ H2.high = (H2h + ch + ((H2l >>> 0) < (cl >>> 0) ? 1 : 0));
+ H3l = H3.low = (H3l + dl);
+ H3.high = (H3h + dh + ((H3l >>> 0) < (dl >>> 0) ? 1 : 0));
+ H4l = H4.low = (H4l + el);
+ H4.high = (H4h + eh + ((H4l >>> 0) < (el >>> 0) ? 1 : 0));
+ H5l = H5.low = (H5l + fl);
+ H5.high = (H5h + fh + ((H5l >>> 0) < (fl >>> 0) ? 1 : 0));
+ H6l = H6.low = (H6l + gl);
+ H6.high = (H6h + gh + ((H6l >>> 0) < (gl >>> 0) ? 1 : 0));
+ H7l = H7.low = (H7l + hl);
+ H7.high = (H7h + hh + ((H7l >>> 0) < (hl >>> 0) ? 1 : 0));
+ },
+
+ _doFinalize: function () {
+ // Shortcuts
+ var data = this._data;
+ var dataWords = data.words;
+
+ var nBitsTotal = this._nDataBytes * 8;
+ var nBitsLeft = data.sigBytes * 8;
+
+ // Add padding
+ dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
+ dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 30] = Math.floor(nBitsTotal / 0x100000000);
+ dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 31] = nBitsTotal;
+ data.sigBytes = dataWords.length * 4;
+
+ // Hash final blocks
+ this._process();
+
+ // Convert hash to 32-bit word array before returning
+ var hash = this._hash.toX32();
+
+ // Return final computed hash
+ return hash;
+ },
+
+ clone: function () {
+ var clone = Hasher.clone.call(this);
+ clone._hash = this._hash.clone();
+
+ return clone;
+ },
+
+ blockSize: 1024/32
+ });
+
+ /**
+ * Shortcut function to the hasher's object interface.
+ *
+ * @param {WordArray|string} message The message to hash.
+ *
+ * @return {WordArray} The hash.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var hash = CryptoJS.SHA512('message');
+ * var hash = CryptoJS.SHA512(wordArray);
+ */
+ C.SHA512 = Hasher._createHelper(SHA512);
+
+ /**
+ * Shortcut function to the HMAC's object interface.
+ *
+ * @param {WordArray|string} message The message to hash.
+ * @param {WordArray|string} key The secret key.
+ *
+ * @return {WordArray} The HMAC.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var hmac = CryptoJS.HmacSHA512(message, key);
+ */
+ C.HmacSHA512 = Hasher._createHmacHelper(SHA512);
+}());
diff --git a/js/x509-1.1.js b/js/x509-1.1.js
new file mode 100644
index 0000000..783873c
--- /dev/null
+++ b/js/x509-1.1.js
@@ -0,0 +1,371 @@
+/*! x509-1.1.2.js (c) 2012 Kenji Urushima | kjur.github.com/jsrsasign/license
+ */
+/*
+ * x509.js - X509 class to read subject public key from certificate.
+ *
+ * Copyright (c) 2010-2013 Kenji Urushima (kenji.urushima@gmail.com)
+ *
+ * This software is licensed under the terms of the MIT License.
+ * http://kjur.github.com/jsrsasign/license
+ *
+ * The above copyright and license notice shall be
+ * included in all copies or substantial portions of the Software.
+ */
+
+/**
+ * @fileOverview
+ * @name x509-1.1.js
+ * @author Kenji Urushima kenji.urushima@gmail.com
+ * @version x509 1.1.2 (2013-Oct-06)
+ * @since jsrsasign 1.x.x
+ * @license MIT License
+ */
+
+/*
+ * Depends:
+ * base64.js
+ * rsa.js
+ * asn1hex.js
+ */
+
+/**
+ * X.509 certificate class.
+ * @class X.509 certificate class
+ * @property {RSAKey} subjectPublicKeyRSA Tom Wu's RSAKey object
+ * @property {String} subjectPublicKeyRSA_hN hexadecimal string for modulus of RSA public key
+ * @property {String} subjectPublicKeyRSA_hE hexadecimal string for public exponent of RSA public key
+ * @property {String} hex hexacedimal string for X.509 certificate.
+ * @author Kenji Urushima
+ * @version 1.0.1 (08 May 2012)
+ * @see 'jwrsasign'(RSA Sign JavaScript Library) home page http://kjur.github.com/jsrsasign/
+ */
+function X509() {
+ this.subjectPublicKeyRSA = null;
+ this.subjectPublicKeyRSA_hN = null;
+ this.subjectPublicKeyRSA_hE = null;
+ this.hex = null;
+
+ // ===== get basic fields from hex =====================================
+
+ /**
+ * get hexadecimal string of serialNumber field of certificate.
+ * @name getSerialNumberHex
+ * @memberOf X509#
+ * @function
+ */
+ this.getSerialNumberHex = function() {
+ return ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 1]);
+ };
+
+ /**
+ * get hexadecimal string of issuer field of certificate.
+ * @name getIssuerHex
+ * @memberOf X509#
+ * @function
+ */
+ this.getIssuerHex = function() {
+ return ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 3]);
+ };
+
+ /**
+ * get string of issuer field of certificate.
+ * @name getIssuerString
+ * @memberOf X509#
+ * @function
+ */
+ this.getIssuerString = function() {
+ return X509.hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 3]));
+ };
+
+ /**
+ * get hexadecimal string of subject field of certificate.
+ * @name getSubjectHex
+ * @memberOf X509#
+ * @function
+ */
+ this.getSubjectHex = function() {
+ return ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 5]);
+ };
+
+ /**
+ * get string of subject field of certificate.
+ * @name getSubjectString
+ * @memberOf X509#
+ * @function
+ */
+ this.getSubjectString = function() {
+ return X509.hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 5]));
+ };
+
+ /**
+ * get notBefore field string of certificate.
+ * @name getNotBefore
+ * @memberOf X509#
+ * @function
+ */
+ this.getNotBefore = function() {
+ var s = ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 4, 0]);
+ s = s.replace(/(..)/g, "%$1");
+ s = decodeURIComponent(s);
+ return s;
+ };
+
+ /**
+ * get notAfter field string of certificate.
+ * @name getNotAfter
+ * @memberOf X509#
+ * @function
+ */
+ this.getNotAfter = function() {
+ var s = ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 4, 1]);
+ s = s.replace(/(..)/g, "%$1");
+ s = decodeURIComponent(s);
+ return s;
+ };
+
+ // ===== read certificate public key ==========================
+
+ // ===== read certificate =====================================
+ /**
+ * read PEM formatted X.509 certificate from string.
+ * @name readCertPEM
+ * @memberOf X509#
+ * @function
+ * @param {String} sCertPEM string for PEM formatted X.509 certificate
+ */
+ this.readCertPEM = function(sCertPEM) {
+ var hCert = X509.pemToHex(sCertPEM);
+ var a = X509.getPublicKeyHexArrayFromCertHex(hCert);
+ var rsa = new RSAKey();
+ rsa.setPublic(a[0], a[1]);
+ this.subjectPublicKeyRSA = rsa;
+ this.subjectPublicKeyRSA_hN = a[0];
+ this.subjectPublicKeyRSA_hE = a[1];
+ this.hex = hCert;
+ };
+
+ this.readCertPEMWithoutRSAInit = function(sCertPEM) {
+ var hCert = X509.pemToHex(sCertPEM);
+ var a = X509.getPublicKeyHexArrayFromCertHex(hCert);
+ this.subjectPublicKeyRSA.setPublic(a[0], a[1]);
+ this.subjectPublicKeyRSA_hN = a[0];
+ this.subjectPublicKeyRSA_hE = a[1];
+ this.hex = hCert;
+ };
+};
+
+X509.pemToBase64 = function(sCertPEM) {
+ var s = sCertPEM;
+ s = s.replace("-----BEGIN CERTIFICATE-----", "");
+ s = s.replace("-----END CERTIFICATE-----", "");
+ s = s.replace(/[ \n]+/g, "");
+ return s;
+};
+
+X509.pemToHex = function(sCertPEM) {
+ var b64Cert = X509.pemToBase64(sCertPEM);
+ var hCert = b64tohex(b64Cert);
+ return hCert;
+};
+
+// NOTE: Without BITSTRING encapsulation.
+X509.getSubjectPublicKeyPosFromCertHex = function(hCert) {
+ var pInfo = X509.getSubjectPublicKeyInfoPosFromCertHex(hCert);
+ if (pInfo == -1) return -1;
+ var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pInfo);
+ if (a.length != 2) return -1;
+ var pBitString = a[1];
+ if (hCert.substring(pBitString, pBitString + 2) != '03') return -1;
+ var pBitStringV = ASN1HEX.getStartPosOfV_AtObj(hCert, pBitString);
+
+ if (hCert.substring(pBitStringV, pBitStringV + 2) != '00') return -1;
+ return pBitStringV + 2;
+};
+
+// NOTE: privateKeyUsagePeriod field of X509v2 not supported.
+// NOTE: v1 and v3 supported
+X509.getSubjectPublicKeyInfoPosFromCertHex = function(hCert) {
+ var pTbsCert = ASN1HEX.getStartPosOfV_AtObj(hCert, 0);
+ var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pTbsCert);
+ if (a.length < 1) return -1;
+ if (hCert.substring(a[0], a[0] + 10) == "a003020102") { // v3
+ if (a.length < 6) return -1;
+ return a[6];
+ } else {
+ if (a.length < 5) return -1;
+ return a[5];
+ }
+};
+
+X509.getPublicKeyHexArrayFromCertHex = function(hCert) {
+ var p = X509.getSubjectPublicKeyPosFromCertHex(hCert);
+ var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, p);
+ if (a.length != 2) return [];
+ var hN = ASN1HEX.getHexOfV_AtObj(hCert, a[0]);
+ var hE = ASN1HEX.getHexOfV_AtObj(hCert, a[1]);
+ if (hN != null && hE != null) {
+ return [hN, hE];
+ } else {
+ return [];
+ }
+};
+
+X509.getHexTbsCertificateFromCert = function(hCert) {
+ var pTbsCert = ASN1HEX.getStartPosOfV_AtObj(hCert, 0);
+ return pTbsCert;
+};
+
+X509.getPublicKeyHexArrayFromCertPEM = function(sCertPEM) {
+ var hCert = X509.pemToHex(sCertPEM);
+ var a = X509.getPublicKeyHexArrayFromCertHex(hCert);
+ return a;
+};
+
+X509.hex2dn = function(hDN) {
+ var s = "";
+ var a = ASN1HEX.getPosArrayOfChildren_AtObj(hDN, 0);
+ for (var i = 0; i < a.length; i++) {
+ var hRDN = ASN1HEX.getHexOfTLV_AtObj(hDN, a[i]);
+ s = s + "/" + X509.hex2rdn(hRDN);
+ }
+ return s;
+};
+
+X509.hex2rdn = function(hRDN) {
+ var hType = ASN1HEX.getDecendantHexTLVByNthList(hRDN, 0, [0, 0]);
+ var hValue = ASN1HEX.getDecendantHexVByNthList(hRDN, 0, [0, 1]);
+ var type = "";
+ try { type = X509.DN_ATTRHEX[hType]; } catch (ex) { type = hType; }
+ hValue = hValue.replace(/(..)/g, "%$1");
+ var value = decodeURIComponent(hValue);
+ return type + "=" + value;
+};
+
+X509.DN_ATTRHEX = {
+ "0603550406": "C",
+ "060355040a": "O",
+ "060355040b": "OU",
+ "0603550403": "CN",
+ "0603550405": "SN",
+ "0603550408": "ST",
+ "0603550407": "L"
+};
+
+/**
+ * get RSAKey/ECDSA public key object from PEM certificate string
+ * @name getPublicKeyFromCertPEM
+ * @memberOf X509
+ * @function
+ * @param {String} sCertPEM PEM formatted RSA/ECDSA/DSA X.509 certificate
+ * @return returns RSAKey/KJUR.crypto.{ECDSA,DSA} object of public key
+ * @since x509 1.1.1
+ * @description
+ * NOTE: DSA is also supported since x509 1.1.2.
+ */
+X509.getPublicKeyFromCertPEM = function(sCertPEM) {
+ var info = X509.getPublicKeyInfoPropOfCertPEM(sCertPEM);
+
+ if (info.algoid == "2a864886f70d010101") { // RSA
+ var aRSA = KEYUTIL.parsePublicRawRSAKeyHex(info.keyhex);
+ var key = new RSAKey();
+ key.setPublic(aRSA.n, aRSA.e);
+ return key;
+ } else if (info.algoid == "2a8648ce3d0201") { // ECC
+ var curveName = KJUR.crypto.OID.oidhex2name[info.algparam];
+ var key = new KJUR.crypto.ECDSA({'curve': curveName, 'info': info.keyhex});
+ key.setPublicKeyHex(info.keyhex);
+ return key;
+ } else if (info.algoid == "2a8648ce380401") { // DSA 1.2.840.10040.4.1
+ var p = ASN1HEX.getVbyList(info.algparam, 0, [0], "02");
+ var q = ASN1HEX.getVbyList(info.algparam, 0, [1], "02");
+ var g = ASN1HEX.getVbyList(info.algparam, 0, [2], "02");
+ var y = ASN1HEX.getHexOfV_AtObj(info.keyhex, 0);
+ y = y.substr(2);
+ var key = new KJUR.crypto.DSA();
+ key.setPublic(new BigInteger(p, 16),
+ new BigInteger(q, 16),
+ new BigInteger(g, 16),
+ new BigInteger(y, 16));
+ return key;
+ } else {
+ throw "unsupported key";
+ }
+};
+
+/**
+ * get public key information from PEM certificate
+ * @name getPublicKeyInfoPropOfCertPEM
+ * @memberOf X509
+ * @function
+ * @param {String} sCertPEM string of PEM formatted certificate
+ * @return {Hash} hash of information for public key
+ * @since x509 1.1.1
+ * @description
+ * Resulted associative array has following properties:
+ *
+ *
+ * @since x509 1.1.1
+ */
+X509.getPublicKeyInfoPropOfCertPEM = function(sCertPEM) {
+ var result = {};
+ result.algparam = null;
+ var hCert = X509.pemToHex(sCertPEM);
+
+ // 1. Certificate ASN.1
+ var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, 0);
+ if (a1.length != 3)
+ throw "malformed X.509 certificate PEM (code:001)"; // not 3 item of seq Cert
+
+ // 2. tbsCertificate
+ if (hCert.substr(a1[0], 2) != "30")
+ throw "malformed X.509 certificate PEM (code:002)"; // tbsCert not seq
+
+ var a2 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a1[0]);
+
+ // 3. subjectPublicKeyInfo
+ if (a2.length < 7)
+ throw "malformed X.509 certificate PEM (code:003)"; // no subjPubKeyInfo
+
+ var a3 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a2[6]);
+
+ if (a3.length != 2)
+ throw "malformed X.509 certificate PEM (code:004)"; // not AlgId and PubKey
+
+ // 4. AlgId
+ var a4 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a3[0]);
+
+ if (a4.length != 2)
+ throw "malformed X.509 certificate PEM (code:005)"; // not 2 item in AlgId
+
+ result.algoid = ASN1HEX.getHexOfV_AtObj(hCert, a4[0]);
+
+ if (hCert.substr(a4[1], 2) == "06") { // EC
+ result.algparam = ASN1HEX.getHexOfV_AtObj(hCert, a4[1]);
+ } else if (hCert.substr(a4[1], 2) == "30") { // DSA
+ result.algparam = ASN1HEX.getHexOfTLV_AtObj(hCert, a4[1]);
+ }
+
+ // 5. Public Key Hex
+ if (hCert.substr(a3[1], 02) != "03")
+ throw "malformed X.509 certificate PEM (code:006)"; // not bitstring
+
+ var unusedBitAndKeyHex = ASN1HEX.getHexOfV_AtObj(hCert, a3[1]);
+ result.keyhex = unusedBitAndKeyHex.substr(2);
+
+ return result;
+};
+
+/*
+X509.prototype.readCertPEM = _x509_readCertPEM;
+X509.prototype.readCertPEMWithoutRSAInit = _x509_readCertPEMWithoutRSAInit;
+X509.prototype.getSerialNumberHex = _x509_getSerialNumberHex;
+X509.prototype.getIssuerHex = _x509_getIssuerHex;
+X509.prototype.getSubjectHex = _x509_getSubjectHex;
+X509.prototype.getIssuerString = _x509_getIssuerString;
+X509.prototype.getSubjectString = _x509_getSubjectString;
+X509.prototype.getNotBefore = _x509_getNotBefore;
+X509.prototype.getNotAfter = _x509_getNotAfter;
+*/
diff --git a/js/x64-core.js b/js/x64-core.js
new file mode 100644
index 0000000..7a5be33
--- /dev/null
+++ b/js/x64-core.js
@@ -0,0 +1,290 @@
+/*
+CryptoJS v3.1.2
+code.google.com/p/crypto-js
+(c) 2009-2013 by Jeff Mott. All rights reserved.
+code.google.com/p/crypto-js/wiki/License
+*/
+(function (undefined) {
+ // Shortcuts
+ var C = CryptoJS;
+ var C_lib = C.lib;
+ var Base = C_lib.Base;
+ var X32WordArray = C_lib.WordArray;
+
+ /**
+ * x64 namespace.
+ */
+ var C_x64 = C.x64 = {};
+
+ /**
+ * A 64-bit word.
+ */
+ var X64Word = C_x64.Word = Base.extend({
+ /**
+ * Initializes a newly created 64-bit word.
+ *
+ * @param {number} high The high 32 bits.
+ * @param {number} low The low 32 bits.
+ *
+ * @example
+ *
+ * var x64Word = CryptoJS.x64.Word.create(0x00010203, 0x04050607);
+ */
+ init: function (high, low) {
+ this.high = high;
+ this.low = low;
+ }
+
+ /**
+ * Bitwise NOTs this word.
+ *
+ * @return {X64Word} A new x64-Word object after negating.
+ *
+ * @example
+ *
+ * var negated = x64Word.not();
+ */
+ // not: function () {
+ // var high = ~this.high;
+ // var low = ~this.low;
+
+ // return X64Word.create(high, low);
+ // },
+
+ /**
+ * Bitwise ANDs this word with the passed word.
+ *
+ * @param {X64Word} word The x64-Word to AND with this word.
+ *
+ * @return {X64Word} A new x64-Word object after ANDing.
+ *
+ * @example
+ *
+ * var anded = x64Word.and(anotherX64Word);
+ */
+ // and: function (word) {
+ // var high = this.high & word.high;
+ // var low = this.low & word.low;
+
+ // return X64Word.create(high, low);
+ // },
+
+ /**
+ * Bitwise ORs this word with the passed word.
+ *
+ * @param {X64Word} word The x64-Word to OR with this word.
+ *
+ * @return {X64Word} A new x64-Word object after ORing.
+ *
+ * @example
+ *
+ * var ored = x64Word.or(anotherX64Word);
+ */
+ // or: function (word) {
+ // var high = this.high | word.high;
+ // var low = this.low | word.low;
+
+ // return X64Word.create(high, low);
+ // },
+
+ /**
+ * Bitwise XORs this word with the passed word.
+ *
+ * @param {X64Word} word The x64-Word to XOR with this word.
+ *
+ * @return {X64Word} A new x64-Word object after XORing.
+ *
+ * @example
+ *
+ * var xored = x64Word.xor(anotherX64Word);
+ */
+ // xor: function (word) {
+ // var high = this.high ^ word.high;
+ // var low = this.low ^ word.low;
+
+ // return X64Word.create(high, low);
+ // },
+
+ /**
+ * Shifts this word n bits to the left.
+ *
+ * @param {number} n The number of bits to shift.
+ *
+ * @return {X64Word} A new x64-Word object after shifting.
+ *
+ * @example
+ *
+ * var shifted = x64Word.shiftL(25);
+ */
+ // shiftL: function (n) {
+ // if (n < 32) {
+ // var high = (this.high << n) | (this.low >>> (32 - n));
+ // var low = this.low << n;
+ // } else {
+ // var high = this.low << (n - 32);
+ // var low = 0;
+ // }
+
+ // return X64Word.create(high, low);
+ // },
+
+ /**
+ * Shifts this word n bits to the right.
+ *
+ * @param {number} n The number of bits to shift.
+ *
+ * @return {X64Word} A new x64-Word object after shifting.
+ *
+ * @example
+ *
+ * var shifted = x64Word.shiftR(7);
+ */
+ // shiftR: function (n) {
+ // if (n < 32) {
+ // var low = (this.low >>> n) | (this.high << (32 - n));
+ // var high = this.high >>> n;
+ // } else {
+ // var low = this.high >>> (n - 32);
+ // var high = 0;
+ // }
+
+ // return X64Word.create(high, low);
+ // },
+
+ /**
+ * Rotates this word n bits to the left.
+ *
+ * @param {number} n The number of bits to rotate.
+ *
+ * @return {X64Word} A new x64-Word object after rotating.
+ *
+ * @example
+ *
+ * var rotated = x64Word.rotL(25);
+ */
+ // rotL: function (n) {
+ // return this.shiftL(n).or(this.shiftR(64 - n));
+ // },
+
+ /**
+ * Rotates this word n bits to the right.
+ *
+ * @param {number} n The number of bits to rotate.
+ *
+ * @return {X64Word} A new x64-Word object after rotating.
+ *
+ * @example
+ *
+ * var rotated = x64Word.rotR(7);
+ */
+ // rotR: function (n) {
+ // return this.shiftR(n).or(this.shiftL(64 - n));
+ // },
+
+ /**
+ * Adds this word with the passed word.
+ *
+ * @param {X64Word} word The x64-Word to add with this word.
+ *
+ * @return {X64Word} A new x64-Word object after adding.
+ *
+ * @example
+ *
+ * var added = x64Word.add(anotherX64Word);
+ */
+ // add: function (word) {
+ // var low = (this.low + word.low) | 0;
+ // var carry = (low >>> 0) < (this.low >>> 0) ? 1 : 0;
+ // var high = (this.high + word.high + carry) | 0;
+
+ // return X64Word.create(high, low);
+ // }
+ });
+
+ /**
+ * An array of 64-bit words.
+ *
+ * @property {Array} words The array of CryptoJS.x64.Word objects.
+ * @property {number} sigBytes The number of significant bytes in this word array.
+ */
+ var X64WordArray = C_x64.WordArray = Base.extend({
+ /**
+ * Initializes a newly created word array.
+ *
+ * @param {Array} words (Optional) An array of CryptoJS.x64.Word objects.
+ * @param {number} sigBytes (Optional) The number of significant bytes in the words.
+ *
+ * @example
+ *
+ * var wordArray = CryptoJS.x64.WordArray.create();
+ *
+ * var wordArray = CryptoJS.x64.WordArray.create([
+ * CryptoJS.x64.Word.create(0x00010203, 0x04050607),
+ * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f)
+ * ]);
+ *
+ * var wordArray = CryptoJS.x64.WordArray.create([
+ * CryptoJS.x64.Word.create(0x00010203, 0x04050607),
+ * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f)
+ * ], 10);
+ */
+ init: function (words, sigBytes) {
+ words = this.words = words || [];
+
+ if (sigBytes != undefined) {
+ this.sigBytes = sigBytes;
+ } else {
+ this.sigBytes = words.length * 8;
+ }
+ },
+
+ /**
+ * Converts this 64-bit word array to a 32-bit word array.
+ *
+ * @return {CryptoJS.lib.WordArray} This word array's data as a 32-bit word array.
+ *
+ * @example
+ *
+ * var x32WordArray = x64WordArray.toX32();
+ */
+ toX32: function () {
+ // Shortcuts
+ var x64Words = this.words;
+ var x64WordsLength = x64Words.length;
+
+ // Convert
+ var x32Words = [];
+ for (var i = 0; i < x64WordsLength; i++) {
+ var x64Word = x64Words[i];
+ x32Words.push(x64Word.high);
+ x32Words.push(x64Word.low);
+ }
+
+ return X32WordArray.create(x32Words, this.sigBytes);
+ },
+
+ /**
+ * Creates a copy of this word array.
+ *
+ * @return {X64WordArray} The clone.
+ *
+ * @example
+ *
+ * var clone = x64WordArray.clone();
+ */
+ clone: function () {
+ var clone = Base.clone.call(this);
+
+ // Clone "words" array
+ var words = clone.words = this.words.slice(0);
+
+ // Clone each X64Word object
+ var wordsLength = words.length;
+ for (var i = 0; i < wordsLength; i++) {
+ words[i] = words[i].clone();
+ }
+
+ return clone;
+ }
+ });
+}());