I'm trying to create a JWT with JWT Scala using:
import pdi.jwt._
...
val claim = s"""{ \"exp\": $oneMinuteFromNow }"""
Jwt.encode(claim, key, JwtAlgorithm.ES512)
key is a String containing a private key in the form of
-----BEGIN EC PRIVATE KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAmG8JrpLz14+qUs7oxFX0pCoe90Ah
MMB/9ZENy8KZ+us26i/6PiBBc7XaiEi6Q8Icz2tiazwSpyLPeBrFVPFkPgIADyLa
T0fp7D2JKHWpdrWQvGLLMwGqYCaaDi79KugPo6V4bnpLBlVtbH4ogg0Hqv89BVyI
ZfwWPCBH+Zssei1VlgM=
-----END EC PRIVATE KEY-----
But I'm getting:
java.security.spec.InvalidKeySpecException: encoded key spec not recognized: wrong version for private key info
at org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi.engineGeneratePrivate(Unknown Source)
at org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi.engineGeneratePrivate(Unknown Source)
at java.security.KeyFactory.generatePrivate(KeyFactory.java:366)
at pdi.jwt.JwtUtils$.parsePrivateKey(JwtUtils.scala:109)
at pdi.jwt.JwtUtils$.sign(JwtUtils.scala:152)
at pdi.jwt.JwtUtils$.sign(JwtUtils.scala:159)
at pdi.jwt.JwtCore$class.encode(Jwt.scala:80)
at pdi.jwt.Jwt$.encode(Jwt.scala:23)
at pdi.jwt.JwtCore$class.encode(Jwt.scala:109)
at pdi.jwt.Jwt$.encode(Jwt.scala:23)
I've Googled everything I can think of, but I'm not sure what I'm doing wrong. Any help appreciated!
(above key is not a real key BTW)
I finally got this working.
openssl ecparam -genkey -name secp521r1 -noout -out ec512-key-pair.pem
Is the command I ued to generate the key. The code to sign the JWT:
object JwtGen {
def generateToken(keyPath: String) : String = {
if (keyPath.isEmpty) throw new java.io.IOException("keyPath is empty!")
val claim = s"""{ \"exp\": $oneMinuteFromNow }"""
Jwt.encode(claim, encodeKey(keyPath), JwtAlgorithm.ES512)
}
private def oneMinuteFromNow: Long = {
(System.currentTimeMillis / 1000) + 60
}
private def encodeKey(keyPath: String): PrivateKey = {
Security.addProvider(new BouncyCastleProvider)
val parser = new PEMParser(new InputStreamReader(new FileInputStream(keyPath)))
val pemObject = Option(parser.readObject)
pemObject match {
case Some(kp: PEMKeyPair) => convertFormat(kp)
case _ => throw new java.io.IOException(s"cannot parse pem file $keyPath")
}
}
private def convertFormat(pemKeyPair: PEMKeyPair): PrivateKey = {
val converter = new JcaPEMKeyConverter
val keyPair = converter.getKeyPair(pemKeyPair)
keyPair.getPrivate
}
}
Related
I am trying to generate a JWT token that has to be given to another system.
I have followed the below steps for doing so:
Created a Connect App. I have got consumer key using this App.
Created a self signed certificate. Downloaded the certificate(crt) file. Converted the crt file into key file using openSSL. I used command:
openssl req -newkey rsa:2048 -nodes -keyout SSO_Self_Signed.key -out SSO_Self_Signed.csr
SSO_Self_Signed is a label name of my certificate. The generated key file has private key in between -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY-----
3. I have created an APEX class that will generate JWT token using the creds we obtained in STEP 1 and STEP 2.
The code snippet of my code is also mentioned below:
jwtToken is generated on line 46.
When I am trying to validate the JWT in https://jwt.io/, I am getting invalid signature and I am not able to figure out the exact cause of the issue.
The inputs passed to the class includes - iss:mailaddress; sub:other system url; aud: consumer key from step1; privateKey: key from step2.
public class SSOJWTGenerator {
public static String generateJWT(String iss, String sub, String aud, String privateKey) {
String alg = 'RS256';
String typ = 'JWT';
// Create the JWT header
Map<String, Object> header = new Map<String, Object>{
'alg' => alg,
'typ' => typ
};
String encodedHeader = base64UrlEncode(JSON.serialize(header));
// Create the JWT claim set
Map<String, Object> claimSet = new Map<String, Object>{
'iss' => iss,
'sub' => sub,
'aud' => aud,
'exp' => String.valueOf(DateTime.now().getTime()/1000 + 300),
'iat' => String.valueOf(DateTime.now().getTime()/1000)
};
String encodedClaimSet = base64UrlEncode(JSON.serialize(claimSet));
// Create the signature
String input = encodedHeader + '.' + encodedClaimSet;
privateKey = privateKey.replace('-----BEGIN PRIVATE KEY-----', '');
privateKey = privateKey.replace('-----END PRIVATE KEY-----', '');
privateKey = privateKey.deleteWhitespace();
Blob privateKeyBlob = EncodingUtil.base64Decode(privateKey);
Blob signatureBlob = Crypto.sign('RSA-SHA256', Blob.valueOf(input), privateKeyBlob);
String signature = base64UrlEncode(signatureBlob);
// Combine the header, claim set, and signature to create the JWT token
String jwtToken = encodedHeader + '.' + encodedClaimSet + '.' + signature;
System.debug('jwtToken'+ jwtToken);
return jwtToken;
}
private static String base64UrlEncode(String input) {
// Replace + with -, / with _, and remove any trailing = signs
String base64 = EncodingUtil.base64Encode(Blob.valueOf(input));
base64 = base64.replace('+', '-').replace('/', '_').replaceAll('\\=+$', '');
return base64;
}
private static String base64UrlEncode(Blob input) {
// Replace + with -, / with _, and remove any trailing = signs
String base64 = EncodingUtil.base64Encode(input);
base64 = base64.replace('+', '-').replace('/', '_').replaceAll('\\=+$', '');
return base64;
}
}
1) How to get ECDSAPublicKey, ECDSAPrivateKey from DER encoded pem string
2) How to Marshal and Unmarshal ECDSAPublicKey,ECDSAPrivateKey using PKCS8 or PKCS1
3) I got ECPublicKey from ECPrivateKey , How to get ECDSAPublicKey directly from pem string instead of getting it from private key
String pemPkString = '''-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg6ZuZLImVj3CA2IE3
21G5mOK65KL71ost37cf2wrc7WChRANCAATMME1IAtwwoD53/IBUOY0H+ua6LKHj
yMhGPi/8dPz9h5FMyXHJQmXI3yEmW/tnyIRu6Z8PmAsVvKX8CnSC9fY6
-----END PRIVATE KEY-----''';
String pemPubString = '''-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzDBNSALcMKA+d/yAVDmNB/rmuiyh
48jIRj4v/HT8/YeRTMlxyUJlyN8hJlv7Z8iEbumfD5gLFbyl/Ap0gvX2Og==
-----END PUBLIC KEY-----''';
String msgToSign = "Hai this is test";
var domainParams = new ECDomainParameters("secp256k1");
void main() {
privateKey = parsePrivateKey(pemPkString);
publicKey = publicKeyFromPrivateKey(privateKey);
var privParams = new PrivateKeyParameter(privateKey);
var signParams =
() => new ParametersWithRandom(privParams, new NullSecureRandom());
generateSignature(new ECDSASigner(), signParams, msgToSign);
var verifyParams = () => new PublicKeyParameter(publicKey);
verifySign(new ECDSASigner(), verifyParams, msgToSign, signature);
}
ECPrivateKey parsePrivateKey(String pemPkString) {
List<int> encodedPkData = PemCodec(PemLabel.privateKey).decode(pemPkString);
BigInt d = decodeBigInt(encodedPkData);
return new ECPrivateKey(d, domainParams);
}
//Get ECPublicKey from ECPrivateKey
ECPublicKey publicKeyFromPrivateKey(ECPrivateKey privateKey) {
ECPoint Q = privateKey.parameters.G * privateKey.d;
return new ECPublicKey(Q, privateKey.parameters);
}
void generateSignature(
Signer signer, CipherParameters params(), String message) {
signer.reset();
signer.init(true, params());
signature =
signer.generateSignature(sha256.convert(utf8.encode(message)).bytes);
print("Signature ");
print(encodeSignatureToPem(signature));
}
void verifySign(Signer signer, CipherParameters params(), String message,
Signature signature) {
signer.reset();
signer.init(false, params());
var ok = signer.verifySignature(
sha256.convert(utf8.encode(message)).bytes, signature);
print("Verified $ok");
}
String encodeSignatureToPem(ECSignature signature) {
var topLevel = new ASN1Sequence();
topLevel.add(ASN1Integer(signature.r));
topLevel.add(ASN1Integer(signature.s));
var dataBase64 = base64Encode(topLevel.encodedBytes);
return dataBase64;
}
I want to sign a PDF with a self generated certificate. In this process I need a keystore and private key. The signing will be made with PDFBox by using the class CreateSignature()
To generate a keyStore with a self generated certificate I am using this:
public KeyStore generateSampleKeyStoreWith509Certificate() throws KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException, UnrecoverableEntryException {
X509Certificate cert;
PrivateKey caKey;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
keyPairGenerator.initialize(1024, new SecureRandom());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
caKey = keyPair.getPrivate();
Date notBefore = new Date();
Date notAfter = new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 365);
SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
X509v3CertificateBuilder newGen = new X509v3CertificateBuilder(new X500Name(issuer), serial, notBefore,
notAfter, new X500Name(subject), spkInfo);
ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider("BC")
.build(caKey);
X509CertificateHolder certHolder = newGen.build(sigGen);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream is1 = new ByteArrayInputStream(certHolder.getEncoded());
cert = (X509Certificate) cf.generateCertificate(is1);
is1.close();
} catch (OperatorCreationException | CertificateException | IOException | NoSuchProviderException
| NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, password);
keyStore.setCertificateEntry("SelfSigned", cert);
return keyStore;
}
The certificate is entered correctly, but shouldn't there be a key, too? Or am I wrong that the keystore should be holding a key?
I'm just figuring this sibject, so I'm thankful of every bit of help.
X509Certificate[] certChain = new X509Certificate[1];
certChain[0] = cert;
keyStore.setKeyEntry("SelfSigned",caKey, password, certChain);
Adding the above code, at the bottom, enters the privateKey previously created to the keystore. It seems most examples on the internet assume loading a keystore with privatekey already entered before.
I am generating an RSA keypair in pkcs11 keystore, it was storing into smartcard and i am generating pkcs10 request. when i download the equivalent certificate how can i store it into smartcard(without privatekey since the key is already stored into smartcard) since i dont have access to the private key in the pkcs11 keystore.
String wdtokenpath = "path to dll file";
String pkcs11ConfigSettings = "name = SmartCard\n" + "library =" + wdtokenpath;
byte[] pkcs11ConfigBytes = pkcs11ConfigSettings.getBytes();
ByteArrayInputStream confStream = new ByteArrayInputStream(pkcs11ConfigBytes);
Provider pkcs11Provider = null;
Class sunPkcs11Class = Class.forName("sun.security.pkcs11.SunPKCS11");
Constructor pkcs11Constr = sunPkcs11Class.getConstructor(
java.io.InputStream.class);
pkcs11Provider = (Provider) pkcs11Constr.newInstance(confStream);
CallbackHandler call = new TextCallbackHandler();
Subject token = new Subject();
AuthProvider aprov = (AuthProvider) pkcs11Provider;
aprov.login(token, call);
System.out.println("Login successfully");
KeyPairGenerator keyGen1 = KeyPairGenerator.getInstance("RSA", aprov);
keyGen1.initialize(2048);
KeyPair pair1 = keyGen1.generateKeyPair();
PublicKey publicKey1 = pair1.getPublic();
String sigAlg = "SHA1withRSA";
PKCS10 pkcs10 = new PKCS10(publicKey1);
Signature signature = Signature.getInstance("SHA1withRSA", pkcs11Provider);
signature.initSign(pair1.getPrivate());
It depends on what kind of smart card you have, or what kind of PKCS#11 device you have. The implementation may differ.
When you are using SunPKCS11, you can do it like this:
public boolean uploadCertificate(X509Certificate cert, String label, String id) {
CK_ATTRIBUTE[] certificate = new CK_ATTRIBUTE[9];
certificate[0] = new CK_ATTRIBUTE(PKCS11Constants.CKA_CLASS, PKCS11Constants.CKO_CERTIFICATE);
certificate[1] = new CK_ATTRIBUTE(PKCS11Constants.CKA_TOKEN, true);
certificate[2] = new CK_ATTRIBUTE(PKCS11Constants.CKA_PRIVATE, false);
certificate[3] = new CK_ATTRIBUTE(PKCS11Constants.CKA_LABEL, label.toCharArray());
certificate[4] = new CK_ATTRIBUTE(PKCS11Constants.CKA_SUBJECT, cert.getSubjectX500Principal().getEncoded());
certificate[5] = new CK_ATTRIBUTE(PKCS11Constants.CKA_ID, HexUtils.hexStringToByteArray(id));
certificate[6] = new CK_ATTRIBUTE(PKCS11Constants.CKA_ISSUER, cert.getIssuerX500Principal().getEncoded());
certificate[7] = new CK_ATTRIBUTE(PKCS11Constants.CKA_SERIAL_NUMBER, cert.getSerialNumber().toByteArray());
try {
certificate[8] = new CK_ATTRIBUTE(PKCS11Constants.CKA_VALUE, cert.getEncoded());
p11.C_CreateObject(hSession, certificate);
} catch (Exception e) {
logger.log(Level.SEVERE, "Upload Certificate Exception", e);
return false;
}
return true;
}
Or with IAIK PKCS#11 Wrapper:
// create certificate object template
X509PublicKeyCertificate pkcs11X509PublicKeyCertificate = new X509PublicKeyCertificate();
pkcs11X509PublicKeyCertificate.getToken().setBooleanValue(Boolean.TRUE);
pkcs11X509PublicKeyCertificate.getPrivate().setBooleanValue(Boolean.FALSE);
pkcs11X509PublicKeyCertificate.getLabel().setCharArrayValue("test".toCharArray());
pkcs11X509PublicKeyCertificate.getSubject().setByteArrayValue(cert.getSubjectX500Principal().getEncoded());
pkcs11X509PublicKeyCertificate.getId().setByteArrayValue(objectId);
pkcs11X509PublicKeyCertificate.getIssuer().setByteArrayValue(cert.getIssuerX500Principal().getEncoded());
// serial number should be an DER encoded ASN.1 integer
/*
INTEGER asn1Integer = new INTEGER(userCertificate.getSerialNumber());
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DerCoder.encodeTo(asn1Integer, buffer);
pkcs11X509PublicKeyCertificate.getSerialNumber().setByteArrayValue(buffer.toByteArray());
*/
// Netscape deviates from the standard here, for use with Netscape rather use
pkcs11X509PublicKeyCertificate.getSerialNumber().setByteArrayValue(cert.getSerialNumber().toByteArray());
pkcs11X509PublicKeyCertificate.getValue().setByteArrayValue(cert.getEncoded());
session.createObject(pkcs11X509PublicKeyCertificate);
The ID of the certificate object should be the same as the ID of generated keys.
A CA gives me a P12 which I want to convert to PEM inside a jvm.
Yes using openssl command works:
openssl pkcs12 -in jack.p12 -out jack.pem -nodes -clcerts
But then it gets ugly getting key and keystore passwords into the openssl program from java not to mention unsecure.
so BouncyCastle seems to be best crypto API for java...
How could it be done (convert P12 to PEM) using bouncy...
Here is an example:
private File createPem(final Certificate certP12, final String name) {
File file = new File(getFileName(name, "pem"));
FileWriter fileWriter;
try {
fileWriter = new FileWriter(file);
PEMWriter pemWriter = new PEMWriter(fileWriter);
pemWriter.writeObject(certP12);
pemWriter.flush();
pemWriter.close();
fileWriter.close();
} catch (IOException e) {
log.error("", e);
}
return file;
}
Load the certificate as Org.BouncyCastle.X509.X509Certificate
Convert to pem.
public static Org.BouncyCastle.X509.X509Certificate ImportCertFromPfx(string path, string password)
{
Pkcs12Store store = new Pkcs12StoreBuilder().Build();
store.Load(File.OpenRead(path), password.ToCharArray());
string alias = null;
foreach (string str in store.Aliases)
{
if (store.IsKeyEntry(str))
alias = str;
}
if (alias == null)
{
Console.WriteLine("alias is null");
}
else
Console.WriteLine(alias);
X509CertificateEntry certEntry = store.GetCertificate(alias);
Org.BouncyCastle.X509.X509Certificate x509cert = certEntry.Certificate;
return x509cert;
}
Org.BouncyCastle.X509.X509Certificate x509cert = ImportCertFromPfx(p12path, p12password);
StringBuilder CertPem = new StringBuilder();
PemWriter CSRPemWriter = new PemWriter(new StringWriter(CertPem));
CSRPemWriter.WriteObject(x509cert);
CSRPemWriter.Writer.Flush();
//get Cert text
var CertPemText = CertPem.ToString();
Console.WriteLine(CertPemText);