Initial Commit
This commit is contained in:
parent
ead0504e9e
commit
d4eb083d77
25 changed files with 1818 additions and 0 deletions
7
.idea/codeStyles/Project.xml
generated
Normal file
7
.idea/codeStyles/Project.xml
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<ScalaCodeStyleSettings>
|
||||
<option name="MULTILINE_STRING_CLOSING_QUOTES_ON_NEW_LINE" value="true" />
|
||||
</ScalaCodeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
7
.idea/encodings.xml
generated
Normal file
7
.idea/encodings.xml
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
8
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
8
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
|
@ -0,0 +1,8 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,java.lang.foreign.Arena,ofAuto,java.lang.foreign.Arena,global,com.squareup.okhttp.Response,body" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
13
.idea/misc.xml
generated
Normal file
13
.idea/misc.xml
generated
Normal file
|
@ -0,0 +1,13 @@
|
|||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="openjdk-22" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
124
.idea/uiDesigner.xml
generated
Normal file
124
.idea/uiDesigner.xml
generated
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
47
pom.xml
Normal file
47
pom.xml
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>net.nosial</groupId>
|
||||
<artifactId>socialclient</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>22</maven.compiler.source>
|
||||
<maven.compiler.target>22</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.15.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||
<version>2.15.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>2.7.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
17
src/main/java/net/nosial/Main.java
Normal file
17
src/main/java/net/nosial/Main.java
Normal file
|
@ -0,0 +1,17 @@
|
|||
package net.nosial;
|
||||
|
||||
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
|
||||
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
//TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
|
||||
// to see how IntelliJ IDEA suggests fixing it.
|
||||
System.out.printf("Hello and welcome!");
|
||||
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
//TIP Press <shortcut actionId="Debug"/> to start debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
|
||||
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.
|
||||
System.out.println("i = " + i);
|
||||
}
|
||||
}
|
||||
}
|
4
src/main/java/net/nosial/socialclient/Client.java
Normal file
4
src/main/java/net/nosial/socialclient/Client.java
Normal file
|
@ -0,0 +1,4 @@
|
|||
package net.nosial.socialclient;
|
||||
|
||||
public class Client {
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package net.nosial.socialclient.abstracts;
|
||||
|
||||
import net.nosial.socialclient.objects.RpcError;
|
||||
import net.nosial.socialclient.objects.RpcResponse;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class RpcResult
|
||||
{
|
||||
private final boolean success;
|
||||
|
||||
protected RpcResult(boolean success)
|
||||
{
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public boolean isSuccess()
|
||||
{
|
||||
return this.success;
|
||||
}
|
||||
|
||||
public abstract RpcError getErrorResponse();
|
||||
public abstract RpcResponse getResponse();
|
||||
|
||||
public static RpcResult fromMap(Map<String, Object> data)
|
||||
{
|
||||
if(data.containsKey("error"))
|
||||
{
|
||||
return new RpcError(data);
|
||||
}
|
||||
else if(data.containsKey("id"))
|
||||
{
|
||||
return new RpcResponse(data);
|
||||
}
|
||||
|
||||
// TODO: Use standard RpcException for this or something
|
||||
throw new IllegalArgumentException("Cannot recognize RPC object");
|
||||
}
|
||||
}
|
402
src/main/java/net/nosial/socialclient/classes/Cryptography.java
Normal file
402
src/main/java/net/nosial/socialclient/classes/Cryptography.java
Normal file
|
@ -0,0 +1,402 @@
|
|||
package net.nosial.socialclient.classes;
|
||||
|
||||
|
||||
import net.nosial.socialclient.exceptions.CryptographyException;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import java.security.*;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
public final class Cryptography
|
||||
{
|
||||
private static final int TIME_BLOCK = 60;
|
||||
private static final int KEY_SIZE = 2048;
|
||||
private static final String ALGORITHM = "RSA";
|
||||
private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
|
||||
private static final String CIPHER_TRANSFORMATION = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
|
||||
private static final int MAX_ENCRYPT_BLOCK_SIZE = 214;
|
||||
private static final int MAX_DECRYPT_BLOCK_SIZE = 256;
|
||||
|
||||
/**
|
||||
* Returns the KeyFactory object for the specified algorithm.
|
||||
*
|
||||
* @return the KeyFactory object for the specified algorithm
|
||||
* @throws NoSuchAlgorithmException if the requested algorithm is not available
|
||||
*/
|
||||
private static KeyFactory getKeyFactory() throws NoSuchAlgorithmException
|
||||
{
|
||||
return KeyFactory.getInstance(ALGORITHM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a key pair using the RSA algorithm.
|
||||
*
|
||||
* @return the generated key pair
|
||||
* @throws CryptographyException if there is an error generating the key pair
|
||||
*/
|
||||
public static KeyPair generateKeyPair() throws CryptographyException
|
||||
{
|
||||
try
|
||||
{
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
|
||||
keyPairGenerator.initialize(KEY_SIZE);
|
||||
return keyPairGenerator.generateKeyPair();
|
||||
}
|
||||
catch (NoSuchAlgorithmException e)
|
||||
{
|
||||
throw new CryptographyException("Failed to generate key pair, RSA algorithm not found", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs the given content using the provided private key.
|
||||
*
|
||||
* @param content the content to be signed
|
||||
* @param privateKey the private key used for signing
|
||||
* @return the base64 encoded signature of the content
|
||||
* @throws CryptographyException if an error occurs in the cryptography process
|
||||
*/
|
||||
public static String signContent(String content, String privateKey) throws CryptographyException
|
||||
{
|
||||
return signContent(content, importPrivateKey(privateKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs the given content using the provided private key.
|
||||
*
|
||||
* @param content the content to be signed
|
||||
* @param privateKey the private key used for signing
|
||||
* @return the base64 encoded signature of the content
|
||||
* @throws CryptographyException if an error occurs in the cryptography process
|
||||
*/
|
||||
public static String signContent(String content, PrivateKey privateKey) throws CryptographyException
|
||||
{
|
||||
// Convert content to sha1 hash
|
||||
try
|
||||
{
|
||||
final Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
|
||||
|
||||
signature.initSign(privateKey);
|
||||
signature.update(MessageDigest.getInstance("SHA-1").digest(content.getBytes()));
|
||||
|
||||
return Base64.getEncoder().encodeToString(signature.sign());
|
||||
}
|
||||
catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e)
|
||||
{
|
||||
throw new CryptographyException("Failed to sign content", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the content using the provided signature and public key.
|
||||
*
|
||||
* @param content the content to be verified
|
||||
* @param signature the signature of the content
|
||||
* @param publicKey the public key used for verification
|
||||
* @return true if the content is verified, false otherwise
|
||||
* @throws CryptographyException if an error occurs in the cryptography process
|
||||
*/
|
||||
public static boolean verifyContent(String content, String signature, String publicKey) throws CryptographyException
|
||||
{
|
||||
return verifyContent(content, signature, importPublicKey(publicKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the signature of the given content using the provided public key.
|
||||
*
|
||||
* @param content the content to be verified
|
||||
* @param signature the signature to be verified
|
||||
* @param publicKey the public key used for verification
|
||||
* @return true if the signature is verified, false otherwise
|
||||
* @throws CryptographyException if there is an error during the verification process
|
||||
*/
|
||||
public static boolean verifyContent(String content, String signature, PublicKey publicKey) throws CryptographyException
|
||||
{
|
||||
try
|
||||
{
|
||||
final Signature sign = Signature.getInstance(SIGNATURE_ALGORITHM);
|
||||
|
||||
sign.initVerify(publicKey);
|
||||
sign.update(MessageDigest.getInstance("SHA-1").digest(content.getBytes()));
|
||||
|
||||
return sign.verify(Base64.getDecoder().decode(signature));
|
||||
}
|
||||
catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e)
|
||||
{
|
||||
throw new CryptographyException("Failed to verify content", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a temporary signature for the given content using the provided private key.
|
||||
*
|
||||
* @param content the content to be signed
|
||||
* @param privateKey the private key used for signing
|
||||
* @return the base64 encoded signature of the content
|
||||
* @throws CryptographyException if an error occurs in the cryptography process
|
||||
*/
|
||||
public static String temporarySignature(String content, String privateKey) throws CryptographyException
|
||||
{
|
||||
return signContent(String.format("%s|%d", content, (System.currentTimeMillis() / 1000 / TIME_BLOCK)), privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a temporary signature for the given content using the provided private key.
|
||||
*
|
||||
* @param content the content to be signed
|
||||
* @param privateKey the private key used for signing
|
||||
* @return the base64 encoded signature of the content
|
||||
* @throws CryptographyException if an error occurs in the cryptography process
|
||||
*/
|
||||
public static String temporarySignature(String content, PrivateKey privateKey) throws CryptographyException
|
||||
{
|
||||
return signContent(String.format("%s|%d", content, (System.currentTimeMillis() / 1000 / TIME_BLOCK)), privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the temporary signature of the given content using the specified public key and number of frames.
|
||||
*
|
||||
* @param content The content to verify the signature for. Must not be null.
|
||||
* @param signature The signature to verify. Must not be null.
|
||||
* @param publicKey The public key used for verification. Must not be null.
|
||||
* @param frames The number of frames to use for verification. Must be a positive integer.
|
||||
* @return true if the signature is valid, false otherwise.
|
||||
* @throws CryptographyException if an error occurs during verification.
|
||||
*/
|
||||
public static boolean verifyTemporarySignature(String content, String signature, String publicKey, int frames) throws CryptographyException
|
||||
{
|
||||
return verifyTemporarySignature(content, signature, importPublicKey(publicKey), frames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the temporary signature of the given content using the specified public key and number of frames.
|
||||
*
|
||||
* @param content The content to verify the signature for. Must not be null.
|
||||
* @param signature The signature to verify. Must not be null.
|
||||
* @param publicKey The public key used for verification. Must not be null.
|
||||
* @param frames The number of frames to use for verification. Must be a positive integer.
|
||||
* @return true if the signature is valid, false otherwise.
|
||||
* @throws CryptographyException if an error occurs during verification.
|
||||
*/
|
||||
public static boolean verifyTemporarySignature(String content, String signature, PublicKey publicKey, int frames) throws CryptographyException
|
||||
{
|
||||
if (frames <= 0) frames = 1;
|
||||
long currentTime = System.currentTimeMillis() / 1000 / TIME_BLOCK;
|
||||
|
||||
for (int i = 0; i < frames; i++)
|
||||
{
|
||||
if (verifyContent(String.format("%s|%d", content, currentTime - i), signature, publicKey))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the given content using the provided public key.
|
||||
*
|
||||
* @param content the content to be encrypted
|
||||
* @param publicKey the public key used for encryption
|
||||
* @return the encrypted content as a String
|
||||
* @throws CryptographyException if an error occurs during the encryption process
|
||||
*/
|
||||
public static String encrypt(String content, String publicKey) throws CryptographyException
|
||||
{
|
||||
return encrypt(content, importPublicKey(publicKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the given content using the provided public key.
|
||||
*
|
||||
* @param content the content to be encrypted
|
||||
* @param publicKey the public key used for encryption
|
||||
* @return the encrypted content as a Base64 encoded string
|
||||
* @throws CryptographyException if an error occurs during the encryption process
|
||||
*/
|
||||
public static String encrypt(String content, PublicKey publicKey) throws CryptographyException
|
||||
{
|
||||
try
|
||||
{
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
|
||||
byte[] data = content.getBytes();
|
||||
int inputLength = data.length;
|
||||
List<Byte> outputList = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < inputLength; i += MAX_ENCRYPT_BLOCK_SIZE)
|
||||
{
|
||||
int length = Math.min(inputLength - i, MAX_ENCRYPT_BLOCK_SIZE);
|
||||
byte[] encryptedBlock = cipher.doFinal(data, i, length);
|
||||
for (byte b : encryptedBlock)
|
||||
{
|
||||
outputList.add(b);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] output = new byte[outputList.size()];
|
||||
for (int i = 0; i < outputList.size(); i++)
|
||||
{
|
||||
output[i] = outputList.get(i);
|
||||
}
|
||||
|
||||
return Base64.getEncoder().encodeToString(output);
|
||||
}
|
||||
catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e)
|
||||
{
|
||||
throw new CryptographyException("Failed to encrypt content", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the given content using the provided private key.
|
||||
*
|
||||
* @param content the encrypted content to be decrypted
|
||||
* @param privateKey the private key used for decryption
|
||||
* @return the decrypted content as a String
|
||||
* @throws CryptographyException if an error occurs during the decryption process
|
||||
*/
|
||||
public static String decrypt(String content, String privateKey) throws CryptographyException
|
||||
{
|
||||
return decrypt(content, importPrivateKey(privateKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the given content using the provided private key.
|
||||
*
|
||||
* @param content the encrypted content to be decrypted
|
||||
* @param privateKey the private key used for decryption
|
||||
* @return the decrypted content as a String
|
||||
* @throws CryptographyException if an error occurs during the decryption process
|
||||
*/
|
||||
public static String decrypt(String content, PrivateKey privateKey) throws CryptographyException
|
||||
{
|
||||
try
|
||||
{
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
|
||||
byte[] data = Base64.getDecoder().decode(content);
|
||||
int inputLength = data.length;
|
||||
List<Byte> outputList = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < inputLength; i += MAX_DECRYPT_BLOCK_SIZE)
|
||||
{
|
||||
int length = Math.min(inputLength - i, MAX_DECRYPT_BLOCK_SIZE);
|
||||
byte[] decryptedBlock = cipher.doFinal(data, i, length);
|
||||
for (byte b : decryptedBlock)
|
||||
{
|
||||
outputList.add(b);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] output = new byte[outputList.size()];
|
||||
for (int i = 0; i < outputList.size(); i++)
|
||||
{
|
||||
output[i] = outputList.get(i);
|
||||
}
|
||||
|
||||
return new String(output);
|
||||
}
|
||||
catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e)
|
||||
{
|
||||
throw new CryptographyException("Failed to decrypt content", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the given private key as a Base64 encoded string.
|
||||
*
|
||||
* @param privateKey the private key to be exported
|
||||
* @return the Base64 encoded string representation of the private key
|
||||
*/
|
||||
public static String exportPrivateKey(PrivateKey privateKey)
|
||||
{
|
||||
return Base64.getEncoder().encodeToString(privateKey.getEncoded());
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the given private key as a Base64 encoded string.
|
||||
*
|
||||
* @param keyPair the private key to be exported
|
||||
* @return the Base64 encoded string representation of the private key
|
||||
*/
|
||||
public static String exportPrivateKey(KeyPair keyPair)
|
||||
{
|
||||
return exportPrivateKey(keyPair.getPrivate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the given public key as a Base64 encoded string.
|
||||
*
|
||||
* @param publicKey the public key to be exported
|
||||
* @return a Base64 encoded string representation of the public key
|
||||
*/
|
||||
public static String exportPublicKey(PublicKey publicKey)
|
||||
{
|
||||
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the given public key as a Base64 encoded string.
|
||||
*
|
||||
* @param keyPair the public key to be exported
|
||||
* @return a Base64 encoded string representation of the public key
|
||||
*/
|
||||
public static String exportPublicKey(KeyPair keyPair)
|
||||
{
|
||||
return exportPublicKey(keyPair.getPublic());
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a public key from a Base64 encoded string representation.
|
||||
*
|
||||
* @param publicKey the Base64 encoded string representation of the public key
|
||||
* @return the imported PublicKey object
|
||||
* @throws CryptographyException if there is an error importing the public key
|
||||
*/
|
||||
public static PublicKey importPublicKey(String publicKey) throws CryptographyException
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] keyBytes = Base64.getDecoder().decode(publicKey);
|
||||
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
|
||||
return getKeyFactory().generatePublic(spec);
|
||||
}
|
||||
catch (InvalidKeySpecException | NoSuchAlgorithmException e)
|
||||
{
|
||||
throw new CryptographyException("Failed to import public key", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a private key from a Base64 encoded string representation.
|
||||
*
|
||||
* @param privateKey the Base64 encoded string representation of the private key
|
||||
* @return the imported PrivateKey object
|
||||
* @throws CryptographyException if there is an error importing the private key
|
||||
*/
|
||||
public static PrivateKey importPrivateKey(String privateKey) throws CryptographyException
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] keyBytes = Base64.getDecoder().decode(privateKey);
|
||||
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
|
||||
return getKeyFactory().generatePrivate(spec);
|
||||
}
|
||||
catch (InvalidKeySpecException | NoSuchAlgorithmException | IllegalArgumentException e)
|
||||
{
|
||||
throw new CryptographyException("Failed to import private key", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
95
src/main/java/net/nosial/socialclient/classes/Resolver.java
Normal file
95
src/main/java/net/nosial/socialclient/classes/Resolver.java
Normal file
|
@ -0,0 +1,95 @@
|
|||
package net.nosial.socialclient.classes;
|
||||
|
||||
import net.nosial.socialclient.exceptions.ResolutionException;
|
||||
import net.nosial.socialclient.objects.ResolvedServer;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.*;
|
||||
import java.util.Hashtable;
|
||||
|
||||
// Improved Resolver class
|
||||
public class Resolver
|
||||
{
|
||||
/**
|
||||
* Resolves the domain to obtain endpoint and public key from its DNS TXT records.
|
||||
*
|
||||
* @param domain The domain to be resolved.
|
||||
* @return ResolvedServer An instance of ResolvedServer containing the endpoint and public key.
|
||||
* @throws ResolutionException If the DNS TXT records cannot be resolved or if required information is missing.
|
||||
*/
|
||||
public static ResolvedServer resolveDomain(String domain) throws ResolutionException
|
||||
{
|
||||
final String endpointPrefix = "socialbox=";
|
||||
final String keyPrefix = "socialbox-key=";
|
||||
|
||||
// Disable caching
|
||||
Hashtable<String, String> env = new Hashtable<>();
|
||||
env.put("com.sun.jndi.dns.timeout.retries", "1");
|
||||
env.put("com.sun.jndi.dns.cache.ttl", "0");
|
||||
env.put("com.sun.jndi.dns.cache.negative.ttl", "0");
|
||||
|
||||
try
|
||||
{
|
||||
DirContext dirContext = new InitialDirContext(env);
|
||||
Attributes attributes = dirContext.getAttributes("dns:/" + domain, new String[]{"TXT"});
|
||||
Attribute attributeTXT = attributes.get("TXT");
|
||||
|
||||
if (attributeTXT == null)
|
||||
{
|
||||
throw new ResolutionException("Failed to resolve DNS TXT records for " + domain);
|
||||
}
|
||||
|
||||
String endpoint = null;
|
||||
StringBuilder publicKeyBuilder = new StringBuilder();
|
||||
boolean publicKeyFound = false;
|
||||
|
||||
for (int i = 0; i < attributeTXT.size(); i++)
|
||||
{
|
||||
String value = (String) attributeTXT.get(i);
|
||||
|
||||
// Remove surrounding quotes if present
|
||||
if (value.startsWith("\"") && value.endsWith("\""))
|
||||
{
|
||||
value = value.substring(1, value.length() - 1);
|
||||
}
|
||||
|
||||
// Split the value into fragments to find the relevant keys
|
||||
String[] fragments = value.split("\\s+");
|
||||
for (String fragment : fragments)
|
||||
{
|
||||
if (fragment.startsWith(endpointPrefix))
|
||||
{
|
||||
endpoint = fragment.substring(endpointPrefix.length());
|
||||
}
|
||||
else if (fragment.startsWith(keyPrefix))
|
||||
{
|
||||
publicKeyBuilder.append(fragment.substring(keyPrefix.length()));
|
||||
publicKeyFound = true;
|
||||
}
|
||||
else if (publicKeyFound)
|
||||
{
|
||||
// If the public key has already started, append the fragment
|
||||
publicKeyBuilder.append(fragment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (endpoint == null)
|
||||
{
|
||||
throw new ResolutionException("Failed to resolve RPC endpoint for " + domain);
|
||||
}
|
||||
|
||||
if (publicKeyBuilder.isEmpty())
|
||||
{
|
||||
throw new ResolutionException("Failed to resolve public key for " + domain);
|
||||
}
|
||||
|
||||
String publicKey = publicKeyBuilder.toString();
|
||||
return new ResolvedServer(endpoint, publicKey);
|
||||
}
|
||||
catch (NamingException e)
|
||||
{
|
||||
throw new ResolutionException("Error resolving domain: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
361
src/main/java/net/nosial/socialclient/classes/RpcClient.java
Normal file
361
src/main/java/net/nosial/socialclient/classes/RpcClient.java
Normal file
|
@ -0,0 +1,361 @@
|
|||
package net.nosial.socialclient.classes;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.squareup.okhttp.*;
|
||||
import net.nosial.socialclient.abstracts.RpcResult;
|
||||
import net.nosial.socialclient.exceptions.CryptographyException;
|
||||
import net.nosial.socialclient.exceptions.ResolutionException;
|
||||
import net.nosial.socialclient.objects.ResolvedServer;
|
||||
import net.nosial.socialclient.objects.RpcRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class RpcClient
|
||||
{
|
||||
private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
private final static String CLIENT_NAME = "SocialClient Java";
|
||||
private final static String CLIENT_VERSION = "1.0";
|
||||
private final static MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
|
||||
private final String domain;
|
||||
private final String endpoint;
|
||||
private final PublicKey serverPublicKey;
|
||||
private final OkHttpClient httpClient;
|
||||
|
||||
private String sessionUuid;
|
||||
private PrivateKey privateKey;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the RpcClient class using the specified domain.
|
||||
* This constructor resolves the domain to obtain the RPC endpoint and public key.
|
||||
*
|
||||
* @param domain The domain to be resolved for the RPC endpoint and public key.
|
||||
* @throws ResolutionException If the domain cannot be resolved or if required information is missing.
|
||||
* @throws CryptographyException If an error occurs while importing the public key.
|
||||
*/
|
||||
protected RpcClient(String domain) throws ResolutionException, CryptographyException
|
||||
{
|
||||
this.domain = domain;
|
||||
this.httpClient = new OkHttpClient();
|
||||
|
||||
// Resolve the domain to get the endpoint and public key
|
||||
ResolvedServer resolvedServer = Resolver.resolveDomain(domain);
|
||||
|
||||
this.endpoint = resolvedServer.getEndpoint();
|
||||
this.serverPublicKey = Cryptography.importPublicKey(resolvedServer.getPublicKey());
|
||||
this.sessionUuid = null;
|
||||
this.privateKey = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an RpcClient instance with the specified endpoint and public key.
|
||||
*
|
||||
* @param endpoint The endpoint to which RPC requests will be sent.
|
||||
* @param serverPublicKey The public key used for cryptographic operations with the RPC server.
|
||||
*/
|
||||
public RpcClient(String endpoint, PublicKey serverPublicKey)
|
||||
{
|
||||
this.domain = null;
|
||||
this.endpoint = endpoint;
|
||||
this.serverPublicKey = serverPublicKey;
|
||||
this.httpClient = new OkHttpClient();
|
||||
this.sessionUuid = null;
|
||||
this.privateKey = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the domain associated with this RpcClient instance.
|
||||
*
|
||||
* @return the domain as a string.
|
||||
*/
|
||||
public String getDomain()
|
||||
{
|
||||
return this.domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the RPC endpoint for the client.
|
||||
*
|
||||
* @return the endpoint as a String.
|
||||
*/
|
||||
public String getEndpoint()
|
||||
{
|
||||
return this.endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the public key associated with the RpcClient instance.
|
||||
*
|
||||
* @return the public key as a PublicKey object.
|
||||
*/
|
||||
public PublicKey getServerPublicKey()
|
||||
{
|
||||
return this.serverPublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the session UUID associated with the RpcClient instance.
|
||||
*
|
||||
* @return the session UUID as a string.
|
||||
*/
|
||||
public String getSessionUuid()
|
||||
{
|
||||
return this.sessionUuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session UUID and imports the provided private key.
|
||||
*
|
||||
* @param sessionUuid the unique identifier for this session
|
||||
* @param privateKey the private key to be imported
|
||||
* @throws CryptographyException if an error occurs while importing the private key
|
||||
*/
|
||||
public void setSession(String sessionUuid, String privateKey) throws CryptographyException
|
||||
{
|
||||
this.sessionUuid = sessionUuid;
|
||||
this.privateKey = Cryptography.importPrivateKey(privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session details for the RPC client.
|
||||
*
|
||||
* @param sessionUuid the UUID of the session
|
||||
* @param privateKey the private key associated with the session
|
||||
*/
|
||||
public void setSession(String sessionUuid, PrivateKey privateKey)
|
||||
{
|
||||
this.sessionUuid = sessionUuid;
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the current session by setting the sessionUuid and privateKey fields to null.
|
||||
* This method effectively logs out the user from the current session.
|
||||
*/
|
||||
public void clearSession()
|
||||
{
|
||||
this.sessionUuid = null;
|
||||
this.privateKey = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an RPC request with the provided JSON data and returns a list of RpcResult objects.
|
||||
*
|
||||
* @param jsonData the JSON-formatted string representing the RPC request data.
|
||||
* @return a list of RpcResult objects representing the response(s) from the RPC server.
|
||||
* @throws RuntimeException if there is an error during the request or response processing.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<RpcResult> sendRequest(String jsonData)
|
||||
{
|
||||
final Request request = new Request.Builder()
|
||||
{{
|
||||
this.url(endpoint);
|
||||
this.post(RequestBody.create(MEDIA_TYPE_JSON, jsonData));
|
||||
this.addHeader("Client-Name", CLIENT_NAME);
|
||||
this.addHeader("Client-Version", CLIENT_VERSION);
|
||||
|
||||
if(sessionUuid != null)
|
||||
{
|
||||
this.addHeader("Session-UUID", sessionUuid);
|
||||
}
|
||||
|
||||
if(privateKey != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.addHeader("Signature", Cryptography.signContent(jsonData, privateKey));
|
||||
}
|
||||
catch(CryptographyException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}}.build();
|
||||
|
||||
try
|
||||
{
|
||||
final Response response = this.httpClient.newCall(request).execute();
|
||||
final String responseString = response.body().string();
|
||||
|
||||
if (!response.isSuccessful())
|
||||
{
|
||||
if(!responseString.isEmpty())
|
||||
{
|
||||
throw new RuntimeException(responseString);
|
||||
}
|
||||
|
||||
throw new RuntimeException("Failed to send request");
|
||||
}
|
||||
|
||||
if(response.code() == 204)
|
||||
{
|
||||
// The response is empty
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Object decoded = decode(responseString);
|
||||
|
||||
// Singular object response
|
||||
if(decoded instanceof List<?>)
|
||||
{
|
||||
List<Map<String, Object>> responseList = (List<Map<String, Object>>) decoded;
|
||||
List<RpcResult> results = new ArrayList<>(responseList.size());
|
||||
for(Map<String, Object> responseMap : responseList)
|
||||
{
|
||||
results.add(RpcResult.fromMap(responseMap));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
if(decoded instanceof Map<?, ?>)
|
||||
{
|
||||
// Singular object response
|
||||
List<RpcResult> results = new ArrayList<>(1);
|
||||
results.add(RpcResult.fromMap((Map<String, Object>) decoded));
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
throw new RuntimeException("Failed to decode response");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an RPC request to the configured endpoint and returns the first result.
|
||||
*
|
||||
* @param request the RPC request to be sent
|
||||
* @return the first RPC result received from the response
|
||||
*/
|
||||
public RpcResult sendRequest(RpcRequest request)
|
||||
{
|
||||
return sendRequest(encode(request.toMap())).getFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a list of RPC requests to the server and returns their results as a list of RpcResult objects.
|
||||
*
|
||||
* @param requests the list of RPC requests to be sent
|
||||
* @return a list of RpcResult objects containing the responses from the server
|
||||
*/
|
||||
public List<RpcResult> sendRequests(List<RpcRequest> requests)
|
||||
{
|
||||
return sendRequest(encode(requests));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decodes a JSON string into a corresponding Java object. If the JSON string
|
||||
* represents an object, it decodes it into a Map. If the JSON string represents
|
||||
* an array, it decodes it into a List.
|
||||
*
|
||||
* @param json the JSON string to be decoded
|
||||
* @return a decoded Java object which can be a Map or a List depending on the JSON structure
|
||||
* @throws RuntimeException if the JSON string is neither an object nor an array, or if decoding fails
|
||||
*/
|
||||
private static Object decode(String json)
|
||||
{
|
||||
try
|
||||
{
|
||||
JsonNode jsonNode = OBJECT_MAPPER.readTree(json);
|
||||
if (jsonNode.isObject())
|
||||
{
|
||||
return decodeJson(OBJECT_MAPPER.readValue(json, new TypeReference<>() {}));
|
||||
}
|
||||
else if (jsonNode.isArray())
|
||||
{
|
||||
return decodeList(OBJECT_MAPPER.readValue(json, new TypeReference<>() {}));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException("JSON is neither an object nor an array");
|
||||
}
|
||||
}
|
||||
catch (JsonProcessingException e)
|
||||
{
|
||||
throw new RuntimeException("Failed to decode input", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively decodes a JSON structure represented as a Map. If the value
|
||||
* of an entry is another Map, it recursively decodes that Map. If the value
|
||||
* is a List, it passes the List to the decodeList method.
|
||||
*
|
||||
* @param map the JSON structure to be decoded represented as a Map
|
||||
* @return the decoded JSON structure as a Map
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> decodeJson(Map<String, Object> map)
|
||||
{
|
||||
for (Map.Entry<String, Object> entry : map.entrySet())
|
||||
{
|
||||
if (entry.getValue() instanceof Map)
|
||||
{
|
||||
entry.setValue(decodeJson((Map<String, Object>) entry.getValue()));
|
||||
}
|
||||
else if (entry.getValue() instanceof List)
|
||||
{
|
||||
entry.setValue(decodeList((List<Object>) entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively decodes a list of objects, transforming any nested maps or lists within it.
|
||||
*
|
||||
* @param list the list of objects to be decoded
|
||||
* @return the decoded list of objects, with all nested maps and lists transformed
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<Object> decodeList(List<Object> list)
|
||||
{
|
||||
for (int i = 0; i < list.size(); i++)
|
||||
{
|
||||
final Object item = list.get(i);
|
||||
if (item instanceof Map)
|
||||
{
|
||||
list.set(i, decodeJson((Map<String, Object>) item));
|
||||
}
|
||||
else if (item instanceof List)
|
||||
{
|
||||
list.set(i, decodeList((List<Object>) item));
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given input object to its JSON string representation.
|
||||
*
|
||||
* @param input the object to encode
|
||||
* @return the JSON string representation of the input object
|
||||
*/
|
||||
private static String encode(Object input)
|
||||
{
|
||||
try
|
||||
{
|
||||
return OBJECT_MAPPER.writeValueAsString(input);
|
||||
}
|
||||
catch(JsonProcessingException e)
|
||||
{
|
||||
throw new RuntimeException("Failed to encode input to JSON Data", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package net.nosial.socialclient.enums;
|
||||
|
||||
public enum StandardErrorCodes
|
||||
{
|
||||
UNKNOWN(-1),
|
||||
|
||||
RPC_METHOD_NOT_FOUND(-1000),
|
||||
RPC_INVALID_ARGUMENTS(-1001),
|
||||
|
||||
INTERNAL_SERVER_ERROR(-2000),
|
||||
SERVER_UNAVAILABLE(-2001),
|
||||
|
||||
INVALID_PUBLIC_KEY(-3000),
|
||||
SESSION_NOT_FOUND(-3001),
|
||||
UNSUPPORTED_AUTHENTICATION_TYPE(-3002);
|
||||
|
||||
private final int code;
|
||||
|
||||
StandardErrorCodes(int code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode()
|
||||
{
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public static StandardErrorCodes resolveCode(int input)
|
||||
{
|
||||
for (StandardErrorCodes errorCode : StandardErrorCodes.values())
|
||||
{
|
||||
if (errorCode.getCode() == input)
|
||||
{
|
||||
return errorCode;
|
||||
}
|
||||
}
|
||||
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package net.nosial.socialclient.exceptions;
|
||||
|
||||
public class CryptographyException extends Exception
|
||||
{
|
||||
public CryptographyException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CryptographyException(String message, Throwable cause)
|
||||
{
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package net.nosial.socialclient.exceptions;
|
||||
|
||||
public class ResolutionException extends Exception
|
||||
{
|
||||
public ResolutionException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ResolutionException(String message, Throwable cause)
|
||||
{
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package net.nosial.socialclient.exceptions;
|
||||
|
||||
public class RpcException extends Exception
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package net.nosial.socialclient.objects;
|
||||
|
||||
public class KeyPair
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package net.nosial.socialclient.objects;
|
||||
|
||||
public class ResolvedServer
|
||||
{
|
||||
private final String endpoint;
|
||||
private final String publicKey;
|
||||
|
||||
public ResolvedServer(String endpoint, String publicKey)
|
||||
{
|
||||
this.endpoint = endpoint;
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
public String getEndpoint()
|
||||
{
|
||||
return this.endpoint;
|
||||
}
|
||||
|
||||
public String getPublicKey()
|
||||
{
|
||||
return this.publicKey;
|
||||
}
|
||||
}
|
79
src/main/java/net/nosial/socialclient/objects/RpcError.java
Normal file
79
src/main/java/net/nosial/socialclient/objects/RpcError.java
Normal file
|
@ -0,0 +1,79 @@
|
|||
package net.nosial.socialclient.objects;
|
||||
|
||||
import net.nosial.socialclient.abstracts.RpcResult;
|
||||
import net.nosial.socialclient.enums.StandardErrorCodes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class RpcError extends RpcResult
|
||||
{
|
||||
private final String id;
|
||||
private final String error;
|
||||
private final StandardErrorCodes code;
|
||||
|
||||
/**
|
||||
* Constructs an instance of RpcError using data from the given map.
|
||||
*
|
||||
* @param data a map containing the data for the error. Must include keys "id" with a String value,
|
||||
* "error" with a String value, and "code" with an integer value which represents the error code.
|
||||
*/
|
||||
public RpcError(Map<String, Object> data)
|
||||
{
|
||||
super(false);
|
||||
this.id = (String) data.get("id");
|
||||
this.error = (String) data.get("error");
|
||||
this.code = StandardErrorCodes.resolveCode((int)data.get("code"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ID of the RPC error.
|
||||
*
|
||||
* @return the ID of the RPC error.
|
||||
*/
|
||||
public String getId()
|
||||
{
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the error message associated with this RPC error.
|
||||
*
|
||||
* @return the error message as a String.
|
||||
*/
|
||||
public String getError()
|
||||
{
|
||||
return this.error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the standard error code associated with this RPC error.
|
||||
*
|
||||
* @return the standard error code.
|
||||
*/
|
||||
public StandardErrorCodes getCode()
|
||||
{
|
||||
return this.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current RpcError instance as the error response.
|
||||
*
|
||||
* @return the current RpcError instance.
|
||||
*/
|
||||
@Override
|
||||
public RpcError getErrorResponse()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null as RpcError does not provide a successful response.
|
||||
*
|
||||
* @return null since RpcError instances do not represent successful responses.
|
||||
*/
|
||||
@Override
|
||||
public RpcResponse getResponse()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
134
src/main/java/net/nosial/socialclient/objects/RpcRequest.java
Normal file
134
src/main/java/net/nosial/socialclient/objects/RpcRequest.java
Normal file
|
@ -0,0 +1,134 @@
|
|||
package net.nosial.socialclient.objects;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class RpcRequest
|
||||
{
|
||||
private final String method;
|
||||
private final String id;
|
||||
private final Map<String, Object> params;
|
||||
|
||||
/**
|
||||
* Initializes a new RpcRequest object using the provided data map.
|
||||
*
|
||||
* @param data a map containing the initial values for the RpcRequest.
|
||||
* The map should have the following expected keys:
|
||||
* - "method": a String representing the RPC method name
|
||||
* - "id": a String representing the unique request identifier
|
||||
* - "params": a Map<String, Object> containing parameters for the method call
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public RpcRequest(Map<String, Object> data)
|
||||
{
|
||||
this.method = (String) data.get("method");
|
||||
this.id = (String) data.get("id");
|
||||
this.params = (Map<String, Object>) data.get("params");
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an RpcRequest with the specified method name, request id, and parameters.
|
||||
*
|
||||
* @param method the RPC method to be called
|
||||
* @param id the unique identifier for this request
|
||||
* @param params the parameters for the RPC method
|
||||
*/
|
||||
public RpcRequest(String method, String id, Map<String, Object> params)
|
||||
{
|
||||
this.method = method;
|
||||
this.id = id;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an RPC request with a specified method and parameters.
|
||||
* The request ID will be set to null.
|
||||
*
|
||||
* @param method the name of the method being called
|
||||
* @param params the parameters to be sent with the method call
|
||||
*/
|
||||
public RpcRequest(String method, Map<String, Object> params)
|
||||
{
|
||||
this.id = null;
|
||||
this.method = method;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an RpcRequest with the specified method and id.
|
||||
*
|
||||
* @param method The method name for the RPC request.
|
||||
* @param id The id of the RPC request.
|
||||
*/
|
||||
public RpcRequest(String method, String id)
|
||||
{
|
||||
this.method = method;
|
||||
this.id = id;
|
||||
this.params = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an RpcRequest with a specified method and no parameters or ID.
|
||||
*
|
||||
* @param method the RPC method name for the request
|
||||
*/
|
||||
public RpcRequest(String method)
|
||||
{
|
||||
this.id = null;
|
||||
this.method = method;
|
||||
this.params = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the identifier of the RPC request.
|
||||
*
|
||||
* @return the identifier of the request.
|
||||
*/
|
||||
public String getId()
|
||||
{
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the method name associated with the RPC request.
|
||||
*
|
||||
* @return the method name as a String.
|
||||
*/
|
||||
public String getMethod()
|
||||
{
|
||||
return this.method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the parameters map associated with the RPC request.
|
||||
*
|
||||
* @return a Map containing the parameters of the RPC request.
|
||||
*/
|
||||
public Map<String, Object> getParams()
|
||||
{
|
||||
return this.params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the current RpcRequest object into a map representation.
|
||||
*
|
||||
* @return a map containing the method, id, and params of the RpcRequest, if present.
|
||||
*/
|
||||
public Map<String, Object> toMap()
|
||||
{
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
map.put("method", this.method);
|
||||
|
||||
if(this.id != null)
|
||||
{
|
||||
map.put("id", this.id);
|
||||
}
|
||||
|
||||
if(this.params != null)
|
||||
{
|
||||
map.put("params", this.params);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package net.nosial.socialclient.objects;
|
||||
|
||||
import net.nosial.socialclient.abstracts.RpcResult;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class RpcResponse extends RpcResult
|
||||
{
|
||||
private final String id;
|
||||
private final Object result;
|
||||
|
||||
/**
|
||||
* Constructs an instance of RpcResponse using data from the given map.
|
||||
*
|
||||
* @param data a map containing the data for the response. Must include an "id" key with a String value
|
||||
* and optionally a "result" key with any Object value.
|
||||
*/
|
||||
public RpcResponse(Map<String, Object> data)
|
||||
{
|
||||
super(true);
|
||||
this.id = (String) data.get("id");
|
||||
this.result = data.get("result");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ID of the RPC response.
|
||||
*
|
||||
* @return the ID of the RPC response.
|
||||
*/
|
||||
public String getId()
|
||||
{
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the result object from the RPC response.
|
||||
*
|
||||
* @return the result object, which can be of any type
|
||||
*/
|
||||
public Object getResult()
|
||||
{
|
||||
return this.result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error response for an RPC operation. For the
|
||||
* RpcResponse class, an error response is always null as
|
||||
* it's assumed to be successful with no errors.
|
||||
*
|
||||
* @return null as RpcResponse does not have an error response.
|
||||
*/
|
||||
@Override
|
||||
public RpcError getErrorResponse()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current RpcResponse instance.
|
||||
*
|
||||
* @return the current RpcResponse instance.
|
||||
*/
|
||||
@Override
|
||||
public RpcResponse getResponse()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
package net.nosial.socialclient.classes;
|
||||
|
||||
import net.nosial.socialclient.exceptions.CryptographyException;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class CryptographyTest
|
||||
{
|
||||
/**
|
||||
* Method under test: Cryptography::generateKeyPair
|
||||
*/
|
||||
@Test
|
||||
public void testGenerateKeyPair_Success() throws CryptographyException {
|
||||
// action
|
||||
KeyPair keyPair = Cryptography.generateKeyPair();
|
||||
|
||||
// assert
|
||||
Assertions.assertNotNull(keyPair, "KeyPair should not be null");
|
||||
|
||||
Assertions.assertNotNull(keyPair.getPrivate(), "PrivateKey should not be null");
|
||||
Assertions.assertTrue(keyPair.getPrivate().getAlgorithm().contains("RSA"), "PrivateKey algorithm should be RSA");
|
||||
|
||||
Assertions.assertNotNull(keyPair.getPublic(), "PublicKey should not be null");
|
||||
Assertions.assertTrue(keyPair.getPublic().getAlgorithm().contains("RSA"), "PublicKey algorithm should be RSA");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignContent_Success() throws CryptographyException {
|
||||
// Arrange
|
||||
String content = "ExampleContent";
|
||||
KeyPair keyPair = Cryptography.generateKeyPair();
|
||||
String privateKey = Cryptography.exportPrivateKey(keyPair);
|
||||
|
||||
// Act
|
||||
String signature = Cryptography.signContent(content, privateKey);
|
||||
|
||||
// Assert
|
||||
assertNotNull(signature, "Signature should not be null");
|
||||
assertTrue(Cryptography.verifyContent(content, signature, Cryptography.exportPublicKey(keyPair)), "Signed content should be verified successfully");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignContent_WithInvalidPrivateKey_ShouldThrowException() {
|
||||
// Arrange
|
||||
String content = "ExampleContent";
|
||||
String invalidPrivateKey = "InvalidPrivateKey";
|
||||
|
||||
// Assert
|
||||
assertThrows(CryptographyException.class, () -> Cryptography.signContent(content, invalidPrivateKey), "SignContent should throw CryptographyException with invalid private key");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testVerifyContent_Success() throws CryptographyException {
|
||||
// Arrange
|
||||
String content = "ExampleContent";
|
||||
KeyPair keyPair = Cryptography.generateKeyPair();
|
||||
String privateKey = Cryptography.exportPrivateKey(keyPair);
|
||||
String signature = Cryptography.signContent(content, privateKey);
|
||||
String publicKey = Cryptography.exportPublicKey(keyPair);
|
||||
|
||||
// Act & assert
|
||||
assertTrue(Cryptography.verifyContent(content, signature, publicKey), "Content should be verified successfully with valid inputs");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifyContent_FailureWithInvalidSignature() throws CryptographyException {
|
||||
// Arrange
|
||||
String content = "ExampleContent";
|
||||
String invalidSignature = "InvalidSignature";
|
||||
KeyPair keyPair = Cryptography.generateKeyPair();
|
||||
String publicKey = Cryptography.exportPublicKey(keyPair);
|
||||
|
||||
// Act & assert
|
||||
assertThrows(CryptographyException.class, () -> Cryptography.verifyContent(content, invalidSignature, publicKey), "verifyContent should throw CryptographyException with invalid signature");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifyContent_FailureWithInvalidPublicKey() throws CryptographyException {
|
||||
// Arrange
|
||||
String content = "ExampleContent";
|
||||
String invalidPublicKey = "InvalidPublicKey";
|
||||
KeyPair keyPair = Cryptography.generateKeyPair();
|
||||
String privateKey = Cryptography.exportPrivateKey(keyPair);
|
||||
String signature = Cryptography.signContent(content, privateKey);
|
||||
|
||||
// Act & assert
|
||||
assertThrows(CryptographyException.class, () -> Cryptography.verifyContent(content, signature, invalidPublicKey), "verifyContent should throw CryptographyException with invalid public key");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTemporarySignature_Success() throws CryptographyException {
|
||||
// Arrange
|
||||
String content = "ExampleContent";
|
||||
KeyPair keyPair = Cryptography.generateKeyPair();
|
||||
String privateKey = Cryptography.exportPrivateKey(keyPair);
|
||||
|
||||
// Act
|
||||
String signature = Cryptography.temporarySignature(content, privateKey);
|
||||
|
||||
// Assert
|
||||
Assertions.assertNotNull(signature, "Signature should not be null");
|
||||
Assertions.assertTrue(Cryptography.verifyTemporarySignature(content, signature, Cryptography.exportPublicKey(keyPair), 1), "Signed content should be verified successfully");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncrypt_FailureWithNullPublicKey() {
|
||||
// Arrange
|
||||
String content = "ExampleContent";
|
||||
String nullPublicKey = null;
|
||||
|
||||
// Act & Assert
|
||||
Assertions.assertThrows(NullPointerException.class, () -> Cryptography.encrypt(content, nullPublicKey), "encrypt should throw NullPointerException when public key is null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTemporarySignature_FailureWithInvalidPrivateKey() {
|
||||
// Arrange
|
||||
String content = "ExampleContent";
|
||||
String invalidPrivateKey = "InvalidPrivateKey";
|
||||
|
||||
// Assert
|
||||
Assertions.assertThrows(CryptographyException.class, () -> Cryptography.temporarySignature(content, invalidPrivateKey), "temporarySignature should throw CryptographyException with invalid private key");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTemporarySignature_FailureWithInvalidContent() throws CryptographyException {
|
||||
// Arrange
|
||||
String content = "ExampleContent";
|
||||
String invalidContent = "InvalidExampleContent";
|
||||
KeyPair keyPair = Cryptography.generateKeyPair();
|
||||
String privateKey = Cryptography.exportPrivateKey(keyPair);
|
||||
|
||||
// Act
|
||||
String signature = Cryptography.temporarySignature(content, privateKey);
|
||||
|
||||
// Assert
|
||||
Assertions.assertFalse(Cryptography.verifyTemporarySignature(invalidContent, signature, Cryptography.exportPublicKey(keyPair), 1), "Temporary signature should fail to verify with invalid content");
|
||||
}
|
||||
|
||||
/**
|
||||
* Method under test: Cryptography::encrypt
|
||||
*/
|
||||
@Test
|
||||
public void testEncrypt_Success() throws CryptographyException {
|
||||
// Arrange
|
||||
String content = "ExampleContent";
|
||||
KeyPair keyPair = Cryptography.generateKeyPair();
|
||||
String publicKey = Cryptography.exportPublicKey(keyPair);
|
||||
|
||||
// Act
|
||||
String encryptedContent = Cryptography.encrypt(content, publicKey);
|
||||
|
||||
// Assert
|
||||
Assertions.assertNotNull(encryptedContent, "Encrypted content should not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncrypt_FailureWithInvalidPublicKey() {
|
||||
// Arrange
|
||||
String content = "ExampleContent";
|
||||
String invalidPublicKey = "InvalidPublicKey";
|
||||
|
||||
// Act & Assert
|
||||
Assertions.assertThrows(CryptographyException.class, () -> Cryptography.encrypt(content, invalidPublicKey), "encrypt should throw CryptographyException with invalid public key");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method under test: Cryptography::decrypt
|
||||
*/
|
||||
@Test
|
||||
public void testDecrypt_Success() throws CryptographyException {
|
||||
// Arrange
|
||||
String originalContent = "ExampleContent";
|
||||
KeyPair keyPair = Cryptography.generateKeyPair();
|
||||
String publicKey = Cryptography.exportPublicKey(keyPair);
|
||||
String privateKey = Cryptography.exportPrivateKey(keyPair);
|
||||
String encryptedContent = Cryptography.encrypt(originalContent, publicKey);
|
||||
|
||||
//Act
|
||||
String decryptedContent = Cryptography.decrypt(encryptedContent, privateKey);
|
||||
|
||||
// Assert
|
||||
Assertions.assertNotNull(decryptedContent, "Decrypted content should not be null");
|
||||
Assertions.assertEquals(originalContent, decryptedContent, "Decrypted content should be equals to original content");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecrypt_FailureWithInvalidPrivateKey() {
|
||||
// Arrange
|
||||
String encryptedContent = "ExampleEncryptedContent";
|
||||
String invalidPrivateKey = "InvalidPrivateKey";
|
||||
|
||||
// Act & Assert
|
||||
Assertions.assertThrows(CryptographyException.class, () -> Cryptography.decrypt(encryptedContent, invalidPrivateKey), "decrypt should throw CryptographyException with invalid private key");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecrypt_FailureWithNullPrivateKey() {
|
||||
// Arrange
|
||||
String encryptedContent = "ExampleEncryptedContent";
|
||||
String nullPrivateKey = null;
|
||||
|
||||
// Act & Assert
|
||||
Assertions.assertThrows(NullPointerException.class, () -> Cryptography.decrypt(encryptedContent, nullPrivateKey), "decrypt should throw NullPointerException when private key is null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncrypt_NullContent_shouldThrowCryptographyException() throws CryptographyException {
|
||||
// Arrange
|
||||
String content = null;
|
||||
KeyPair keyPair = Cryptography.generateKeyPair();
|
||||
String publicKey = Cryptography.exportPublicKey(keyPair);
|
||||
|
||||
// Assert
|
||||
assertThrows(NullPointerException.class, () -> Cryptography.encrypt(content, publicKey), "Encrypt should throw NullPointerException when content is null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncrypt_EmptyContent_shouldRunSuccessfully() throws CryptographyException {
|
||||
// Arrange
|
||||
String content = "";
|
||||
KeyPair keyPair = Cryptography.generateKeyPair();
|
||||
String publicKey = Cryptography.exportPublicKey(keyPair);
|
||||
|
||||
// Act
|
||||
String encryptedContent = Cryptography.encrypt(content, publicKey);
|
||||
|
||||
// Assert
|
||||
assertNotNull(encryptedContent, "Encrypted content should not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncrypt_LargeContent_shouldRunSuccessfully() throws CryptographyException {
|
||||
// Arrange
|
||||
String content = String.join("", Collections.nCopies(50000, "a"));
|
||||
KeyPair keyPair = Cryptography.generateKeyPair();
|
||||
String publicKey = Cryptography.exportPublicKey(keyPair);
|
||||
|
||||
// Act
|
||||
String encryptedContent = Cryptography.encrypt(content, publicKey);
|
||||
|
||||
// Assert
|
||||
assertNotNull(encryptedContent, "Encrypted content should not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncrypt_InvalidPublicKey_ShouldThrowException() {
|
||||
// Arrange
|
||||
String content = "ExampleContent";
|
||||
String invalidPublicKey = "InvalidPublicKey";
|
||||
|
||||
// Assert
|
||||
assertThrows(CryptographyException.class, () -> Cryptography.encrypt(content, invalidPublicKey), "Encrypt should throw CryptographyException when public key is invalid");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package net.nosial.socialclient.classes;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.squareup.okhttp.*;
|
||||
import net.nosial.socialclient.abstracts.RpcResult;
|
||||
import net.nosial.socialclient.exceptions.CryptographyException;
|
||||
import net.nosial.socialclient.exceptions.ResolutionException;
|
||||
import net.nosial.socialclient.objects.RpcRequest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class RpcClientTest {
|
||||
|
||||
@Test
|
||||
public void testSendRequest() {
|
||||
RpcClient rpcClient = null;
|
||||
|
||||
try
|
||||
{
|
||||
rpcClient = new RpcClient("n64.cc");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
fail(e.getMessage(), e);
|
||||
}
|
||||
|
||||
RpcResult result = rpcClient.sendRequest(new RpcRequest("ping", "abcd123"));
|
||||
assertNotNull(result);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue