Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Getting-Started-with-Akka-http-signature.md #3

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
122 changes: 47 additions & 75 deletions Getting-Started-with-Akka-http-signature.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,111 +114,77 @@ This will save the contents of the key in String format within the file found by

### Saving a public Key to RDF

A user can also transform his public keys to an RDF. In order to do this one must first import multiple files in order to resolve the required dependencies.
A user can also transform his public keys to an RDF. In order to do this one must first import multiple files in order to resolve the required dependencies:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

->"to an RDF Graph".

There is no such thing as an RDF.

Also I don't think we should be speaking of "user" here. Just stick to the generic one. Users are often thought of as end users, whereas here we are speaking of programmers, but that is obvious and a mouthful.


The org.w3 declarations required are:

```scala
import org.w3.banana.binder
import org.w3.banana.binder.RecordBinder
implicit val binder = RecordBinder
val cert = CertPrefix[Rdf]
```
Several java imports from java's security and math library are also required for this process:
import $ivy.`run.cosy::solid-client:0.1-SNAPSHOT`
import run.cosy.auth.RSAKeys
import com.typesafe.sslconfig.akka._
import akka.actor._
import akka.http.scaladsl.model.Uri
import com.typesafe.config.ConfigFactory
import akka.event.Logging
import akka.stream.{ActorMaterializer, ActorMaterializerSettings,Supervision,_}
import akka.http.scaladsl.model.{Uri=>AkkaUri,_}

```scala
import java.math.BigInteger
import java.security.KeyFactory
import java.security.interfaces.RSAPublicKey
import java.security.spec.RSAPublicKeySpec
```
import scala.concurrent.ExecutionContext

import org.w3.banana.jena.Jena

After that the binder and all it's functionalities can be imported:
import Jena._

```scala
import org.w3.banana.binder._
import recordBinder._
import run.cosy.solid.client.Web._
import java.security.interfaces.RSAPublicKey
```

Finally, after that, the user can create the Cert object that contains the required dependencies to turn a Key into a Pointed Graph
Finally, after that, the user can create a value for the public key String collected from the .pem file in located within the .keys directory.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

horrible grammar. A value?
The thing to point out here is that different software could be using this public and private keys on different days, and so need to read this in order to be able to authenticate to different resources on the web. So we need to de-serialise a.k.a marshal the public key. Those are all terms you should learn of, and use correctly.


```scala
@ object Cert {

implicit val rsaClassUri = classUrisFor[RSAPublicKey](cert.RSAPublicKey)
val factory = KeyFactory.getInstance("RSA")
val exponent = property[BigInteger](cert.exponent)
val modulus = property[Array[Byte]](cert.modulus)

implicit val binder: PGBinder[Rdf, RSAPublicKey] =
pgb[RSAPublicKey](modulus, exponent)(
(m, e) => factory.generatePublic(new RSAPublicKeySpec(new BigInteger(m), e)).asInstanceOf[RSAPublicKey],
key => Some((key.getModulus.toByteArray, key.getPublicExponent))
) // withClasses rsaClassUri
}
@ val pubStr = read(home/".keys"/"pubKey.pem")

defined object Cert
pubStr: String = """MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkZXI44b8Qh7oNXzGvmdGSan1cisdzOur
WWWheSxvr9zHe5kyNh8UtVZeVjTsRr/SnwEwqm1KJRiu0CfnyjTZmhQZJitZDjg+sTdDx3pcxntC
MfckgRfaG+MjODbL2VTNHzaDYHlex0VwITcPH7RjPxJZyYmAlVg+MWMfX2VOBPqdBRZxO0DyH6ka
kENtgT0TJ9XNEVWH+gpezc66jgptz/wryzCaVobdF042TvQ5VoZC5gavUMgDuHS3TiT9LXBFT3Hg
A18h0qaZDkwJe6mHD/aULVrZYxf9irGJQAS2aTQHO/zdRJazlMX7oWQrVRpCmcz/BLpgxz4x9IC0
hHhTWQIDAQAB"""

@ import Cert._
```

After this all dependencies should be resolved and the user will be able to transform the keys into a Pointed Graph:
After this all dependencies should be resolved and the user will be able to transform the keys into a Pointed Graph using the following function
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the function below is hardly a function worth mentioning: It's more of a piece of test code to verify that things are working correctly. That is obvious because there are no attributes passed to the funciton.


