[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Handle-info] hsj 8 challenge response "Authentication via Authorization: Handle" via HS_PUBKEY python example.



First of all, use the hdl-convert-key utility to produce a standard PEM file for the handle private key file.  (That way you can take advantage of key-decoding functionality from a standard library.)

I've attached a Python example written by my colleague Ben Hadden.  It uses "requests" and "PyCrypto".  It resolves a handle record, changes the data in the handle value at index 1, and updates the handle record.  Change the values in the main() function at the top to reflect your actual situation.

The updateHandleRecord() function does the work.  It sends an unauthenticated PUT request, parses the WWW-Authenticate: header from the response to that, constructs an Authorization: header, and sends the request again authenticated.

Let us know if you have any questions!

Robert

import os
import json
import base64
import requests

from Crypto.PublicKey import RSA 
from Crypto.Signature import PKCS1_v1_5 
from Crypto.Hash import SHA256 

def main():
    pathToPrivateKeyPemFile = "/path/to/privatekey.pem"
    authId = "300:id/handle"
    handle = "handle/to-edit"
    ip = "handle.server.ip.address"
    port = 8000
    handleRecord = getHandleRecord(handle, ip, port)
    print handleRecord
    emailValue = handleRecord["values"][1]
    emailValue["data"]["value"] = "new.email@example.org"
    print handleRecord
    updateHandleRecord(handle, handleRecord, ip, port, pathToPrivateKeyPemFile, authId);

def getHandleRecord(handle, ip, port):
    url = "https://"; + ip + ":" + str(port) + "/api/handles/" + handle
    r = requests.get(url, verify=False)
    handleRecord = r.json()
    return handleRecord

def updateHandleRecord(handle, handleRecord, ip, port, pathToPrivateKeyPemFile, authId): 
    headers = {
        "Content-Type": "application/json"
    }
    url = "https://"; + ip + ":" + str(port) + "/api/handles/" + handle
    BODY =  json.dumps(handleRecord)
    #send the request expecting a response with a WWW-Authenticate header
    r = requests.put(url, headers=headers, verify=False, data=BODY)
    authenticateHeader = r.headers["WWW-Authenticate"]
    authenticateHeaderDict = parseAuthenticateHeader(authenticateHeader)
    serverNonceBytes = base64.b64decode(authenticateHeaderDict["nonce"])
    sessionId = authenticateHeaderDict["sessionId"]
    clientNonceBytes = generateClientNonceBytes()
    clientNonceString = base64.b64encode(clientNonceBytes)
    combinedNonceBytes = serverNonceBytes + clientNonceBytes
    signatureBytes = signBytesSHA256(combinedNonceBytes, pathToPrivateKeyPemFile)
    signatureString = base64.b64encode(signatureBytes)
    authorizationHeaderString = buildAuthorizationString(signatureString, "HS_PUBKEY", "SHA256", sessionId, clientNonceString, authId)
    headers["Authorization"] = authorizationHeaderString
    #send the request again with a valid correctly signed Authorization header
    r2 = requests.put(url, headers=headers, verify=False, data=BODY)
    print r2.status_code, r2.reason 
    return r2
    
def signBytesSHA256(bytesArray, pathToPrivateKeyPemFile):
    key = open(pathToPrivateKeyPemFile, "r").read() 
    rsakey = RSA.importKey(key) 
    signer = PKCS1_v1_5.new(rsakey) 
    digest = SHA256.new() 
    digest.update(bytesArray) 
    sign = signer.sign(digest) 
    return sign;

def buildAuthorizationString(signatureString, typeString, alg, sessionId, clientNonceString, authId):
    result = ('Handle ' 
              'sessionId="' + sessionId + '", ' 
              'cnonce="' + clientNonceString + '", '  
              'id="' + authId + '", '  
              'type="' + typeString + '", '  
              'alg="'+ alg +'", '  
              'signature="' + signatureString + '"')
    return result    
    
def parseAuthenticateHeader(authenticateHeader): 
    result = {}
    tokens =  authenticateHeader.split(", ");
    for token in tokens:
        firstEquals = token.find("=")
        key = token[0:firstEquals]
        # quick and dirty parsing of the expected WWW-Authenticate headers
        if key == "Basic realm":
            continue
        if key == "Handle sessionId":
            key = "sessionId"
        value = token[firstEquals+2:len(token)-1]
        result[key] = value
    return result
    
def generateClientNonceBytes():
    return  bytearray(os.urandom(16))   
    
if __name__ == '__main__':
    main()    

> On Feb 2, 2016, at 9:30 AM, Robert Verkerk <robert.verkerk@surfsara.nl> wrote:
> 
> Hi,
> 
> We want to use the handle rest-full api. But don’t want to use the basic authentication with HS_SECKEY.
> 
> Does anybody have an python example of challenge response  "Authentication via Authorization: Handle" using HS_PUBKEY?
> We would like to generate a private/public keypair with hdl-keygen. The public key is stored in the handle database. The private key is known on the client.
> 
> We saw the handle technical manual chapter 14.6.4 and further. This shows the basics. But it is not enough to create a working piece of python code. 
> 
> -- 
> Greetings,
> 
> Robert Verkerk
> 
> 
> 
> 
> 
> 
> _______________________________________________
> Handle-Info mailing list
> Handle-Info@cnri.reston.va.us
> http://www.handle.net/mailman/listinfo/handle-info

_______________________________________________
Handle-Info mailing list
Handle-Info@cnri.reston.va.us
http://www.handle.net/mailman/listinfo/handle-info