Author Posts

December 20, 2016 at 9:01 pm

I am going through the instructions given by Box on how to connect to their API using a JWT.
https://docs.box.com/docs/app-auth
I have created a public and private pem key.
I have the Box pieces setup.

I am at the point where I trying to construct the JWT.
It states there are three parts, the Header, Claims, and Signature.
I believe I have the Header and claims JSON created properly.

$rawheader = [Ordered]@{
    alg = "RS256"
    typ = "JWT"
    kid = "NumberFromBox"  #Box
} | ConvertTo-Json -Compress

$client_id = "Generated by box"   #Box
$enterprise_id = "FromBox"    #Box
$unixDatePlus60 = [int][double]::Parse((Get-Date -Date (Get-Date).AddSeconds(59) -UFormat %s))
$rawclaims = [Ordered]@{
    iss = $client_id
    sub = $enterprise_id
    box_sub_type = "enterprise"
    aud = "https://api.box.com/oauth2/token"  #Box
    jti = "$rdmHex"
    exp = $unixDatePlus60
} | ConvertTo-Json -Compress

December 20, 2016 at 9:02 pm

Is there a question?

December 20, 2016 at 9:16 pm

4. Constructing the JWT Assertion
Once you have created the RSA keypair and submitted the public key to Box, you can request Enterprise and User OAuth2.0 Access tokens using the JWT grant.

Every JWT assertion is composed of three components, the header, the claims, and the signature.

The header specifies the algorithm used for the JWT signature.
The claims contain the information necessary to authenticate and provide the correct token.
The signature is used to verify the identify of the application and is verified using the public key.
To construct the JWT assertion, these three components must be base64 encoded and concatenated using a “.” separator:

Format for JWT Assertion
..
Once encoded and concatenated, the JWT assertion will look like this:

Example Encoded JWT Assertion
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9eyJpc3MiOiJ2Z3.
B2bWFvaDJjZ2ZjNGRuMzFnMWx0cmlhbmdlZCIsInN1YiI.
6IjE2ODczOTQzIiwiZXhwIjoxNDI5MDM3ODYwLCJqdGkiOiJ

To do this part, the only thing I could figure out, following their instructions is to use the Nuget module for encoding. So I used Visual Studio and created a project and added under Tool – NuGet Package Manager – NuGet Solutions:
Microsoft.Owin.Security 3.0.0
Microsoft.Owin 3.0.0
OWIN 1.0.0

Box gives this site as a general reference to help with this process, rather than just telling how it is actually done.
https://jwt.io/#libraries

If someone can help me figure out how to begin to use this site, I would be greatful.

December 20, 2016 at 9:21 pm

Because I don't know how to use the reference they supplied, I found this blog post I thought would be helpful.
http://www.thingsthatmademeangry.com/2014/11/google-apps-oauth2-service-account.html

This is the site that showed how to use the Nuget pieces.

#MS Owin for base64url encoding
$Owin = 'C:\EEDevOps\TeamFoundation\DevOps\Box\BoxConnect\packages\Owin.1.0\lib\net40\Owin.dll'
$msOwin = 'C:\EEDevOps\TeamFoundation\DevOps\Box\BoxConnect\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll'
$msOwinSec = 'C:\EEDevOps\TeamFoundation\DevOps\Box\BoxConnect\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll'
Add-Type -Path $Owin 
Add-Type -Path $msOwin
Add-Type -Path $msOwinSec

# get a base64url encoder object
$encoder = New-Object Microsoft.Owin.Security.DataHandler.Encoder.Base64UrlTextEncoder
# we then serialize to UTF-8 bytes, then base64url encode the claims
$header = $encoder.Encode([System.Text.Encoding]::UTF8.GetBytes($rawheader))
$claims = $encoder.Encode([System.Text.Encoding]::UTF8.GetBytes($rawclaims))

This gives me this much:
PS C:\EEDevOps> $header
eyJhbGciOiJSUzI1NtIsInR5cCI6IkpXVCvsImtpZCI6Iml0MmgxZXVrIn0

PS C:\EEDevOps> $claims
eyJpc3MiOiJ6NbliMmkzbWZ3Z3ZnazBiNzFzNzNlNXM4Y3hhdzF0bSIsInN1YiI6IjE1NjM1OCIsImJveF9zdrrfdHlwZSI6ImVudGVycHJpc2UiLCJhdWQiOiJodHRwczovL2FwaS5ib3guY29tL29hdXRoMi90b2tlbiIsImp0aSI6IzNCNjAxMUJFNERBMTZGMT
Y0NzAwIiwiZXhwIjoxNDgyMjUwMDY3fQ

