Sign data using PKCS #7 in JAVA - java

I want to sign a text file (may be a .exe file or something else in the future)
using PKCS#7 and verify the signature using Java.
What do I need to know?
Where will I find an API (.jar and documentation)?
What are the steps I need to follow in order to sign data and verify the data?
Please provide me code snippet if possible.

I reckon you need the following 2 Bouncy Castle jars to generate the PKCS7 digital signature:
bcprov-jdk15on-147.jar (for JDK 1.5 - JDK 1.7)
bcmail-jdk15on-147.jar (for JDK 1.5 - JDK 1.7)
You can download the Bouncy Castle jars from here.
You need to setup your keystore with the public & private key pair.
You need only the private key to generate the digital signature & the public key to verify it.
Here's how you'd pkcs7 sign content (Exception handling omitted for brevity) :
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Base64;
public final class PKCS7Signer {
private static final String PATH_TO_KEYSTORE = "/path/to/keyStore";
private static final String KEY_ALIAS_IN_KEYSTORE = "My_Private_Key";
private static final String KEYSTORE_PASSWORD = "MyPassword";
private static final String SIGNATUREALGO = "SHA1withRSA";
public PKCS7Signer() {
}
KeyStore loadKeyStore() throws Exception {
KeyStore keystore = KeyStore.getInstance("JKS");
InputStream is = new FileInputStream(PATH_TO_KEYSTORE);
keystore.load(is, KEYSTORE_PASSWORD.toCharArray());
return keystore;
}
CMSSignedDataGenerator setUpProvider(final KeyStore keystore) throws Exception {
Security.addProvider(new BouncyCastleProvider());
Certificate[] certchain = (Certificate[]) keystore.getCertificateChain(KEY_ALIAS_IN_KEYSTORE);
final List<Certificate> certlist = new ArrayList<Certificate>();
for (int i = 0, length = certchain == null ? 0 : certchain.length; i < length; i++) {
certlist.add(certchain[i]);
}
Store certstore = new JcaCertStore(certlist);
Certificate cert = keystore.getCertificate(KEY_ALIAS_IN_KEYSTORE);
ContentSigner signer = new JcaContentSignerBuilder(SIGNATUREALGO).setProvider("BC").
build((PrivateKey) (keystore.getKey(KEY_ALIAS_IN_KEYSTORE, KEYSTORE_PASSWORD.toCharArray())));
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").
build()).build(signer, (X509Certificate) cert));
generator.addCertificates(certstore);
return generator;
}
byte[] signPkcs7(final byte[] content, final CMSSignedDataGenerator generator) throws Exception {
CMSTypedData cmsdata = new CMSProcessableByteArray(content);
CMSSignedData signeddata = generator.generate(cmsdata, true);
return signeddata.getEncoded();
}
public static void main(String[] args) throws Exception {
PKCS7Signer signer = new PKCS7Signer();
KeyStore keyStore = signer.loadKeyStore();
CMSSignedDataGenerator signatureGenerator = signer.setUpProvider(keyStore);
String content = "some bytes to be signed";
byte[] signedBytes = signer.signPkcs7(content.getBytes("UTF-8"), signatureGenerator);
System.out.println("Signed Encoded Bytes: " + new String(Base64.encode(signedBytes)));
}
}

PKCS#7 is known as CMS now (Cryptographic Message Syntax), and you will need the Bouncy Castle PKIX libraries to create one. It has ample documentation and a well established mailing list.
I won't supply code snippet, it is against house rules. Try yourself first.

Related

Rebuild of ED25519 keys with Bouncy Castle (Java)

