Skip to content

Commit

Permalink
Verify JWT
Browse files Browse the repository at this point in the history
  • Loading branch information
jgrateron committed Jun 25, 2024
1 parent fe4a278 commit 0eee746
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 9 deletions.
5 changes: 5 additions & 0 deletions kse/src/main/java/org/kse/gui/actions/SignJwtAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.Certificate;
import java.util.Base64;

import javax.swing.ImageIcon;

Expand Down Expand Up @@ -88,14 +90,17 @@ protected void doAction() {

Provider provider = history.getExplicitProvider();
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray());
Certificate cert = keyStore.getCertificate(alias);
KeyPairType keyPairType = KeyPairUtil.getKeyPairType(privateKey);

DSignJwt dSignJwt = new DSignJwt(frame, keyPairType, privateKey);
dSignJwt.setLocationRelativeTo(frame);
dSignJwt.setVisible(true);
if (dSignJwt.isOk()) {
byte [] dataPublic = cert.getPublicKey().getEncoded();
SignedJWT jwt = signJwt(dSignJwt, privateKey, provider);
DViewJwt dialog = new DViewJwt(frame, jwt);
dialog.setPublicKey(Base64.getEncoder().encodeToString(dataPublic));
dialog.setLocationRelativeTo(frame);
dialog.setVisible(true);
}
Expand Down
139 changes: 130 additions & 9 deletions kse/src/main/java/org/kse/gui/dialogs/DViewJwt.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,32 +26,51 @@
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.text.ParseException;
import java.util.Base64;
import java.util.ResourceBundle;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;

import org.kse.crypto.CryptoException;
import org.kse.crypto.filetype.CryptoFileType;
import org.kse.crypto.filetype.CryptoFileUtil;
import org.kse.crypto.publickey.OpenSslPubUtil;
import org.kse.gui.CursorUtil;
import org.kse.gui.JEscDialog;
import org.kse.gui.LnfUtil;
import org.kse.gui.PlatformUtil;
import org.kse.gui.error.DError;
import org.kse.utilities.DialogViewer;

import com.nimbusds.jose.Header;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.Payload;
import com.nimbusds.jose.crypto.ECDSAVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.shaded.gson.Gson;
import com.nimbusds.jose.shaded.gson.GsonBuilder;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTParser;
import com.nimbusds.jwt.SignedJWT;

import net.miginfocom.swing.MigLayout;