Now I need to generate the signature and pull it all together to get it in the right format.

December 20, 2016 at 9:26 pm

The Box employee gives as an example his code:
https://github.com/cghil/cmd-line-app-users

And the blog post I found does it this way:

$googleCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("[path to your p12 private key]", "notasecret",[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable )
    
 
# get just the private key
$rsaPrivate = $googleCert.PrivateKey
 
# get a new RSA provider
$rsa = New-Object System.Security.Cryptography.RSACryptoServiceProvider
 
# copy the parameters from the private key into our new rsa provider
$rsa.ImportParameters($rsaPrivate.ExportParameters($true))

I don't know what I am missing, but this doesn't work. I am not sure what I am suppose to put in this spot above:
"[path to your p12 private key]"

I need help trying to pull all of this together using the private key to generate the final JWT.

December 20, 2016 at 9:26 pm

Hi Don, Yup there is a question...lol

December 20, 2016 at 9:31 pm

It's probably expecting the private key to be in a key file on disk. However... honestly, you should ask the blogger. I'm a PowerShell guy, not a Box guy ;).

Based on https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2(v=vs.110).aspx, there are several constructors for that class; the one with only one String seems to be looking for a file path. See https://msdn.microsoft.com/en-us/library/ms148423(v=vs.110).aspx. If you've got the private key in another form, another constructor might be appropriate, but I'm also not a .NET guy ;).

December 20, 2016 at 9:48 pm

Thanks Don, I will look through these references and see what I can pull together.
In my opinion, if I am a PowerShell Guy, which I am, I am the glue, the string or whatever else is needed because I can traverse any piece of any environment because PowerShell is just that good!!!

Always taking it to the next level. You should see all the functions I have created for the Box API, now I want to make it so no one has to use them, Jams just runs them once a month as needed.

December 21, 2016 at 7:31 am

may be you can use .net libraries from https://jwt.io/#libraries ?
'Jose JWT' use examples seem to me easy to translate into powershell from c#

January 4, 2017 at 1:56 pm

Ok, I have created a project in Visual Studio and imported Jose JWT:
PS C:\EEDevOps> dir C:\EEDevOps\TeamFoundation\DevOps\Box\BoxJWT\BoxJWT1

Directory: C:\EEDevOps\TeamFoundation\DevOps\Box\BoxJWT\BoxJWT1

Mode LastWriteTime Length Name
—- ————- —— —-
d—– 1/3/2017 4:18 PM bin
d—– 1/3/2017 4:18 PM obj
d—– 1/3/2017 4:20 PM packages
d—– 1/3/2017 4:18 PM Properties
-a—- 1/3/2017 4:18 PM 2581 .gitattributes
-a—- 1/3/2017 4:18 PM 4077 .gitignore
-a—- 1/3/2017 4:18 PM 189 App.config
-a—- 1/3/2017 4:20 PM 2987 BoxJWT1.csproj
-a—- 1/3/2017 4:18 PM 980 BoxJWT1.sln
-a—- 1/3/2017 4:20 PM 136 packages.config
-a—- 1/3/2017 4:18 PM 247 Program.cs

PS C:\EEDevOps> dir C:\EEDevOps\TeamFoundation\DevOps\Box\BoxJWT\BoxJWT1\packages

Directory: C:\EEDevOps\TeamFoundation\DevOps\Box\BoxJWT\BoxJWT1\packages

Mode LastWriteTime Length Name
—- ————- —— —-
d—– 1/3/2017 4:20 PM jose-jwt.2.1.0

A representative from Box is recommending one of the examples given by the creator of this Nuget package to help guide doing this in PowerShell:

var payload = new Dictionary()
{
    { "sub", "mr.x@contoso.com" },
    { "exp", 1300819380 }
};
var privateKey=new X509Certificate2("my-key.p12", "password", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet).GetRSAPrivateKey();
string token=Jose.JWT.Encode(payload, privateKey, JwsAlgorithm.RS256);

The question is, did I need to create the package above to be utilized in PowerShell, or is there a better way. And, How do I put the Nuget package to use and create the JWT signature in PowerShell? Is any of what I have done above useful?