The latest (beta) version of Bouncy Castle (bcprov-jdk15on-161b20.jar) supports ED25519 and ED448 EC cryptography for signing purposes. I setup this full working example and it works as expected.
My question: did I rebuild the private and public keys correctly as I didn't found any example in the bc-tests ? I expected that I have to use some Spec-functionality like "X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content)" for RSA-key rebuilding but my code is working.
package bc;
// original source: https://github.com/bcgit/bc-java/blob/master/core/src/test/java/org/bouncycastle/crypto/test/Ed25519Test.java
// needs bouncy castle beta: bcprov-jdk15on-161b20.jar (version 1.605)
// tested with Java 8 Build 191 x64
// this is a full working example for generating, signing, verififying with ed25519 keys
// code: https://github.com/java-crypto/Bouncy-Castle
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;
import java.io.UnsupportedEncodingException;
import javax.xml.bind.DatatypeConverter;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator;
import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters;
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
import org.bouncycastle.crypto.signers.Ed25519Signer;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class Ed25519Bc {
public static void main(String[] args) throws DataLengthException, CryptoException, UnsupportedEncodingException {
System.out.println("ED25519 with BC");
Security.addProvider(new BouncyCastleProvider());
Provider provider = Security.getProvider("BC");
System.out.println("Provider :" + provider.getName() + " Version: " + provider.getVersion());
// generate ed25519 keys
SecureRandom RANDOM = new SecureRandom();
Ed25519KeyPairGenerator keyPairGenerator = new Ed25519KeyPairGenerator();
keyPairGenerator.init(new Ed25519KeyGenerationParameters(RANDOM));
AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
Ed25519PrivateKeyParameters privateKey = (Ed25519PrivateKeyParameters) asymmetricCipherKeyPair.getPrivate();
Ed25519PublicKeyParameters publicKey = (Ed25519PublicKeyParameters) asymmetricCipherKeyPair.getPublic();
// the message
byte[] message = "Message to sign".getBytes("utf-8");
// create the signature
Signer signer = new Ed25519Signer();
signer.init(true, privateKey);
signer.update(message, 0, message.length);
byte[] signature = signer.generateSignature();
// verify the signature
Signer verifier = new Ed25519Signer();
verifier.init(false, publicKey);
verifier.update(message, 0, message.length);
boolean shouldVerify = verifier.verifySignature(signature);
// output
byte[] privateKeyEncoded = privateKey.getEncoded();
byte[] publicKeyEncoded = publicKey.getEncoded();
System.out.println("privateKey Length :" + privateKeyEncoded.length + " Data:"
+ DatatypeConverter.printHexBinary(privateKeyEncoded));
System.out.println("publicKey Length :" + publicKeyEncoded.length + " Data:"
+ DatatypeConverter.printHexBinary(publicKeyEncoded));
System.out.println(
"signature Length :" + signature.length + " Data:" + DatatypeConverter.printHexBinary(signature));
System.out.println("signature correct :" + shouldVerify);
// rebuild the keys
System.out.println("Rebuild the keys and verify the signature with rebuild public key");
Ed25519PrivateKeyParameters privateKeyRebuild = new Ed25519PrivateKeyParameters(privateKeyEncoded, 0);
Ed25519PublicKeyParameters publicKeyRebuild = new Ed25519PublicKeyParameters(publicKeyEncoded, 0);
byte[] privateKeyRebuildEncoded = privateKeyRebuild.getEncoded();
System.out.println("privateKey Length :" + privateKeyRebuild.getEncoded().length + " Data:"
+ DatatypeConverter.printHexBinary(privateKeyRebuild.getEncoded()));
byte[] publicKeyRebuildEncoded = publicKeyRebuild.getEncoded();
System.out.println("publicKey Length :" + publicKeyRebuild.getEncoded().length + " Data:"
+ DatatypeConverter.printHexBinary(publicKeyRebuild.getEncoded()));
// compare the keys
System.out.println("private Keys Equal:" + Arrays.equals(privateKeyEncoded, privateKeyRebuildEncoded));
System.out.println("public Keys Equal :" + Arrays.equals(publicKeyEncoded, publicKeyRebuildEncoded));
// verify the signature with rebuild public key
Signer verifierRebuild = new Ed25519Signer();
verifierRebuild.init(false, publicKeyRebuild);
verifierRebuild.update(message, 0, message.length);
boolean shouldVerifyRebuild = verifierRebuild.verifySignature(signature);
System.out.println("signature correct :" + shouldVerifyRebuild + " with rebuild public key");
}
}
This is the console output that shows the properly rebuild keys:
ED25519 with BC
Provider :BC Version: 1.605
privateKey Length :32 Data:F6A1F3A0B8F44EE64ACE636AFCA262F656160A728C042E3F98F9A0FD45717DE7
publicKey Length :32 Data:858C2D6D5910B8AA7B52F7DF8E5806DAD3A7E43DC19C5A548F241BD8B82510FE
signature Length :64 Data:4D402B0095F6692742DCACB0C2C39BFB70A5687F162DFAB3721A660D2259C96B972DF41B97502347E534FAD8D59496811CDFFFA831264ECBB1429439CF350E08
signature correct :true
Rebuild the keys and verify the signature with rebuild public key
privateKey Length :32 Data:F6A1F3A0B8F44EE64ACE636AFCA262F656160A728C042E3F98F9A0FD45717DE7
publicKey Length :32 Data:858C2D6D5910B8AA7B52F7DF8E5806DAD3A7E43DC19C5A548F241BD8B82510FE
private Keys Equal:true
public Keys Equal :true
signature correct :true with rebuild public key
I'm also doing some research into Ed25519 so I've been looking at the BouncyCastle implementation along with Tink and libsodium; I certainly can't see anything wrong with how you're rebuilding the key pair, it seems be consistent with how BouncyCastle is using it in other places: OpenSSHPrivateKeyUtil and Ed25519Test.
Pure BouncyCastle
I re-wrote your test using the key pair and example signature from [RFC8037][3], which works fine as follows (Java 11):
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
import org.bouncycastle.crypto.signers.Ed25519Signer;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import static org.junit.Assert.assertEquals;
public class BouncyCastleTest {
private static final Logger LOG = LoggerFactory.getLogger(BouncyCastleTest.class);
#Test
public void testBouncyCastle() throws CryptoException {
// Test case defined in https://www.rfc-editor.org/rfc/rfc8037
var msg = "eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc".getBytes(StandardCharsets.UTF_8);
var expectedSig = "hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg";
var privateKeyBytes = Base64.getUrlDecoder().decode("nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A");
var publicKeyBytes = Base64.getUrlDecoder().decode("11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo");
var privateKey = new Ed25519PrivateKeyParameters(privateKeyBytes, 0);
var publicKey = new Ed25519PublicKeyParameters(publicKeyBytes, 0);
// Generate new signature
Signer signer = new Ed25519Signer();
signer.init(true, privateKey);
signer.update(msg, 0, msg.length);
byte[] signature = signer.generateSignature();
var actualSignature = Base64.getUrlEncoder().encodeToString(signature).replace("=", "");
LOG.info("Expected signature: {}", expectedSig);
LOG.info("Actual signature : {}", actualSignature);
assertEquals(expectedSig, actualSignature);
}
}
JCA
You can also achieve the same thing using JCA, in this example my keypair are in the 'raw' format (i.e. the X and D coordinates):
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.Security;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import static org.junit.Assert.assertEquals;
public class Ed25519JCA {
private static final Logger LOG = LoggerFactory.getLogger(Ed25519JCA.class);
#Test
public void testEd25519WithJCA() throws Exception {
Security.addProvider(new BouncyCastleProvider());
// Test case defined in https://www.rfc-editor.org/rfc/rfc8037
var msg = "eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc".getBytes(StandardCharsets.UTF_8);
var expectedSig = "hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg";
// Both formatted as 32bit raw key values (x and d)
var privateKeyBytes = Base64.getUrlDecoder().decode("nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A");
var publicKeyBytes = Base64.getUrlDecoder().decode("11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo");
var keyFactory = KeyFactory.getInstance("Ed25519");
// Wrap public key in ASN.1 format so we can use X509EncodedKeySpec to read it
var pubKeyInfo = new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), publicKeyBytes);
var x509KeySpec = new X509EncodedKeySpec(pubKeyInfo.getEncoded());
var jcaPublicKey = keyFactory.generatePublic(x509KeySpec);
// Wrap private key in ASN.1 format so we can use
var privKeyInfo = new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), new DEROctetString(privateKeyBytes));
var pkcs8KeySpec = new PKCS8EncodedKeySpec(privKeyInfo.getEncoded());
var jcaPrivateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// Generate new signature
var dsa = Signature.getInstance("EdDSA"); // Edwards digital signature algorithm
dsa.initSign(jcaPrivateKey);
dsa.update(msg, 0, msg.length);
byte[] signature = dsa.sign();
var actualSignature = Base64.getUrlEncoder().encodeToString(signature).replace("=", "");
LOG.info("Expected signature: {}", expectedSig);
LOG.info("Actual signature : {}", actualSignature);
assertEquals(expectedSig, actualSignature);
}
}
Just for completeness, you can also generate the keypair using JCA in the first place, this avoids lots of format conversion:
Security.addProvider(new BouncyCastleProvider());
var keyPair = KeyPairGenerator.getInstance("Ed25519").generateKeyPair();

