There is a mechanism called Provider in java.security that allows you to add encryption and signature implementations, but probably only a few people use it, so there aren't many sites that explain it.
Sample.java
package io.github.tshibata.sample;
import java.security.*;
public class Sample {
static void test(byte[] data, KeyPair pair) throws Exception {
Signature sig = Signature.getInstance("SHA256withRSA");
//I'll sign
sig.initSign(pair.getPrivate());
sig.update(data);
byte[] s = sig.sign();
//I will output it in Base64
System.out.println(java.util.Base64.getEncoder().encodeToString​(s));
//I'll verify
sig.initVerify(pair.getPublic());
sig.update(data);
System.out.println(sig.verify(s));
}
public static void main(String[] args) throws Exception {
//I'll make a key
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
KeyPair pair = gen.generateKeyPair();
// "hello"I will sign and verify
test("hello".getBytes(), pair);
}
}
For signatures, the Signature class is in charge.
MySignature.java
package io.github.tshibata.sample;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.math.BigInteger;
public class MySignature extends Signature {
// echo | openssl dgst -sign private.pem | openssl rsautl -verify -inkey private.pem -raw | head --bytes=-32 | base64
private static byte[] padding = java.util.Base64.getDecoder().decode(
"AAH/////////////////////////////////////////////////////////////////////////" +
"////////////////////////////////////////////////////////////////////////////" +
"////////////////////////////////////////////////////////////////////////////" +
"////////////////////////////////////////////ADAxMA0GCWCGSAFlAwQCAQUABCA=");
private RSAPrivateKey privateKey;
private RSAPublicKey publicKey;
private MessageDigest messageDigest;
public MySignature() {
super("SHA256withRSA");
try {
messageDigest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException exc) {
throw new UnsupportedOperationException(exc);
}
}
protected Object engineGetParameter(String param) throws InvalidParameterException {
throw new UnsupportedOperationException("engineGetParameter");
}
protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
throw new UnsupportedOperationException("engineSetParameter");
}
protected void engineInitSign(PrivateKey key) throws InvalidKeyException {
messageDigest.reset();
privateKey = (RSAPrivateKey) key;
}
protected void engineInitVerify(PublicKey key) throws InvalidKeyException {
messageDigest.reset();
publicKey = (RSAPublicKey) key;
}
protected void engineUpdate(byte[] data, int offset, int length) throws SignatureException {
messageDigest.update(data, offset, length);
}
protected void engineUpdate(byte data) throws SignatureException {
messageDigest.update(data);
}
protected byte[] engineSign() throws SignatureException {
byte[] data = new byte[256];
System.arraycopy(padding, 0, data, 0, padding.length);
byte[] digest = messageDigest.digest();
System.arraycopy(digest, 0, data, padding.length, digest.length);
BigInteger m = new BigInteger(1, data);
BigInteger d = privateKey.getPrivateExponent();
BigInteger n = privateKey.getModulus();
BigInteger c = m.modPow(d, n);
byte[] signed = new byte[256];
for (int i = signed.length - 1; 0 <= i; i--) {
signed[i] = c.byteValue();
c = c.shiftRight(8);
}
return signed;
}
protected boolean engineVerify(byte[] signed) throws SignatureException {
BigInteger c = new BigInteger(1, signed);
BigInteger e = publicKey.getPublicExponent();
BigInteger n = publicKey.getModulus();
BigInteger m = c.modPow(e, n);
byte[] data = new byte[256];
System.arraycopy(padding, 0, data, 0, padding.length);
byte[] digest = messageDigest.digest();
System.arraycopy(digest, 0, data, padding.length, digest.length);
for (int i = data.length - 1; 0 <= i; i--) {
if (data[i] != m.byteValue()) {
return false;
}
m = m.shiftRight(8);
}
return true;
}
}
The key length is fixed at 2048bit (256byte). All you have to do is create a hash, pad it, raise it to the exponent, divide it by modulus, and calculate the remainder. Padding is a magic or a rule.
echo | openssl dgst -sign private.pem | openssl rsautl -verify -inkey private.pem -raw | head --bytes=-32 | base64
I made it. "The remainder of riding exponent and dividing by modulus" is the key to RSA, and was found by great scholars in the past. To get this used in
MyProvider.java
package io.github.tshibata.sample;
import java.security.*;
public class MyProvider extends Provider {
public MyProvider() {
super("MyProvider", "1.0", "My provider");
put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.11", "SHA256withRSA");
put("Signature.SHA256withRSA SupportedKeyClasses", "java.security.interfaces.RSAPublicKey|java.security.interfaces.RSAPrivateKey");
put("Signature.SHA256withRSA", "io.github.tshibata.sample.MySignature");
put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA");
}
}
Create a subclass of Provider called
Sample.java
package io.github.tshibata.sample;
import java.security.*;
public class Sample {
static void test(byte[] data, KeyPair pair) throws Exception {
Signature sig = Signature.getInstance("SHA256withRSA");
//I'll sign
sig.initSign(pair.getPrivate());
sig.update(data);
byte[] s = sig.sign();
//I will output it in Base64
System.out.println(java.util.Base64.getEncoder().encodeToString​(s));
//I'll verify
sig.initVerify(pair.getPublic());
sig.update(data);
System.out.println(sig.verify(s));
}
public static void main(String[] args) throws Exception {
//I'll make a key
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
KeyPair pair = gen.generateKeyPair();
// "hello"I will sign and verify
test("hello".getBytes(), pair);
//I'll try using MyProvider
Security.insertProviderAt(new MyProvider(), 1);
test("hello".getBytes(), pair);
}
}
I will register it before signing and verification. If the second argument of insertProviderAt is set to 1 in priority, it will be used with the highest priority.
Recommended Posts