January 5, 2017 at 5:53 pm

Thanks @beefarino,

Import-Module 'C:\EEDevOps\TeamFoundation\DevOps\Box\BoxJWT\BoxJWT1\bin\Debug\jose-jwt.dll'

$payload = New-Object 'System.Collections.Generic.Dictionary[[string].[System.Collections.Generic.List[string]]]'

$payload.add( "sub", "mr.x@contoso.com" );
$payload.add( "exp", 1300819380 );

$x509Flags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable + [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet;
$certificate = new-object 'System.Security.Cryptography.X509Certificates.X509Certificate2' "C:\01Servers\pem\private_key.pem", "password", $x509Flags
$privateKey = $certificate.GetRSAPrivateKey();
$token = [Jose.JWT]::Encode($payload, $privateKey, [Jose.JwsAlgorithm]::RS256);

I am not sure about:

$payload = New-Object 'System.Collections.Generic.Dictionary[[string].[System.Collections.Generic.List[string]]]'
New-Object : Cannot find type [System.Collections.Generic.Dictionary[[string].[System.Collections.Generic.List[string]]]]: verify that the assembly containing this type is loaded.
At line:1 char:12
+ $payload = New-Object 'System.Collections.Generic.Dictionary[[string] ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

$x509Flags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable + [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet;
$certificate = new-object 'System.Security.Cryptography.X509Certificates.X509Certificate2' "C:\01Servers\pem\private_key.pem", "password", $x509Flags
new-object : Exception calling ".ctor" with "3" argument(s): "Cannot find the requested object.
"
At line:2 char:16
+ ... rtificate = new-object 'System.Security.Cryptography.X509Certificates ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
+ FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

January 5, 2017 at 10:50 pm

Right now, this seem to be the part that has me fooled. I can't seem to get this method to work. I have tried creating this PEM cert with password and without. I have tried putting the path in quotes in the bracket, setting the type string and system.string to a variable, I tried putting the raw data in a variable, and the method doesn't' seem to work.
I used a Linux server and these commands one time to create the certs:
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private_key.pem -out public_key.pem

PS Cert:\CurrentUser\My> [string]$path = 'C:\01Servers\pem\private_key.pem'

PS Cert:\CurrentUser\My> $pfx = new-object System.Security.Cryptography.X509Certificates.X509Certificate2

PS Cert:\CurrentUser\My> $pfx.import($Path)
Exception calling "Import" with "1" argument(s): "Cannot find the requested object.
"
At line:1 char:3
+ $pfx.import($Path)
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : CryptographicException

PS Cert:\CurrentUser\My> $PublicCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($path)
New-Object : Exception calling ".ctor" with "1" argument(s): "Cannot find the requested object.
"
At line:1 char:17
+ ... ublicCert = New-Object System.Security.Cryptography.X509Certificates. ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
+ FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

January 24, 2017 at 12:23 am

Did you find a solution to this?

I just started on a powershell Box.com upload script that needs to use JWT. I was originally going to go down the same path as you and try to construct the JWT assertion using power shell, but then realized it was fairly complex to generate. I then was looking at just installing the the .Net SDK and attempting to call that from Powershell. If I followed your post correctly, it looks like you pulled from the jwt.io libraries and compiled a dll.

Is there a reason you didn't use the Box .Net sdk?

Did you figure out the issue with reading the private key in?

February 7, 2017 at 2:59 pm

Sorry for the slow response. I have been pulled away from this for a bit, but I still need to work this out. Bruce Payette reached out and said that it was a formatting issue, for the reason why I am unable to read the cert. He gave this as a reference: http://stackoverflow.com/questions/7400500/how-to-get-private-key-from-pem-file

I have gone in a couple of different directions, with which pull the dll libraries, I guess I chose the jwt.io because that is what one of Box's employees had used in his none PowerShell script. I am hoping to be able to spend some time on this over the next 3 to 5 days. I will need to get my head back into it, which will probably take 4 hours.

Box has also reached out to me and says they are going to work out the code in .NET soon.

March 16, 2018 at 4:16 pm

I found this post while searching for a native powershell solution for generating JWT's and came up fairly empty, with most of the code people posted required external libraries. I wrote a function pulling some info from here and there, using i255d's code as a good seed. I figured I should post it here as well for anyone else searching in the future.

The code works for my purposes and should be fairly simple to expand upon if you need additional claim types added.