Instantiate java.security classes PrivateKey and X509Certificate from .key and .cer files

The original goal is:
Generate a https url where one of parameters is PKCS7 detached signature (RSA, SHA-256, UTF-8, BASE64).
What do I have:
private key (.key file begin with "-----BEGIN RSA PRIVATE KEY-----",
end like this "kIng0BFt5cjuur81oQqGJgvU+dC4vQio+hVc+eAQTGmNQJV56vAHcq4v
-----END RSA PRIVATE KEY-----")
self signed certificate (.cer file begin with "-----BEGIN CERTIFICATE-----",
end like this "xwRtGsSkfOFL4ehKn/K7mgQEc1ZVPrxTC7C/g+7grbKufvqNmsYW4w==
-----END CERTIFICATE-----")
data to sign
I found a java code that do almost what I need.
Method signature:
public static String sign(PrivateKey privateKey,
X509Certificate certificate,
String data);
Now I'm stuck on how to get PrivateKey and X509Certficiate classes from given files.
I looked at many examples and got confused by these moments:
1.
KeyStore ks = KeyStore.getInstance("pkcs12");
or
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
Didn't find alternatives for PKCS7 standard.
A snippet of method that builds PrivateKey using bouncycastle library:
inputStream = Files.newInputStream(privateKeyFile.toPath());
reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
pemParser = new PEMParser(reader);
PEMDecryptorProvider decryptorProvider = new JcePEMDecryptorProviderBuilder()
.setProvider(PROVIDER)
.build(privateKeyPassword.toCharArray());
PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair) pemParser.readObject();
PEMKeyPair keyPair = encryptedKeyPair.decryptKeyPair(decryptorProvider);
...
In this example I have to provide some privateKeyPassword to PEMDecryptorProvider. What is the point of this password and where can I get it?
From keyPair value I can get both privateKey and publicKey.
What is the connection between publicKey from PEMKeyPair and my certificate ? Are they the same?
Any help will be appreciated, thanks!
You don't need bouncycastle to read in the public key as Java's CertificateFactory directly supports the format of your .cer file.
The private key appears to be in a PKCS1 format that openssl can produce. If you wish to keep that format this answer shows how to extract the private key. Combining the two, here is a short snippet to read in a certificate and a private key.
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import java.io.FileInputStream;
import java.io.FileReader;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public class Main {
private static PrivateKey readPrivateKey(String filename) throws Exception {
PEMParser pemParser = new PEMParser(new FileReader(filename));
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
PEMKeyPair pemKeyPair = (PEMKeyPair) pemParser.readObject();
KeyPair kp = converter.getKeyPair(pemKeyPair);
return kp.getPrivate();
}
private static X509Certificate readCertificate(String filename) throws Exception {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
return (X509Certificate) certificateFactory.generateCertificate(new FileInputStream(filename));
}
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
PrivateKey privateKey = readPrivateKey("myKey.priv");
X509Certificate cert = readCertificate("mycert.cer");
}
}

Bouncy castle with spring boot

I am trying to use Https over http, I generated the CA certificate with bouncy castle and generated from the .cer file the jks file, spring boot started successfully with the properties provided with an embedded tomcat.
The problem is that I'm getting always insecure ssl certificate from the browser (for example from chrome : This CA Root certificate is not trusted because it is not in the Trusted Root Certification Authorities store.) while I add the trust store programatically with another config class.
Here is my code:
import org.apache.commons.codec.binary.Base64; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.cmp.CertRepMessage; import org.bouncycastle.asn1.cmp.PKIBody; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.cert.CertException; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.cmp.CMPException; import org.bouncycastle.cert.cmp.ProtectedPKIMessage; import org.bouncycastle.cert.cmp.ProtectedPKIMessageBuilder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.ContentVerifierProvider; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.math.BigInteger; import java.security.*; import java.security.cert.X509Certificate; import java.util.Date;
public class generateService {
private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
private static X509CertificateHolder makeV3Certificate(KeyPair subKP, String _subDN, KeyPair issKP, String _issDN)
throws GeneralSecurityException, IOException, OperatorCreationException, CertException
{
PublicKey subPub = subKP.getPublic();
PrivateKey issPriv = issKP.getPrivate();
PublicKey issPub = issKP.getPublic();
X509v3CertificateBuilder v1CertGen = new JcaX509v3CertificateBuilder(
new X500Name(_issDN),
BigInteger.valueOf(System.currentTimeMillis()),
new Date(System.currentTimeMillis()),
new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)),
new X500Name(_subDN),
subPub);
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA").setProvider(BC).build(issPriv);
X509CertificateHolder certHolder = v1CertGen.build(signer);
ContentVerifierProvider verifier = new JcaContentVerifierProviderBuilder().setProvider(BC).build(issPub);
return certHolder;
}
public X509Certificate test(KeyPair kp) throws OperatorCreationException, GeneralSecurityException, CertException, IOException, CMPException {
Provider bcProvider = new BouncyCastleProvider();
Security.addProvider(bcProvider);
X509CertificateHolder cert = makeV3Certificate(kp, "CN=CA", kp, "CN=CA");
GeneralName sender = new GeneralName(new X500Name("CN=CA"));
GeneralName recipient = new GeneralName(new X500Name("CN=CA"));
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(BC).build(kp.getPrivate());
ProtectedPKIMessage message = new ProtectedPKIMessageBuilder(sender, recipient)
.setBody(new PKIBody(PKIBody.TYPE_INIT_REP, CertRepMessage.getInstance(new DERSequence(new DERSequence()))))
.addCMPCertificate(cert)
.build(signer);
X509Certificate jcaCert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(message.getCertificates()[0]);
return jcaCert;
}
public void generateJKS() throws GeneralSecurityException, IOException, OperatorCreationException, CMPException, CertException {
Provider bcProvider = new BouncyCastleProvider();
Security.addProvider(bcProvider);
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
X509Certificate certificate = test(keyPair);
final FileOutputStream os = new FileOutputStream("E:\\cert4.cer");
os.write("-----BEGIN CERTIFICATE-----\n".getBytes("US-ASCII"));
os.write(Base64.encodeBase64(certificate.getEncoded(), true));
os.write("-----END CERTIFICATE-----\n".getBytes("US-ASCII"));
os.close();
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null,null);
X509Certificate[] certChain = new X509Certificate[1];
certChain[0] = certificate;
keyStore.setKeyEntry("myaliaskey", (Key)keyPair.getPrivate(), "secret".toCharArray(), certChain);
OutputStream outputStream = new FileOutputStream("E:\\keystoreRSA4.jks");
keyStore.store(outputStream, "secret".toCharArray());
outputStream.flush();
outputStream.close();
}
public static void main(String[] args) throws Exception {
new generateService().generateJKS();
} }
Application.properties:
server.port = 8443 server.http.port = 8080 server.ssl.key-store = E:\\keystoreRSA4.jks server.ssl.key-password = secret server.ssl.keyAlias = myaliaskey
/***************************************************************************/
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
#Configuration public class SSLConfig {
#PostConstruct
private void configureSSL() {
System.setProperty("https.protocols", "TLSv1.2");
System.setProperty("javax.net.ssl.trustStore", "E:\\keystoreRSA4.jks");
System.setProperty("javax.net.ssl.keyStore", "E:\\keystoreRSA4.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "secret");
} }
import org.apache.catalina.Context; import org.apache.catalina.connector.Connector; import org.apache.tomcat.util.descriptor.web.SecurityCollection; import org.apache.tomcat.util.descriptor.web.SecurityConstraint; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
#Configuration public class TomcatConfig {
#Value("${server.http.port}")
private int httpPort;
#Value("${server.port}")
private int httpsPort;
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() {
#Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(initiateHttpConnector());
return tomcat;
}
public Connector initiateHttpConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("https");
connector.setPort(httpPort);
connector.setSecure(true);
connector.setRedirectPort(httpsPort);
return connector;
} }
Any help will be appreciated and thank you in advance.
how i can figure it to let the browser accept it as trust certificate?
Fortunately, you can't. There's a discussion about the topic in this question:
If we simply said "HTTPS is now possible with self-signed certificates", your browser could not distinguish whether a site you are trying to visit has a self-signed cert because it is supposed to, or because you are being attacked. Thus, it would decrease security.
You have to add an exception to your browser to accept your certificate. Normally during development. For production use, you have to buy a certificate from a trusted vendor like Verisign.
To add an exception to your browser, please see this other question.
Cheers!

DER Decode ECDSA Signature in Java

I have generated an ECDSA signature in Java and I would like to get the R and S values from it. It is my understanding that the signature I have generated is DER encoded. Can someone please provide me with some Java code (maybe using Bouncy Castle) to retrieve the R and S values as BigIntegers?
Note: In case it helps, I generated the signature using a built in provider via the JCE's Signature class and the signature lengths for my P_256 EC key pair hover between 70 and 72 bytes usually.
I was able to solve this myself. In case it helps anyone here is how I did it (most exception handling has been stripped for readability):
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Security;
import java.security.Signature;
import java.security.spec.ECGenParameterSpec;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class DecodeEcdsaSignature {
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
byte[] signature = getSignature();
ASN1Primitive asn1 = toAsn1Primitive(signature);
if (asn1 instanceof ASN1Sequence) {
ASN1Sequence asn1Sequence = (ASN1Sequence) asn1;
ASN1Encodable[] asn1Encodables = asn1Sequence.toArray();
for (ASN1Encodable asn1Encodable : asn1Encodables) {
ASN1Primitive asn1Primitive = asn1Encodable.toASN1Primitive();
if (asn1Primitive instanceof ASN1Integer) {
ASN1Integer asn1Integer = (ASN1Integer) asn1Primitive;
BigInteger integer = asn1Integer.getValue();
System.out.println(integer.toString());
}
}
}
}
private static ASN1Primitive toAsn1Primitive(byte[] data) throws Exception
{
try (ByteArrayInputStream inStream = new ByteArrayInputStream(data);
ASN1InputStream asnInputStream = new ASN1InputStream(inStream);)
{
return asnInputStream.readObject();
}
}
private static byte[] getSignature() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDSA");
ECGenParameterSpec ecParameterSpec = new ECGenParameterSpec("P-256");
keyPairGenerator.initialize(ecParameterSpec);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initSign(keyPair.getPrivate());
signature.update("message to sign".getBytes("UTF-8"));
return signature.sign();
}
}

Verifying signature of a file using the certificates available in store

I am totally new to security and signature verification and so far I couldn't find a place which explained the basics of signature verification. I need to verify the signature of a file by obtaining the public key from the appropriate certificate available from certificate store. the tutorial in Java (https://docs.oracle.com/javase/tutorial/security/apisign/versig.html) doesn't teach how to obtain a certificate from the trusted certificate store and verify using that. I went through Bouncy castle WIKI http://www.bouncycastle.org/wiki/display/JA1/BC+Version+2+APIs but its not really explanatory for a beginner. How do I do this? Given a signed file, how can I check for its public key from the certificate store and verify if its the right person who has sent the file? Please advice.
Because you did not provide what build management you use, I assume it will be Maven.
First, include BouncyCastle in your dependency
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.53</version>
</dependency>
After that, you need to make a util class that you will be using for sign or verify the certificate. Something like this:
package your.pack.location;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* Author: harunalfat
*/
public class SignatureUtils {
private static final Logger log = LogManager.getLogger(SignatureUtils.class);
public static String sign(String plainText, PrivateKey privateKey) throws Exception {
byte[] data = plainText.getBytes("ISO-8859-1");
Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
signature.initSign(privateKey);
signature.update(data);
return Base64.toBase64String(signature.sign());
}
public static boolean verify(String plainText, String signString, PublicKey publicKey) throws Exception{
byte[] data = plainText.getBytes("ISO-8859-1");
Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
signature.initVerify(publicKey);
signature.update(data);
byte[] signByte = Base64.decode(signString);
return signature.verify(signByte);
}
private static PemObject getPemObjectFromResource(String fileLocation) throws IOException {
Resource resource = new ClassPathResource(fileLocation);
InputStream is = resource.getInputStream();
PemObject pemObject = new PemReader(new InputStreamReader( is )).readPemObject();
return pemObject;
}
private static X509EncodedKeySpec getPubKeySpec(String fileLocation) throws IOException, NoSuchAlgorithmException {
PemObject pemObject = getPemObjectFromResource(fileLocation);
byte[] data = pemObject.getContent();
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(data);
return keySpec;
}
private static PKCS8EncodedKeySpec getPriKeySpec(String fileLocation) throws IOException, NoSuchAlgorithmException {
PemObject pemObject = getPemObjectFromResource(fileLocation);
byte[] data = pemObject.getContent();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(data);
return keySpec;
}
public static PublicKey getPublicKey(String fileLocation) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
KeyFactory kf = KeyFactory.getInstance("RSA");
KeySpec keySpec = getPubKeySpec(fileLocation);
return kf.generatePublic(keySpec);
}
public static PrivateKey getPrivateKey(String fileLocation) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
KeyFactory kf = KeyFactory.getInstance("RSA");
KeySpec keySpec = getPriKeySpec(fileLocation);
return kf.generatePrivate(keySpec);
}
}
And then you will use it like this
package your.another.pack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tomcat.util.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import static org.junit.Assert.assertTrue;
/**
* Author: harunalfat
*/
public class SignatureUtilsTest {
private static final Logger log = LogManager.getLogger(SignatureUtilsTest.class);
private static final String PLAIN = "attack at dawn";
#Test
public void testSignAndVerify() throws Exception {
Security.addProvider(new BouncyCastleProvider()); // <-- IMPORTANT!!! This will add BouncyCastle as provider in Java Security
PrivateKey privateKey = SignatureUtils.getPrivateKey("key/private2.pem"); // This is located on src/main/resources/key/private2.pem
PublicKey publicKey = SignatureUtils.getPublicKey("key/public2.pem"); // This is located on src/main/resources/key/public2.pem
// In this example, I use junit test, so it will be on src/test/resources/...
log.info("Private Key : "+Base64.encodeBase64String(privateKey.getEncoded()));
log.info("Public Key : "+Base64.encodeBase64String(publicKey.getEncoded()));
String sign = SignatureUtils.sign(PLAIN, privateKey);
log.info("Plain String : "+PLAIN);
log.info("Sign : "+sign);
boolean result = SignatureUtils.verify(PLAIN,sign, publicKey);
log.info("Result : "+result);
assertTrue(result);
}
}
Of course, you can change the Signature instance with another Algorithm. In my case I use "SHA1WithRSA", but you get the point right?
With this, someone will encrypt their data using their private key, and send it to you. After that, you will verify the data using the public key they give.
In example, Bob send to you message about money amount he sent to you ($5000), and sign it using their private key, become encrypted. When the data arrived to you, you know Bob supposed to send $5000, then you verify the encrypted data with text $5000 and public key Bob share, but is it really $5000 OR does it comes from Bob?
If the data has been changed, OR when someday you ask for some Money to Bob, but the message tapped by someone else and s/he send you the amount message with private key other than Bob's, you will know.
Feel free to ask :)

Categories

Resources