I'm trying to create a JWT with joken
privKey = """
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIPaIrqi+I+znfdsteEXELr2J1e+qC72KNam6fx40pYvi
-----END PRIVATE KEY-----
"""
signer = Joken.Signer.create("Ed25519", %{"pem" => privKey})
Joken.generate_and_sign!(%{}, %{"name" => "John Doe"}, signer)
I receive the error
** (FunctionClauseError) no function clause matching in :jose_jwk_kty_ec.parameters_to_crv/1
The following arguments were given to :jose_jwk_kty_ec.parameters_to_crv/1:
# 1
:ed25519
(jose 1.11.2) src/jwk/jose_jwk_kty_ec.erl:410: :jose_jwk_kty_ec.parameters_to_crv/1
(jose 1.11.2) src/jwk/jose_jwk_kty_ec.erl:389: :jose_jwk_kty_ec.jws_alg_to_digest_type/2
(jose 1.11.2) src/jwk/jose_jwk_kty_ec.erl:199: :jose_jwk_kty_ec.sign/3
(jose 1.11.2) src/jws/jose_jws.erl:311: :jose_jws.sign/4
(jose 1.11.2) src/jwt/jose_jwt.erl:173: :jose_jwt.sign/3
(joken 2.5.0) lib/joken/signer.ex:128: Joken.Signer.sign/2
(joken 2.5.0) lib/joken.ex:361: Joken.encode_and_sign/3
iex:6: (file)
What is causing the error, I had a look at the code on https://github.com/joken-elixir/joken/issues/214 to try and fix it but couldn't.
Related
const fs = require("fs")
const rsaPemToJwk = require('rsa-pem-to-jwk')
const pem = fs.readFileSync('./certs/private.pem');
var jwk = rsaPemToJwk(pem, {use: 'sig'}, 'public');
console.log(jwk)
this result:
Node.js v18.12.1
PS D:\ppp\JWKS-tutorial\auth-server> node covermtion.js
undefined
I added the private key instead of asking for it but it gave me the same result
const fs = require("fs")// fs.readFileSync('./certs/private.pem');
const rsaPemToJwk = require('rsa-pem-to-jwk')
const pem =
"-----BEGIN RSA PRIVATE KEY-----\n\
MIIG4wIBAAKCAYEAxzZZa6MCLR/rYmQ9BRKK/tpJ736j8QFR3RrUC8TZKvhx6QIB\
CULWvehctkjxkqREmfPbtQ1EkeoAfT68AeRd5CYpeucQrL1auZa6A0Wfjn5kRLIf\
5U8e7G+0eOd93GZ7BOLV5OgfkgX5Q4RYxAiHKc6zKqmFIBgj57oM00TcHF0lw7rv\
wvLzF3lDpEkS9ib/XlqpyHBgKrcjdVhD5V6GAgEv6TJpd/HtrV4CKC3prZIIilAF\
IUvVkHKT/F91XE6zo7IAyoFB+z/bTut7RQ4Eg1U3CtFxTjnBYxmPMIrqoUJDSdGt\
hBoG1uprNzdSupY1dIwpVDEXWqaOnzePsOYQnLgnV5sPgZOC85UgszfUu+bHuFtb\
tc+WZ7zBYYJzHGwraW/Jq5r9QwJ+8pJTh7ksS2ugV86s6Jxdg0dk6wMSd0NwQqfO\
iehqNw7w/TbayrtXl7ZCtHCErtMsnJEwVFRYsW7XB85piRcOEih30Yrx40oLfYUD\
mpAfdrUSuaAN9AR3AgMBAAECggGBAJVDmtK0q//Fp+fsNXBkiXMVekqRuCLvmzgx\
TuqWjyXMTAWLs90NkLtwrG8EzsTSgnhcKKZLD7LJA57Ub6h3RQaatX/aPDtCEgV2\
819aPohVGaoDUTTPCNudgzsIMN0AiHKLyDqg1m7iPPei5aPUw/nGX6a6p/35OIX7\
\n -----END RSA PRIVATE KEY-----";
var jwk = rsaPemToJwk(pem, {use: 'sig'}, 'public');
console.log(jwk)
this result:
Node.js v18.12.1
PS D:\ppp\JWKS-tutorial\auth-server> node covermtion.js
undefined
I want to transfer the private key via rsapemtojwk which gives me unknown result
I am using Scala to generate JWT using RS256 algorithm and private keys:
val jwtPayload = s"""{
| "exp": $time,
| "iss": "$orgId",
| "sub": "$technicalAccountId",
| "aud": "${imsExp}/c/${clientId}",
| "${imsExp}/s/${metaScope}": true
|}""".stripMargin
println(jwtPayload)
val token = Jwts.builder()
.setPayload(jwtPayload)
.signWith(SignatureAlgorithm.RS256,privateKey.getBytes("UTF-8"))
But this fails with the error:
Key bytes may only be specified for HMAC signatures. If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.
at io.jsonwebtoken.lang.Assert.isTrue(Assert.java:38)
But the same code works well in javascript:
const jwtPayload = {
exp: Math.round(300 + Date.now() / 1000),
iss: secrets.org,
sub: secrets.id,
aud: `${secrets.imsEndpoint}/c/${secrets.technicalAccount.clientId}`,
[`${secrets.imsEndpoint}/s/${secrets.metascopes}`]: true
};
let token;
try {
token = jwt.sign(
jwtPayload,
{ key: secrets.privateKey},
{ algorithm: 'RS256' }
);
console.log(token);
} catch (tokenError) {
return Promise.reject(tokenError);
}
I am unable to identify two things:
How to pass passphrase?
How to get rid of below error:
Key bytes may only be specified for HMAC signatures. If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.
at io.jsonwebtoken.lang.Assert.isTrue(Assert.java:38)
When I remove .getBytes method, I recieve a new error:
Exception in thread "main" java.lang.IllegalArgumentException: Base64-encoded key bytes may only be specified for HMAC signatures. If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.
I'm trying to reproduce a decoding of a JWE starting from jwt.io as an example and translating into code by using library jose4j
From site jwt.io I have the following:
HEADER:
{
"alg": "HS256"
}
PAYLOAD:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
VERIFY SIGNATURE:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
Fdh9u8rINxfivbrianbbVT1u232VQBZYKx1HGAGPt2I
)
the secret base64 is not encoded.
Now I try to reproduce the situation with jose4j and then having as a result the same value on the encoded field, which is:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.jOJ7G4oijaDk9Tr4ntAXczd6PlI4oVvBU0_5cf7oaz4
Then:
Key key = new HmacKey("Fdh9u8rINxfivbrianbbVT1u232VQBZYKx1HGAGPt2I".getBytes(StandardCharsets.UTF_8));
JsonWebEncryption jwe = new JsonWebEncryption();
String payload = Json.createObjectBuilder()
.add("sub", "1234567890")
.add("name", "John Doe")
.add("iat", "1516239022")
.build()
.toString();
jwe.setPayload(payload);
jwe.setHeader("alg", "HS256");
jwe.setKey(key);
String serializedJwe = jwe.getCompactSerialization();
System.out.println("Serialized Encrypted JWE: " + serializedJwe);
However I get this error:
org.jose4j.lang.InvalidAlgorithmException: HS256 is an unknown, unsupported or unavailable alg algorithm (not one of [RSA1_5, RSA-OAEP, RSA-OAEP-256, dir, A128KW, A192KW, A256KW, ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW, PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW, A128GCMKW, A192GCMKW, A256GCMKW]).
HS256 is a JWS algorithm so you'd need to use JsonWebSignature rather than JsonWebEncryption to accomplish what it looks like you're trying to do.
The RFC7518 has a list of algorithms values used in JWT. However there is no value for EdDSA, such as Ed25519. Also Ed25519 is not accepted as a valid value when verifying in Jose. What is the correct alg value for Ed25519?
ED25519 is a EdDSA (Edwards-curve DSA) signature scheme. See also RFC8037 and RFC8032.
According to the jose documentation alg needs to be set to EdDSA:
JWS Algorithm: Edwards-curve DSA
alg: EdDSA
EdDSAis also listed in the section JSON Web Signature and Encryption Algorithms of the IANA Registry (thanks #Florent Morselli for the hint)
Here I show an example how to generate a ed25519 keypair and a signed token in Node.js with jose and crypto and then verify the token with the public key in Python:
Generate key pair and token:
const { SignJWT } = require('jose/jwt/sign')
const { generateKeyPairSync } = require('crypto')
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
console.log(publicKey.export({format:'pem',type:'spki'}))
console.log(privateKey.export({format:'pem',type:'pkcs8'}))
const jwt = await new SignJWT({ 'id': 1 })
.setProtectedHeader({ alg: 'EdDSA' })
.setExpirationTime('2h')
.sign(privateKey)
console.log(jwt)
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA7fySb/9h7hVH8j1paD5IoLfXj4prjfNLwOPUYKvsTOc=
-----END PUBLIC KEY-----
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIIJtJBnTuKbIy5YjoNiH95ky3DcA3kRB0I2i7DkVM6Cf
-----END PRIVATE KEY-----
eyJhbGciOiJFZERTQSJ9.eyJpZCI6MX0.RAxBAQPFOxrCfgqb56eaAz9u2lByj-WEO-
JWgJH3Cyx1o1Hwjn1pA2M4NgJeob9vb2Oaw4FOeYFr6_33XMTnAQ
the decoded token header:
{
"alg": "EdDSA"
}
Verify the token in Python with PyJWT:
import jwt
public_key = """-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA7fySb/9h7hVH8j1paD5IoLfXj4prjfNLwOPUYKvsTOc=
-----END PUBLIC KEY-----"""
token = 'eyJhbGciOiJFZERTQSJ9.eyJpZCI6MX0.RAxBAQPFOxrCfgqb56eaAz9u2lByj-WEO-JWgJH3Cyx1o1Hwjn1pA2M4NgJeob9vb2Oaw4FOeYFr6_33XMTnAQ'
decoded = jwt.decode(token, public_key, algorithms='EdDSA', verify=True)
print(decoded)
Note: jwt.io currently doesn't support the EdDSA/ED25519 algorithm, so you can't verify the token on that site. I also don't know about any other JWT website that can verify EdDSA signed tokens.
Update: I have rewritten the sample code and the CSR is very close to the actual openssl created CSR (only missing the CA:False extended attribute)
I have a CA already and would like to dynamically generate user certs for enrolling authorized devices with phpseclib.
I know the logic is a little cloudy, this code was pieced together from a variety of different examples:
<?php
$USERNAME = "tester";
$DEVICENAME = "command";
$PASSWORD = "test";
$ID = 123;
require_once("config.inc.php"); // Sets defined paths to CA cert and key
require_once("File/X509.php");
require_once("Crypt/RSA.php");
// Setup our CA
$CA = array(); // Store our certificate authority information
$CA["key" ] = new Crypt_RSA();
$CA["key" ]->loadKey( file_get_contents(CAKEY) ); // Load our CA key to sign with
$CA["asciicert" ] = file_get_contents(CACERT);
$CA["cert" ] = new File_X509();
$CA["cert" ]->loadX509( $CA["asciicert"] ); // Load our CA cert and public key
$CA["cert" ]->setPrivateKey($CA["key"]);
// Create a new keypair
$DEVICE = array();
$DEVICE["keys" ] = new Crypt_RSA();
$DEVICE["keypair" ] = $DEVICE["keys"]->createKey(2048);
// Save our private key
$DEVICE["privkey" ] = new Crypt_RSA();
$DEVICE["privkey" ]->loadKey($DEVICE["keypair"]["privatekey"]);
// Save our public key
$DEVICE["pubkey" ] = new Crypt_RSA();
$DEVICE["pubkey" ]->loadKey($DEVICE["keypair"]["publickey"]);
// Create a new CSR
$DEVICE["csr" ] = new File_X509();
$DEVICE["csr" ]->setPrivateKey($DEVICE["privkey"]);
$DEVICE["csr" ]->setPublicKey ($DEVICE["pubkey" ]);
$DEVICE["csr" ]->setDN("C=SS, ST=obscure, L=obscure, O=secure, OU=networksecurity, CN={$USERNAME}#{$DEVICENAME}/emailAddress={$USERNAME}#{$DEVICENAME}");
// Sign the CSR
$DEVICE["signedcsr" ] = $DEVICE["csr"]->signCSR("sha256WithRSAEncryption");
$DEVICE["asciicsr" ] = $DEVICE["csr"]->saveCSR($DEVICE["signedcsr"]);
// Update the CSR with attributes
$DEVICE["cert" ] = new File_X509();
$DEVICE["cert" ]->loadX509( $DEVICE["asciicsr"] ); // Now load it back up so we can set extended attributes
$DEVICE["cert" ]->setPublicKey ($DEVICE["pubkey" ]);
$DEVICE["cert" ]->setStartDate("-1 day"); // Make it valid from yesterday...
$DEVICE["cert" ]->setEndDate("+ 5 years"); // Set a 5 year expiration on all device certs
$DEVICE["cert" ]->setSerialNumber($ID, 10); // Use our ID number in the DB, base 10 (decimal) notation
// These wont work, ill fix this later...
$DEVICE["cert" ]->setExtension("id-ce-basicConstraints", array("cA" => false ), 1 );
$DEVICE["cert" ]->setExtension("id-ce-keyUsage" , array("keyEncipherment" ,"nonRepudiation" ,"digitalSignature" ), 1 );
$DEVICE["cert" ]->setExtension("id-ce-extKeyUsage" , array("id-kp-emailProtection" ,"id-kp-clientAuth" ), 1 );
$DEVICE["cert" ]->setExtension("netscape-cert-type" , array("Email" ,"SSLClient" ), 1 );
// Finally have the CA sign the updated CSR
$DEVICE["signedcert"] = $DEVICE["cert"]->sign($CA["cert"], $DEVICE["cert"], "sha256WithRSAEncryption"); // Sign the new certificate with our CA
$DEVICE["asciicert" ] = $DEVICE["cert"]->saveX509($DEVICE["signedcert"]); // Ascii our certificate for presentation
print <<<END
User Public key:\n{$DEVICE["keypair"]["publickey"]}\n
User Private key:\n{$DEVICE["keypair"]["privatekey"]}\n
User CSR:\n{$DEVICE["asciicsr"]}\n
CA Cert:\n{$CA["asciicert"]}\n
User Certificate:\n{$DEVICE["asciicert"]}\n
END;
?>
Below is some sample output from this program:
...
User CSR:
-----BEGIN CERTIFICATE REQUEST-----
MIIC1DCCAb4CAQAwgZQxCzAJBgNVBAYMAlNTMRAwDgYDVQQIDAdvYnNjdXJlMRAw
DgYDVQQHDAdvYnNjdXJlMQ8wDQYDVQQKDAZzZWN1cmUxGDAWBgNVBAsMD25ldHdv
cmtzZWN1cml0eTEXMBUGA1UEAwwOdGVzdGVyQGNvbW1hbmQxHTAbBgkqhkiG9w0B
CQEMDnRlc3RlckBjb21tYW5kMIIBIDALBgkqhkiG9w0BAQEDggEPADCCAQoCggEB
ANzsEUPULfmkbDK2DLTWUbHxpqbxiQ0WF5vuUOXutcdJADG9uExyllRtnPmUq3yV
GhfF/jbKKwXrDMTc9JLRoPRjbCesHQq9p+WU2pgzHWOGne3b2SNB6ISSVUbmZwRd
3Uu78u2EIPw2yB/YAdMaSipuLnA4jbGwWHtMXWSr9g0en+CkJ0cHN3P0GAnDR7BF
2pq88si5Who9Dvn/Mo3npMZiAmD5D7qwD88mVFMC1j4q1xnqHi2X19Xz9+xgWZ1h
IdedpWh+v0i2kHE73Csu0Jiczxb28zdm+MjBEYGZ6X6LzCYHI5kx2wDKsxuxraMf
sY0QS/kATiTzSJMcWzxR82UCAwEAATALBgkqhkiG9w0BAQsDggEBABuThxlknbCS
Fzd8B+eM98uFW6YQmfp5js/S2+Cor3+btGi2d/siXDGusW7ceOEv4WLRikrv+0tt
rHNzndhl77ukSikA0H0VMUqNFYIL1N//W2nDhphYKWwKWHGoQ+/ZH30aLgw43iNz
IzWAj2d39bGEqAvGPzU6BDpm8o1ucMoJaqUAHNov69Ro/n+rb0k3ouuTqjewF781
lQFHMPhUY4j2JbGKpah3qPGvAgc44JR6VmG6Rh9nVhcZz5szit8K1UTgvIlSl7EH
7ggBHQl67kON19JKlKneJVsD36tczkEhuDGU5Dv4TH7SLiKwIRYHulD3bmqg2/Nq
sGAxVviaKBA=
-----END CERTIFICATE REQUEST-----
CA Cert:
-----BEGIN CERTIFICATE-----
MIIDVTCCAj2gAwIBAgICEA8wDQYJKoZIhvcNAQELBQAwSTELMAkGA1UEBhMCU1Mx
EjAQBgNVBAgMCVNlY3VyZSBDQTESMBAGA1UEBwwJU2VjdXJlIENBMRIwEAYDVQQK
DAlTZWN1cmUgQ0EwHhcNMTUwMjAxMTkzNjA2WhcNMTYwMjAxMTkzNjA2WjBPMQsw
CQYDVQQGEwJTUzEPMA0GA1UECAwGU2VjdXJlMQ8wDQYDVQQKDAZTZWN1cmUxHjAc
BgNVBAMMFW5ldHdvcmtzZWN1cml0eS5uaW5qYTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAN7PJWsyd3Hn7q5/Y4N9Dcpvtip/hiSEFwrkl4UWd+bD7CGz
wQjyZziVAj7mXjgTrPCmMzwV/aRtT2WM7l1vI8WV0swsTEidvZF+EDEAujnadMxr
8JWVC+ljYvhy7nIDRYpPwkKSBWpIF1UFaG8MduHxBtqlRlOJoIDQmJkLQO5fV/kv
cujct4myMhar6TPx52xWX0FLt0B3Rn04Rb0InstyDY0NtrTMsgSq32rj3sijTCAG
WDsnxNO+jsC7uFAjjldcWnqBs7of+sVb7TPiEsq/5adE6G50ctqW8H7JpY+SFZzG
Y+wPRUxJZsYq4qt/rkEv7ldtsbhHD6wO4I61eksCAwEAAaNBMD8wDAYDVR0TBAUw
AwEB/zALBgNVHQ8EBAMCBeAwIgYDVR0RBBswGYIXKi5uZXR3b3Jrc2VjdXJpdHku
bmluamEwDQYJKoZIhvcNAQELBQADggEBAMsXyUX95AkQKadbaZ1XEWoayElWtKUc
dRB15XDJ7xoWGQo/fDYebXOJMPffIQoOGtRZcYtPaVjr3PMUCaxIAUvdmO3UMfLh
M8kQhYBzyEKw+SRwcUHmKbU8Tz5AolL1qjoNm5SWBV9RbFj2TRcR27v/apmhIR+K
6KKbcIXklKhhBPacJL7NwAgibb8Ip4OtxSuzarydddPryAwTwUSJNlmozRAx7dFk
xLkLMQMqEtW7BmJqU+YUczddYvbsxmYqfaChM/TBo7VZd84RlWoXOqqfon6JGLWN
5lN86iVnfXeGLbhLt5GKWB6e4rUbiMAqmGYO6Cd2BMFRtlp9IYZIBSY=
-----END CERTIFICATE-----
User Certificate:
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
I find it really confusing that the signed certificate is blank. The error checking in place seems to make me think it would simply return FALSE if there was a problem, but getting blank output between the ----- lines makes me wonder what is going on.
From your code:
$DEVICE["cert" ]->loadX509( $DEVICE["asciicsr"] );
Try this:
$DEVICE["cert" ]->loadCSR( $DEVICE["asciicsr"] );