```scala
@ val keyPG = pub.toPG
@ def pubKeyPG = {
import ops._
implicit val bind = run.cosy.crypto.Cert.binderWithName[Rdf](Uri("#key"))
pubKey.map(_.toPG)
}

keyPG : PointedGraph[RDF] = org.w3.banana.PointedGraph$$anon$1@7f8fa63d
defined function pubKeyPG
```
One can then retrieve both the pointer and the graph:
After that the user can simply call said function to get a pointed graph:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a little note as to what the binderWithName function does.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unsure what you mean by this - where can I find this function

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if you search the PR you will find that function. It's from the client code.


```scala
@ val keyGraph = keyGraph.graph
@ val keyGraph = pubKeyPG

keyGraph : RDF#Graph = {5880.026289.0-02638.0-6288.0-06411.0-23110.021178.029228.0 http://www.w3.org/ns/auth/cert#exponent "65537"^^http://www.w3.org/2001/XMLSchema#integer; 5880.026289.0-02638.0-6288.0-06411.0-23110.021178.029228.0 @http://www.w3.org/ns/auth/cert#modulus "00937adccd722bc982aed4847872b81e36b890bca13166714bc2befe4d8547b6218ecd2da1eb020198a4ea00e4db6757c7dda738ec8db8b3bf211d3a3a17e196a2035bc4c79d06d8a581487d9f49e86374712b10ef500dfa242a20cab52911e2636c9d99b21fe9768ef2381989a25dc8b0b7a46531249aac27c4b8ab451a19d5fbdfa5f78b0deac9778c7ff87cf6106ae4a6433466beb21df1265bf1fc9ab9cc80d7aff8cc4d0f67ae28647e4048da8df753493b8de6a8e0961416b4b37f7012907d2e756034b8a84c6e495c8f1d81f69843ae51379571d83b38e1c08a08c3748ef75ed7ecb016d0c8426b30c8c5060a08f87f6764b0ec14667cd6f0daa1244157"^^http://www.w3.org/2001/XMLSchema#hexBinary}

@ val keyRDFPointer = keyGraph.pointer

keyRDFPointer: RDF#Node = 5880.026289.0-02638.0-6288.0-06411.0-23110.021178.029228.0
```
As is evident, the pointer is an automatically generated Blank Node, which can be quite difficult to process. Because of this, it would be more optimal to change the pointer to not be a blank node but rather a #uri. The user can change the pointer by using the following function :

```scala
@ implicit class PGwrapper(val pg: PointedGraph[Rdf]) extends AnyVal {
def rename(to: Rdf#Node)(implicit ops: RDFOps[Rdf]): PointedGraph[Rdf] = {
val oldNode = pg.pointer
PointedGraph[Rdf](
to,
ops.makeGraph(pg.graph.triples.map{ triple =>
ops.fromTriple(triple) match {
case (oldNode,rel,obj) => ops.makeTriple(to,rel,obj)
case (subj,rel,oldNode) => ops.makeTriple(subj,rel,to)
case _ => triple
}
})
)
}
}

defined class PGwrapper
res44: Try[PointedGraph[Jena]] = Success(org.w3.banana.PointedGraph$$anon$1@5487b93e)
```

After this, one can simply change the header by into something simple by calling this function like so:
The function pubKeyPG returns a Try[PointedGraph[Jena]]. We can then retreive both the pointer and the graph from this PointedGraph like so:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should be able to do that in one step I think using val (pointer,graph) = ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah this is how it works:

val org.w3.banana.PointedGraph(p,g) = res2.value.get.get._1.content

Read up pattern matching in Ordersky's book.