Expand All @@ -72,8 +91,14 @@ public class DViewJwt extends JEscDialog {
private JLabel jlEncoded;
private JTextArea jtaEncoded;
private JScrollPane jspEncoded;
private JLabel jlPublicKey;
private JTextArea jtaPublicKey;
private JScrollPane jspPublicKey;

private JButton jbCopy;
private JButton jbOK;
private JButton jbVerify;
private JPanel jpButtons;

private JWT jwt;

Expand All @@ -84,7 +109,7 @@ public class DViewJwt extends JEscDialog {
* @param jwt The encoded JWT
*/
public DViewJwt(JFrame parent, JWT jwt) {
super(parent, Dialog.ModalityType.DOCUMENT_MODAL);
super(parent, Dialog.ModalityType.MODELESS);
setTitle(res.getString("DViewJwt.Title"));
this.jwt = jwt;
initComponents();
Expand Down Expand Up @@ -117,36 +142,77 @@ private void initComponents() {

jtaEncoded = new JTextArea();
jtaEncoded.setFont(new Font(Font.MONOSPACED, Font.PLAIN, LnfUtil.getDefaultFontSize()));
jtaEncoded.setEditable(false);
jtaEncoded.setEditable(true);
jtaEncoded.setLineWrap(true);
jtaEncoded.setToolTipText(res.getString("DViewJwt.jtaEncoded.tooltip"));
jtaEncoded.addFocusListener(new FocusListener() {

@Override
public void focusGained(FocusEvent arg0) {
}

@Override
public void focusLost(FocusEvent arg0) {
try {
JWT jwtVeri = JWTParser.parse(jtaEncoded.getText());
if (!jwt.equals(jwtVeri)) {
jwt = jwtVeri;
populateDialog();
}
} catch (ParseException e) {
jtaPayload.setText("");
jtaHeader.setText("");
}
}
});

jspEncoded = PlatformUtil.createScrollPane(jtaEncoded, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);

jbCopy = new JButton(res.getString("DViewJwt.jbCopy.text"));
jlPublicKey = new JLabel(res.getString("DViewJwt.jlPublicKey.text"));

jtaPublicKey = new JTextArea();
jtaPublicKey.setFont(new Font(Font.MONOSPACED, Font.PLAIN, LnfUtil.getDefaultFontSize()));
jtaPublicKey.setEditable(true);
jtaPublicKey.setLineWrap(true);
jtaPublicKey.setToolTipText(res.getString("DViewJwt.jtaPublicKey.tooltip"));

jspPublicKey = PlatformUtil.createScrollPane(jtaPublicKey, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);

jbCopy = new JButton(res.getString("DViewJwt.jbCopy.text"));
jbCopy.setToolTipText(res.getString("DViewJwt.jbCopy.tooltip"));
PlatformUtil.setMnemonic(jbCopy, res.getString("DViewJwt.jbCopy.mnemonic").charAt(0));

jbOK = new JButton(res.getString("DViewJwt.jbOK.text"));

jbVerify = new JButton(res.getString("DViewJwt.jbVerify.text"));
jbVerify.setToolTipText(res.getString("DViewJwt.jbVerify.tooltip"));
// layout
Container pane = getContentPane();
pane.setLayout(new MigLayout("insets dialog", "[right]unrel[]", "[]unrel[]"));
pane.add(jlHeader, "");
pane.add(jspHeader, "width 400lp:400lp:400lp, height 100lp:100lp:100lp, wrap");
pane.add(jspHeader, "width 400lp:400lp:400lp, height 150lp:150lp:150lp");
pane.add(jlPayload, "");
pane.add(jspPayload, "width 400lp:400lp:400lp, height 200lp:200lp:200lp, wrap");
pane.add(jspPayload, "width 400lp:400lp:400lp, height 150lp:150lp:150lp, wrap");
pane.add(jlEncoded, "");
pane.add(jspEncoded, "width 400lp:400lp:400lp, height 200lp:200lp:200lp, wrap");
pane.add(jbCopy, "spanx");
pane.add(new JSeparator(), "spanx, growx, wrap unrel:push");
pane.add(jspEncoded, "width 400lp:400lp:400lp, height 150lp:150lp:150lp");
pane.add(jlPublicKey, "");
pane.add(jspPublicKey, "width 400lp:400lp:400lp, height 150lp:150lp:150lp, wrap");

jpButtons = PlatformUtil.createDialogButtonPanel(jbCopy, jbVerify, "insets 0");

pane.add(jpButtons, "right, spanx");
pane.add(new JSeparator(), "spanx, growx, wrap unrel:push");

pane.add(jbOK, "spanx, tag ok");

// actions

jbOK.addActionListener(evt -> okPressed());

jbVerify.addActionListener(evt -> verifyPressed());

jbCopy.addActionListener(evt -> {
try {
CursorUtil.setCursorBusy(DViewJwt.this);
Expand Down Expand Up @@ -194,6 +260,61 @@ private void okPressed() {
closeDialog();
}

private static byte[] decodeIfBase64(String data) {
byte[] dataAsBytes = data.getBytes();

// first handle base64 encoded binary data
try {
dataAsBytes = Base64.getDecoder().decode(data.trim());
} catch (IllegalArgumentException e) {
// was not valid b64
}
return dataAsBytes;
}

private void verifyPressed() {
String data = jtaPublicKey.getText();
if (data.isEmpty()) {
JOptionPane.showMessageDialog(this, res.getString("DViewJwt.InvalidPublicKey.message"),
res.getString("DViewJwt.Verify.Title"), JOptionPane.ERROR_MESSAGE);
return;
}
byte[] dataAsBytes = decodeIfBase64(data);
try {
CryptoFileType fileType = CryptoFileUtil.detectFileType(dataAsBytes);
if (fileType != CryptoFileType.OPENSSL_PUB) {
JOptionPane.showMessageDialog(this, res.getString("DViewJwt.InvalidPublicKey.message"),
res.getString("DViewJwt.Verify.Title"), JOptionPane.ERROR_MESSAGE);
return;
}
PublicKey publicKey = OpenSslPubUtil.load(dataAsBytes);
var signedJWT = SignedJWT.parse(jwt.serialize());
JWSVerifier verifier = null;
if (publicKey instanceof ECPublicKey) {
verifier = new ECDSAVerifier((ECPublicKey) publicKey);
} else if (publicKey instanceof RSAPublicKey) {
verifier = new RSASSAVerifier((RSAPublicKey) publicKey);
} else {
JOptionPane.showMessageDialog(this, res.getString("DViewJwt.InvalidPublicKey.message"),
res.getString("DViewJwt.Verify.Title"), JOptionPane.ERROR_MESSAGE);
return;
}
if (signedJWT.verify(verifier)) {
JOptionPane.showMessageDialog(this, res.getString("DViewJwt.SignatureVerified.message"),
res.getString("DViewJwt.Verify.Title"), JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(this, res.getString("DViewJwt.InvalidSignature.message"),
res.getString("DViewJwt.Verify.Title"), JOptionPane.ERROR_MESSAGE);
}
} catch (IOException | CryptoException | JOSEException | ParseException ex) {
DError.displayError(this, ex);
}
}

public void setPublicKey(String publicKey) {
jtaPublicKey.setText(publicKey);
}

private void closeDialog() {
setVisible(false);
dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,14 @@ DViewJwt.jlPayload.text = Payload:
DViewJwt.jtaEncoded.tooltip = JSON Web Token
DViewJwt.jtaHeader.tooltip = JWT header
DViewJwt.jtaPayload.tooltip = JWT payload
DViewJwt.jlPublicKey.text = Public key:
DViewJwt.jtaPublicKey.tooltip = Public key
DViewJwt.Verify.Title = Verify signature
DViewJwt.jbVerify.text = Verify
DViewJwt.jbVerify.tooltip = Verify signature
DViewJwt.InvalidPublicKey.message = Invalid public key
DViewJwt.SignatureVerified.message = Signature verified
DViewJwt.InvalidSignature.message = Invalid signature

DViewKeyPair.ViewCertificateDetails.Title = Certificate Details for key pair
DViewKeyPair.ViewPrivateKeyDetails.Title = Private Key Details for key pair
Expand Down

0 comments on commit 0eee746

Please sign in to comment.