a) Tyr Webservice

The Tyr webservice or tyrservice is a XML-RPC based service, providing webSignatureOffice functionality for integration in third party applications (http://en.wikipedia.org/wiki/XML-RPC).

The Tyr webservice can be used by a variety of XML-RPC libraries which are available for all major programming languages (https://www.tutorialspoint.com/xml-rpc/xml_rpc_examples).

Preview + Testing

The Tyr webservice can be tested with a test account on webSignatureOffice.com, a development server can be provided on request.

Authentication and request signing

In order to use this service two keys have to be requested from StepOver:

  • an application key (customer key)
  • a request signing key (shared secret)

All service calls (other than login) must contain a sessionId string as follows:

session_id:timestamp:unique_id:customer_key

  1. session_id: the string returned after successful login
  2. timestamp: unix timestamp
  3. unique_id: a nonce, best is to use a UUID generator
  4. customer_key: the application key supplied by StepOver

Example: 7bd273e259b20052666ce9194468c439:1563264207:b9554fc6-43a2-467d-b4e9-7c694306f639:af5539de0753868ef1872410b2eb7366

TyrService help and error codes

The tyrservice always returns HTTP status code 200 (OK). If an error occurs, custom error codes are returned in the XML response body. The response is a struct or map, which also contains a fault string or error message.


 Example response when a login fails with the custom error code 102 (login_failed):
Login response 102
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions">
	<fault>
		<value>
			<struct>
				<member>
					<name>faultCode</name>
					<value>
						<i4>102</i4>
					</value>
				</member>
				<member>
					<name>faultString</name>
					<value>login_failed</value>
				</member>
			</struct>
		</value>
	</fault>
</methodResponse>
 List of TyrService error codes
List of TyrService error codes
100 = invalid service signature. E.g. no session id, unknown customer key, session expired
101 = braga server exception, severe
102 = login data incorrect
103 = max login tries reached, account locked, try again in 5 minutes
104 = document locked, try again later
105 = document upload failed
106 = permission denied
107 = user unknown
108 = no signature request
109 = duplicate pdf field name
110 = unexpected error
111 = document not found
112 = db error
113 = invalid adhoc code
114 = operation timed out
115 = audit server exception
116 = error xml parsing, technical error parsing the xml file for processXml. E.g. wrong encoding, corrupted file, malformed XML
117 = xml inconsistency, the xml file for processXml is functional inconsistent. E.g. two documents have the same <Position> in envelope
118 = invalid URL
119 = invalid customer key
120 = internal server error
121 = unknown session
122 = invalid envelope adhoc code
123 = invalid viewer type
124 = feature disabled

The complete list of tyrservice methods and error codes can be found in the tyrservice help documentation: https://www.websignatureoffice.com/tyrservice

XML-RPC spec: http://xmlrpc.scripting.com/spec.html

HTML server implementation: http://ws.apache.org/xmlrpc 

To activate gzip compression the http header Content-Encoding has to be set to gzip, this reduces the required bandwidth.

Implementing the TyrService in third party applications

Each request is signed as follows:

The request signature hash is built by concatenating the request xml and the shared_secret, then SHA256 hashing the result.

The request signature hash (hex representation) is sent as a HTTP header "X-SOSIGNATURE".

*Please note*: The shared_secret should be securely stored, in an app it should be obfuscated.

Example Client in Java (Apache XMLRpc)

TyrClient
public class TyrServiceClient {
    XmlRpcClient client = new XmlRpcClient();
    XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
    String sharedSecret;
    String customerKey;

    public TyrServiceClient(String host, String sharedSecret, String customerKey) throws MalformedURLException, NoSuchAlgorithmException, KeyManagementException {
        this.customerKey = customerKey;
        this.sharedSecret = sharedSecret;
        config.setServerURL(new URL(host + "/tyrservice"));
        client.setConfig(config);
        client.setTransportFactory(new LoggingTransportFactory(client));
        config.setEnabledForExtensions(true);
        TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            public void checkClientTrusted(X509Certificate[] certs, String authType) {
                // Trust always
            }

            public void checkServerTrusted(X509Certificate[] certs, String authType) {
                // Trust always
            }

        }};
        // Install the all-trusting trust manager
        SSLContext sc = SSLContext.getInstance("SSL");
        // Create empty HostnameVerifier
        HostnameVerifier hv = new HostnameVerifier() {
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }
        };

        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(hv);

    }

    class LoggingTransportFactory extends XmlRpcSun15HttpTransportFactory {

        XmlRpcClient client;

        @Override
        public XmlRpcTransport getTransport() {
            return new LoggingTransport(client);
        }

        public LoggingTransportFactory(XmlRpcClient pClient) {
            super(pClient);
            client = pClient;
        }

    }

    class LoggingTransport extends XmlRpcSun15HttpTransport {

        @Override
        protected InputStream getInputStream() throws XmlRpcException {

            BufferedReader reader = null;
            StringBuilder respBuf = new StringBuilder();

            try {
                InputStream inputStream = super.getInputStream();
                reader = new BufferedReader(new InputStreamReader(inputStream));
                String line = null;
                while ((line = reader.readLine()) != null) {
                    respBuf.append(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            return new ByteArrayInputStream(respBuf.toString().getBytes());
        }

        @Override
        protected void writeRequest(ReqWriter reqWriter) throws XmlRpcException {

            try {
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                reqWriter.write(os);
                String requestHeader = "";
                requestHeader = new String(Hex.encodeHex(DigestUtils.sha256(os.toString() + sharedSecret)));
                this.setRequestHeader("X-SOSIGNATURE", requestHeader);
                super.writeRequest(reqWriter);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public LoggingTransport(XmlRpcClient pFactory) {
            super(pFactory);
        }
    }
    
    public Object execute(String method, Object[] param) throws XmlRpcException {
        Object execute = client.execute("tyrservice." + method, param);
        return execute;
    }

Example client with imports:

TyrServiceClient.java

Request example to upload and view a signature request

1.) TyrService.login: create a sessionId

Method: tyrservice.login | Return: sessionId 

XmlRpcClient client = new XmlRpcClient(); Object[] loginParams = new Object[]{ "mylogin", "mypassword" }; //parameters are username/login and password
sessionId = (String) client.execute( "login", loginParams );
 
String sessionIdString = sessionId + ":" + System.currentTimeMillis() / 1000 + ":" + UUID.randomUUID() + ":" + customerKey;
 
//the sessionIdString has to be added for each method call, e.g. for method processXml( string sessionIdString, base64 xml )
 Login Request Example

tyrservice.login

login XML request
<?xml version="1.0" encoding="UTF-8"?>
<methodCall xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions">
  <methodName>tyrservice.login</methodName>
  <params>
    <param>
      <value>foo</value>
    </param>
    <param>
      <value>bar</value>
    </param>
  </params>
</methodCall>
login XML response
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions">
  <params>
    <param>
      <value>eb9cd247e5850e785093aee86b9b35b9</value>
    </param>
  </params>
</methodResponse>


2.) TyrService.processXml: Upload a document with signature definition

For this step, you need to encode your PDF-file into base64. The base64 code will be placed then inside the tag <PDF> at the end of the XML-File.

The structure of the XML will be shown later at topic "Input-XML Extensions" or here: XML Sample

Please continue with the implementation steps.


Method: tyrservice.processXML | Return: envelope or document id 

Object[] params = new Object[]{ sessionIdString, xmlBytes }; //parameters are sessionIdString and XML bytes 
Integer documentId = (Integer) client.execute( "processXml", params ); 

-> The file is uploaded on the webSignatureOffice server as a PDF now.


 ProcessXml Request Example

tyrservice.processXml

login XML request
<?xml version="1.0" encoding="UTF-8"?>
<methodCall xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions">
	<methodName>tyrservice.processXml</methodName>
	<params>
		<param>
			<value>{sessionIdString}</value>
		</param>
		<param>
			<value>
				<base64>PD94b...</base64> <!-- base64 encoded XML file containing the documents and signature definitions -->
			</value>
		</param>
	</params>
</methodCall>
login XML response
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions">
	<params>
		<param>
			<value>
				<i4>123456</i4>
			</value>
		</param>
	</params>
</methodResponse>


3.) TyrService.getEncryptedParameters: Create a viewer link for other users

The method getEncryptedParameters encrypts the information for a viewer url. The returned encrypted string is added as the querey parameter "xen" to a process-servlet (path /process) call. The process servlet decrypts the parameter and redirects to the viewer. The method requires admin rights because links for other users can be created.

TimeLimitedViewer URL format: https://websignatureoffice.com/process?xen=JLKASDJLKASDJasdasd&locale=de

Encrypted parameters contain

  • a timestamp: Until the viewer link is valid
  • a user_id: Id of the user the link is created for
  • a id: document or envelope id

In the method, it must be defined, which viewer type is requested as the first parameter:

  • "tlev": TimeLimitedEnvelopeViewer
  • "tldv": TimeLimitedDocumentViewer


String userId = 123; //can be obtained with the method tyrservice.getUserMetaByLogin; see below
String time = Long.toString( System.currentTimeMillis() + ( 1000 * 60 * 2880 ) ); //the validity of the URL, for example 48 hours
String documentId = 2222; //the response from the processXML call
 
Map<String, String> values = new HashMap<>();
values.put( "u", userId );
values.put( "t", time );
values.put( "d", documentId );
 
Object[] params = new Object[]{ sessionIdString, "tldv", values }; //action is TimeLimitedDocumentViewer
String encryptedParams= (String) client.execute( "tyrservice.getEncryptedParameters", params );

String url = host + "/process?xen=encryptedParams;


 GetEncryptedParameters Request Example

tyrservice.getEncryptedParameters

getEncryptedParameters XML request
<?xml version="1.0" encoding="UTF-8"?>
<methodCall xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions">
	<methodName>tyrservice.getEncryptedParameters</methodName>
		<params>
			<param>
				<value>{sessionIdString}</value>
			</param>
			<param>
				<value>tldv</value>
			</param>
			<param>
				<value>
					<struct>
						<member>
							<name>t</name>
							<value>1731005422130</value>
						</member>
						<member>
							<name>d</name>
							<value>79313</value>
						</member>
						<member>
							<name>u</name>
							<value>34375</value>
						</member>
					</struct>
				</value>
			</param>
		</params>
</methodCall>
GetEncryptedParameters XML response
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions">
	<params>
		<param>
			<value>ck-Q5ujpc2cZtEP0hLdNeIz0Okf8tzo5Vr22j7WiyqaIQft2fJXd0cu3lwSM2ehh</value>
		</param>
	</params>
</methodResponse>


Method: tyrservice.getUserMetaByLogin | Return: user metadata (necessary for document links)

 GetUserMetaByLogin: Obtaining the user_id

The user_id is neccessary to create a TimeLimitedViewer link. If it is not known, it can be retrieved with the method getUserMetaByLogin:

Object[] params = new Object[]{ sessionIdString, "mylogin" }; // parameters are sessionIdString and username/login
HashMap<String,Object> userMetadata = (HashMap<String, Object>) client.execute( "tyrservice.getUserMetaByLogin", params );
 
String userId = userMetadata.get("user_id").toString();

3.1 TyrService.getTimeLimitedViewerUrl: create a viewer link for own user

To create a TimeLimitedViewer link for the own user, the method getTimeLimitedViewerUrl can be used. The method directly returns the URL. The method has five parameters:

  • sessionId
  • viewerType:
    • tlev: TimeLimitedEnvelopeViewer
    • tldv: TimeLimitedDocumentViewer
  • timestamp
  • locale

The method can be called by a registered user and doesn't require admin rights. Therefore only links for documents or envelopes the user is associated with as a signer, owner or observer can be created. The userId is not necessary to call the methotd.

String timestamp = Long.toString( System.currentTimeMillis() + ( 1000 * 60 * 2880 ) ); //the validity of the URL, for example 48 hours
String documentId = 2222; //the response from the processXML call
 
Object[] params = new Object[]{ sessionIdString, "tldv", documentId, timestamp, "en" }; //viewerType is TimeLimitedDocumentViewer
String url = (String) client.execute( "tyrservice.getEncryptedParameters", params );
 GetTimeLimitedViewerUrl Request Example

tyrservice.getEncryptedParameters

GetTimeLimitedViewerUrl XML request
<?xml version="1.0" encoding="UTF-8"?>
<methodCall xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions">
	<methodName>tyrservice.getTimeLimitedViewerUrl</methodName>
		<params>
			<param>
				<value>{sessionIdString}</value>
			</param>
			<param>
				<value>tldv</value>
			</param>
			<param>
				<value>
					<i4>123456</i4>
				</value>
			</param>
			<param>
				<value>1762505614000</value>
			</param>
			<param>
				<value>de</value>
			</param>
		</params>
</methodCall>
GetTimeLimitedViewerUrl XML response
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions">
	<params>
		<param>
			<value>https://websignatureoffice.com/process?xen=JLKASDJLKASDJasdasd&locale=de</value>
		</param>
	</params>
</methodResponse>

Input-XML Extensions

For customers which used or still use our eSignatureOffice in the past: webSignatureOffice uses the same standard input-XML as eSignatureOffice does, but with a few extensions.

You can find the definition of the input XML here: Input-XML


In the signature field definition a user name can be set. The UserName is either a valid email adress or the user name of a registered user. If no user name is defined, the logged in user is taken as signer

<Signatures>
      <Signature>
        <Placeholder>Signature 1</Placeholder>
        <Width>4</Width>
        <Height>3</Height>
        <UserName>myLogin</UserName>
        ...


Additionally a signature type can be defined:
0 = stamp data (text/image) without signature
1 = digital stamp (signature with user certificate instead of hand-written signature)
2 = html-signer signature only
3 = pad signature only
4 = app signature only (QR code)
If no signature type is defined, a selection window appears. The signer can choose between following types: StepOver signature pad OR one of our apps OR HTML-signer (with a pen or your finger on your mobile device)


<Signatures>
      <Signature>
        <Placeholder>Signature 1</Placeholder>
        <Width>4</Width>
        <Height>3</Height>
        <UserName>username</UserName>
        <SignatureType>2</SignatureType>
		...


You can also rename the uploaded PDF with the PDFName tag.

With this tag you can change the standard upload name into something more suitable.

PDFName
	<Signatures>
    	<Signature>
    		<Placeholder>Signature 1</Placeholder>
			<Width>4</Width>	
        	<Height>3</Height>	
        	<UserName>username</UserName>
			<SignatureType>2</SignatureType>
			...
		</Signature>
	</Signatures>
<PDFName>AlternativePDFName</PDFName>