Now generating RSA keys.

This commit is contained in:
Yvan 2025-02-04 00:03:08 +00:00
parent b218591ea7
commit 2d026711c1
3 changed files with 62 additions and 10 deletions

View file

@ -33,6 +33,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.shell:spring-shell-starter'
implementation 'org.postgresql:postgresql'
implementation 'org.bouncycastle:bcpkix-jdk18on:1.76'
implementation 'org.thymeleaf:thymeleaf-spring5:3.1.2.RELEASE'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'

View file

@ -5,6 +5,12 @@ import lombok.Setter;
import lombok.AccessLevel;
import lombok.Data;
import java.time.Instant;
import java.security.KeyPair;
import java.security.PublicKey;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.util.io.pem.PemObject;
import java.io.StringWriter;
import java.io.IOException;
/**
* POJO for our Bot...
@ -22,8 +28,35 @@ public class Bot {
@Setter(AccessLevel.NONE)
Instant published; // "2025-01-24T00:00:00Z",
/**
* The RSA key for the bot... TODO: thinking, with separatation of data maybe
* the private key should not be "exposed" here and instead we provide only
* services to do key operations... keeping priv key data "secret" to the model
*/
@Setter(AccessLevel.NONE)
String publicKeyPem; // "-----BEGIN PUBLIC KEY-----\\nMI [...] AB\\n-----END PUBLIC KEY-----"
KeyPair keyPair;
/**
* Get the string representation of the public key as required for the
* ActivityPub key JSON.
* "-----BEGIN PUBLIC KEY-----\\nMI [...] AB\\n-----END PUBLIC KEY-----"
*/
String getPublicKeyPEMString() {
// Note: for performance it could be worth storing this in the db, or caching it in some way
PublicKey pk = keyPair.getPublic();
StringWriter w = new StringWriter();
PEMWriter pw = new PEMWriter(w);
try {
pw.writeObject(new PemObject("PUBLIC KEY", pk.getEncoded()));
pw.flush();
pw.close();
// TODO: flush and close with stringwriter?
// Can an exception be thrown here using stringwriter?
} catch (IOException e) {
// FIXME: ?
}
return w.toString();
}
@Setter(AccessLevel.NONE)
String type = "Person";
@ -36,17 +69,17 @@ public class Bot {
@Override
public String toString() {
return "Bot: " + this.username;
return "Bot: " + this.username + "\nKey: " + this.getPublicKeyPEMString() + "\n";
}
Bot() {
}
Bot( String username, String name, String summary, Instant published, String publicKeyPem, String type, boolean manuallyApproveFollowers, boolean indexable ) {
Bot( String username, String name, String summary, Instant published, KeyPair keyPair, String type, boolean manuallyApproveFollowers, boolean indexable ) {
this.username = username;
this.name = name;
this.summary = summary;
this.published = published;
this.publicKeyPem = publicKeyPem;
this.keyPair = keyPair;
this.type = type;
this.manuallyApproveFollowers = manuallyApproveFollowers;
this.indexable = indexable;

View file

@ -12,12 +12,18 @@ import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.security.PublicKey;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import lombok.extern.slf4j.Slf4j;
/**
* Our core Bot (aka user) data as stored persistently in the database.
* Our core Bot (aka user, or 'actor') data as stored persistently in the database.
* This is all the key non-derived data as required by the ActivityPub
* specification. NOTE: This is not a comprehensive implementation!
* specification. NOTE: This is not a comprehensive AP 'actor' implementation!
*/
@Slf4j
@Entity
@Table(name = "bot")
@EntityListeners(AuditingEntityListener.class)
@ -68,8 +74,8 @@ public class BotModel {
// TODO how and where do we generate this beastie?!?!
@Column(nullable=true,unique=false) // FIXME: this isn't true, just easy for now
@Getter private String publicKeyPem; // "-----BEGIN PUBLIC KEY-----\\nMI [...] AB\\n-----END PUBLIC KEY-----"
@Getter private KeyPair keyPair;
@Column(nullable=false,unique=false)
@Getter private String type;
@ -87,12 +93,24 @@ public class BotModel {
bm.type = bot.getType();
bm.manuallyApproveFollowers = bot.isManuallyApproveFollowers();
bm.indexable = bot.isIndexable();
bm.publicKeyPem = bot.getPublicKeyPem();
bm.keyPair = bot.getKeyPair();
if ( bm.keyPair == null ) {
// if a bot is created without a KeyPair is must be a new bot and needs a KeyPair
// not confident this is the best place or way to do this, but "works for now"
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048); // TODO: make this configurable?
bm.keyPair = generator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
// TODO: this should be pretty fatal to functionality really
log.error("BotModel::from: NoSuchAlgorithm when creating: " + bot);
}
}
return bm;
}
Bot asBot() {
return new Bot( username, name, summary, published, publicKeyPem, type, manuallyApproveFollowers, indexable );
return new Bot( username, name, summary, published, keyPair, type, manuallyApproveFollowers, indexable );
}
@Override