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
161 changes: 78 additions & 83 deletions Getting-Started-with-Akka-http-signature.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The user can also run the rww-play web server and manipulate the access control

## The Key Pair

The Akka-Http-Signature library uses asymmetrical cryptography. As detailed in the [Public-Key cryptography wikipedia page](https://en.wikipedia.org/wiki/Public-key_cryptography), this is a cryptographic system that uses pairs of private and public keys. The library uses them to form Digital Signatures. In such a system public keys can be looked up by anyone and private keys are only known by their owner. Communication is achieved the following way :
The Akka-Http-Signature library uses what is known as asymmetrical cryptography. As detailed in the [Public-Key cryptography wikipedia page](https://en.wikipedia.org/wiki/Public-key_cryptography), this is a cryptographic system that uses pairs of private and public keys. The library uses them to form Digital Signatures. In such a system public keys can be looked up by anyone and private keys are only known by their owner. Communication is achieved the following way :
1. The sender sends a message with a header that is signed by his own private key
2. The server then receives the message and uses the sender's public key to verify that sender's identity
Copy link
Member

Choose a reason for hiding this comment

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

add that the server discovers the public key through using the keyId argument passed in the the message sent by the client. The rww-play server works with http and https:// urls and fetches the public key that way.


Expand Down Expand Up @@ -102,121 +102,85 @@ publ: scala.util.Try[java.security.PublicKey] = Success(
```
The .readPublicKeyFrom() function returns a Try of Public/Private key depending on whether the String given can be parsed back to some valid key. If the operation is successful the value of the original key is assigned to the new value in BigInt format.

The user can then save his keys on his local filesystem by using the following ammonite commands:
One should then make a .keys directory in their home directory to store the keys in for later use. One can do this via the bash `mkdir` command or navigate to their home directory and make a folder there manually.

The user can then save his keys within the .keys directory which we will make use of later in the guide by using the following ammonite commands:

```scala
write(wd/"publicKey.pem", RSAKeys.save(pub))
write(wd/"privateKey.pem", RSAKeys.save(priv))
write(home/".keys"/"publicKey.pem", RSAKeys.save(pub))
write(home/".keys"/"privateKey.pem", RSAKeys.save(priv))
```
This will save the contents of the key in String format within the file found by following the specified path in .pem files.

### 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.
One can also transform his public keys to a Pointed Graph. In order to do this one must first import multiple files in order to resolve the required dependencies:

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

After that the binder and all it's functionalities can be imported:
import org.w3.banana.jena.Jena

```scala
import org.w3.banana.binder._
import recordBinder._
import Jena._

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 [marshal (de-serialise)](http://doc.akka.io/docs/akka-http/10.0.9/scala/http/common/unmarshalling.html#unmarshalling) the public key String collected from the .pem file in located within the .keys directory. Different software can make use of this key at different times so it needs to be stored in a readily available form

```scala
@ object Cert {
@ val pubStr = read(home/".keys"/"pubKey.pem")

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)
pubStr: String = """MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkZXI44b8Qh7oNXzGvmdGSan1cisdzOur
WWWheSxvr9zHe5kyNh8UtVZeVjTsRr/SnwEwqm1KJRiu0CfnyjTZmhQZJitZDjg+sTdDx3pcxntC
MfckgRfaG+MjODbL2VTNHzaDYHlex0VwITcPH7RjPxJZyYmAlVg+MWMfX2VOBPqdBRZxO0DyH6ka
kENtgT0TJ9XNEVWH+gpezc66jgptz/wryzCaVobdF042TvQ5VoZC5gavUMgDuHS3TiT9LXBFT3Hg
A18h0qaZDkwJe6mHD/aULVrZYxf9irGJQAS2aTQHO/zdRJazlMX7oWQrVRpCmcz/BLpgxz4x9IC0
hHhTWQIDAQAB"""

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
}

defined object Cert

@ 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 code - one can always add more specifics and attributed to make turn this into a function.

```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

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 :
@ val keyGraph = pubKeyPG

```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 and return them as a pair like so:

```scala
@ val finalKeyPG = keyPG.rename(URI("#key"))

finalKeyPG: PointedGraph[Rdf] = org.w3.banana.PointedGraph$$anon$1@4353a749

@ finalKeyPGPointer = finalKeyPG.pointer
@ val(keyFinalPointer, keyFinalGraph) = (keyGraph.get.pointer -> keyGraph.get.graph)

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

finalKeyPGPointer : Rdf#Node = #key

@ finalKeyPGGraph = finalKeyPG.graph

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}
pointer: Jena#Node = #key
graph: 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}

```

As evident, the Blank node is now changed to something more readable and useful.
As evident, the pointer of this graph uses the relative URL "#key" as its identifier. This is useful as it allows the parties that have access to the public key easier access to that information. It is also convenient as, when dealing with the access control one can use this identifier to make sure the .acl agents are linked to this specific key file.

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 @@ -229,7 +193,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 @@ -247,6 +211,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 httpMethods.sc script which can be found in the [Read-write-web/Banana-wiki github repository](https://github.com/read-write-web/banana-wiki). This script 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.

```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 Expand Up @@ -329,4 +299,29 @@ could not find actor for Actor[akka://rww/user/rootContainer/card]rww.ldp.LDPExc
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
```
## Adding a Certificate Authority to the trust store

We have now shown how to use public and private keys to verify the identity of the user. But often we also want to let the user be able to confirm the identity of the server they are connecting to as well. This can be done through Certificate Authority. The user can view this on the rww-play server as it makes a CA for the locally hosted server.

In order to test whether this functionality works, one first has to save rww-play's certificate locally by using the following command after the server has been run:
Copy link
Member

Choose a reason for hiding this comment

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

I would add a line to show what not doing this looks like. You get some form of error on connection. Show the error in detail.


```bash
$ keytool -printcert -sslserver localhost:8443 -rfc > ~/.keys/localhost_8443.crt
```
In the real world there tends to be a constant communication between the server and its clients so one doesn't have to retrieve the Certificate manually but for the purpose of this guide we will get that certificate in bash and store it in a file.

The above command will retrieve the certificate and store it in a .crt file within the .keys directory, located in the current home directory for the user's system. For more information on SSL, one can refer to the [Quick start to WS SSL guide](http://typesafehub.github.io/ssl-config/WSQuickStart.html#obtain-the-root-ca-certificate).

After that, the user can run the getTest script from within ammonite in order to verify the identity of the local server like so:

```scala
@ import $exec.getTest

import $exec.$
```

Currently if the user runs the getTest script they may be presented with SSL Warnings. For more information on the matter one can look at the [Debugging SSL Connections Help page](https://typesafehub.github.io/ssl-config/DebuggingSSL.html).

This will add the CA of the localhost to the user's trust store and it will be recognised by the client that the user is running.

***