I use this code generate a private and a public certificate:
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
keyPairGenerator.initialize(2048, new SecureRandom());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
I tried to store the private and public key into DB using direct toString() like this keyPair.getPublic().toString(), but I get values into DB:
RSA Private CRT Key [5e:cf:06:fc:ff:52:cd:16:3a:b4:28:42:dc:c2:5b:9c:e3:fc:a8:c1],[56:66:d1:a4]
modulus: b3b9ae25337bedf971aa2767412eec0f2d7792873e9c500eb0cff747e5f80b182f3f29ed97b28471de8c68346354bb4fdbe8f8f3ba1b09e745e3235ffdd2ff11efe379cca27914a6a6754e6f967f21358c05462bc20426f7f8c4df0f6632673897b17d8a0592b6291c9281dd3e94b0ed9c8f02dc02df9d8ae8dc2688dfadcb2a94840b2b22a4528e1169b79056288b111a70ebebc8e838e17943c0b88b103c27bbaa6c40624babde1e97e86a42e532cb58c0948705b8943a0f203d648828848e20e5d4ea0aaab8b06d804732352c8eae01cc9672e5140e34dc5054d073d8e617b211fa80ccac810045d4def198e587ff0b7ba858dfa11fd3682cdfeb88d360df
public exponent: 10001
Which is not correct. What is the proper way to get the certificates as a String and store them in DB?
EDIT: I tried this:
import io.jsonwebtoken.lang.Assert;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.jupiter.api.Test;
import javax.xml.bind.DatatypeConverter;
import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Base64;
public class CertificateGenerateTestApiTest {
String data; // complete certificate and metadata. We need to get of when we generate a certificate
String signature; // signature of the certificate
@Test
public void certificateGenerationTest()
{
// Generate private and public keys
KeyPair keyPair = generateCertificate();
PublicKey publicCertificate = keyPair.getPublic();
// Problem 1 - think how to populate "data" variable from public certificate
// Problem 2 - think how to populate "signature" variable from public certificate
// Get public key as byte[] in order later on to be stored into DB
byte[] originalPublicCertificate = privateCertificateToByte(publicCertificate);
// Let's say we received some certificate by some web client, and we want to verify it against our original certificate
byte[] secondPublicCertificateReceivedByClient = cloneByteArrayData(originalPublicCertificate);
// Verify certificate
Assert.isTrue(verifyCertificate(secondPublicCertificateReceivedByClient));
}
private KeyPair generateCertificate()
{
try {
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
keyPairGenerator.initialize(2048, new SecureRandom());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
return keyPair;
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
e.printStackTrace();
}
return null;
}
private boolean verifyCertificate(byte[] secondPublicCertificateReceivedByClient)
{
PublicKey publicKey = getKeyPayload(byteToString(secondPublicCertificateReceivedByClient));
boolean verified = false;
try {
verified = verifySignature(data.getBytes("UTF8"), publicKey,
DatatypeConverter.parseHexBinary(signature));
} catch (SignatureException | UnsupportedEncodingException e) {
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
}
return verified;
}
private boolean verifySignature(byte[] data, PublicKey key, byte[] signature)
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Signature signer = Signature.getInstance("SHA1withDSA");
signer.initVerify(key);
signer.update(data);
try {
if (signer.verify(signature)) {
return true;
}
} catch (SignatureException e) {
e.printStackTrace();
}
return false;
}
private PublicKey getKeyPayload(String certificateString) {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
ByteArrayInputStream bis =
new ByteArrayInputStream(certificateString.getBytes(StandardCharsets.UTF_8));
Certificate certificate = null;
while (bis.available() > 0) {
certificate = cf.generateCertificate(bis);
}
return (certificate == null) ? null : certificate.getPublicKey();
} catch (CertificateException e) {
e.printStackTrace();
}
return null;
}
private String byteToString(byte[] publicKeyBytes)
{
return Base64.getEncoder().encodeToString(publicKeyBytes);
}
private byte[] privateCertificateToByte(PublicKey publicCertificate)
{
return publicCertificate.getEncoded();
}
/**
* Clone byte[] data
*/
private byte[] cloneByteArrayData(byte[] sourceArray)
{
int sourceOffset = 0; /* Starting index in the source array */;
int destinationOffset = 0; // Starting index in the destination array
byte[] destinationArray = new byte[sourceArray.length];
for (int i = 0; i < sourceArray.length; i++) {
destinationArray[destinationOffset + i] = sourceArray[sourceOffset + i];
}
return destinationArray;
}
}
keyPair.getPublic().getEncoded()the public key is returned as DER encoded key in the common X.509/SPKI format (byte[]) which can be stored in binary or, if a string is preferred, Base64 encoded.keyPair.getPrivate().getEncoded()returns the private key DER encoded in the popular PKCS#8 format (byte[]). Note that keys and certificates are not the same thing.getEncoded()exports the keys asbyte[]. You can store these in the DB either raw or you apply a binary-to-text encoding like Base64 and store the data as string. Base64 has an efficiency of 75% (three input bits become 4 output bits), so it increases the amount of data, which is why storing raw data is more efficient (see also e.g. binary data in database, blob vs compressed base64).PKCS8EncodedKeySpecandX509EncodedKeySpec. E.g. here.