```scala
@ val finalKeyPG = keyPG.rename(URI("#key"))
@ val keyFinalPointer= keyGraph.get.pointer

finalKeyPG: PointedGraph[Rdf] = org.w3.banana.PointedGraph$$anon$1@4353a749
keyFinalPointer: Jena#Node = #key

@ finalKeyPGPointer = finalKeyPG.pointer

finalKeyPGPointer : Rdf#Node = #key
@ val keyFinalGraph = keyGraph.get.graph

@ finalKeyPGGraph = finalKeyPG.graph
keyFinalGraph: Jena#Graph = {#key @http://www.w3.org/1999/02/22-rdf-syntax-ns#type http://www.w3.org/ns/auth/cert#RSAPublicKey; #key @http://www.w3.org/ns/auth/cert#modulus "009195c8e386fc421ee8357cc6be674649a9f5722b1dccebab5965a1792c6fafdcc77b9932361f14b5565e5634ec46bfd29f0130aa6d4a2518aed027e7ca34d99a1419262b590e383eb13743c77a5cc67b4231f7248117da1be3233836cbd954cd1f368360795ec7457021370f1fb4633f1259c9898095583e31631f5f654e04fa9d0516713b40f21fa91a90436d813d1327d5cd115587fa0a5ecdceba8e0a6dcffc2bcb309a5686dd174e364ef439568642e606af50c803b874b74e24fd2d70454f71e0035f21d2a6990e4c097ba9870ff6942d5ad96317fd8ab1894004b66934073bfcdd4496b394c5fba1642b551a4299ccff04ba60c73e31f480b484785359"^^http://www.w3.org/2001/XMLSchema#hexBinary; #key @http://www.w3.org/ns/auth/cert#exponent "65537"^^http://www.w3.org/2001/XMLSchema#integer}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

val org.w3.banana.PointedGraph(p,g) = keyGraph.get

finalKeyPGGraph: Rdf#Graph = {#key @http://www.w3.org/ns/auth/cert#modulus "0087acb657366c6a911a0ce2470c24b8d85dd21ae76c6001db37d122c2eafe0fe2ae35541ccdd3c1c81603d98dd7d61b2c31c8605f81fbc3566604b7755793698836caaa99d868477c46b5b735529b50c0acfea6e10fc9b953697a67d57499801beb651e7e08343131a00d1873ab753ce5e79fc961874ee132472f7f210bc38966081fe263c620b67469b52cc555a26dee4fa10d8f40959e0e13516cc0bd1c1669ce53367d28248149142429b127f01e13d9bf21de52a4ac5694d5038a94178e144823b152fa19cbbc094dd40ddbd41e2195a19081125887fccedae21c50660c629a6f2ba4c85a54e1a6472f90b7b62ac7fe58fdae9daa0edc8edec49802fbfac7"^^http://www.w3.org/2001/XMLSchema#hexBinary; #key @http://www.w3.org/ns/auth/cert#exponent "65537"^^http://www.w3.org/2001/XMLSchema#integer}
```

As evident, the Blank node is now changed to something more readable and useful.
As evident, the pointer of this graph is a useful identifier "#key" and the graph itself is the key information.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it useful? Also note that it is a relative URI. That should help you understand what is interesting about it when you post it. (It is useful also because how it is used in access control settings).


The user can transform his key graph into a one of several well-known formats before publishing it on the server. One such format is turtle. In order to do that however, more external libraries are required:

Expand All @@ -231,7 +197,7 @@ import org.w3.banana.jena.Jena
One can then represent the rdf of the key in turtle format:

```scala
@ val toTurtle = turtleWriter.asString(finalKeyPGGraph ,"").get
@ val toTurtle = turtleWriter.asString(keyFinalGraph ,"").get

toTurtle: String = """<#key> <http://www.w3.org/ns/auth/cert#exponent>
65537 ;
Expand All @@ -249,6 +215,12 @@ write(wd/"publicKey.ttl", toTurtle)

### Attaching Public keys to a File/URI
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Publishing the Public key to the Web

There are a number of ad hoc ways to do this and one simple standard way to publish it.
The ad hoc ways requires placing the public key on the server by either copying it over to the right location with ftp, sftp, scp, ... or of connecting to the remote server using telnet, or ssh and building the object there. In each case this requires knowing a lot in addition to the location (URL) one wants the document to be found at. It requires knowing:

  • the type of server that is running
  • which files are metadata files, their conventions and usually their syntax
  • how to make sure content negotiation is working correctly to avoid tying semantic web URIs to representations,
  • where the root of the file system is
  • what the password or access control system is on that server
  • and usually getting access to the server via ssh gives access to all the conentent there, rather than allowing
    access control rules to be set per resource.

For illustration see the document [ I pointed you to twice already] and to the setup for rww-play.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please add that to the doc as is, with the last link filled in.


The most efficient way to attach a public key to a server is by using the rww-play library's httpMethods.sc script which makes use of the solid-server POST definition. That way the user can use the postLocal() and postLocalGood() functions to pubish his public key on a web and local server respectively.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not rww-play's httpMethod.sc !


```scala
Code but errors not resolved
```

One can use the `cp` or the `mv` Ammonite commands to move the public key file into the test_www directory which resides within the rww-play directory. The process of attaching the file, containing the key to a URI is very similar.

```scala
Expand Down