i use EJBCA to generate a certificate from a CommonName. In java code i have generated private key and public key and then the csr for generate the certificate.
Now i save the certificate in PEM format (.cer), but i need also private key so i want save with .pfx or p12 extension. How can i do?
This is my actual code for generate certificate:
KeyPair keys;
try {
keys = KeyTools.genKeys("1024", AlgorithmConstants.KEYALGORITHM_RSA);
//SAVE PRIVKEY
//PrivateKey privKey = keys.getPrivate();
//byte[] privateKeyBytes = privKey.getEncoded();
PKCS10CertificationRequest pkcs10 = new PKCS10CertificationRequest("SHA256WithRSA",
CertTools.stringToBcX509Name("CN=NOUSED"), keys.getPublic(), null, keys.getPrivate());
//Print Privatekey
//System.out.println(keys.getPrivate().toString());
CertificateResponse certenv = ws.certificateRequest(user1,
new String(Base64.encode(pkcs10.getEncoded())),
CertificateHelper.CERT_REQ_TYPE_PKCS10,
null,
CertificateHelper.RESPONSETYPE_CERTIFICATE);
//Certificate certenv = ejbcaraws.pkcs10Req("WSTESTUSER1","foo123",new
//String(Base64.encode(pkcs10.getEncoded())),null);
return certenv.getCertificate ();
}catch (Exception e) {}
and with this i save the certificate:
File file = new File(path+"/"+ x509Cert.getSubjectDN().toString().replace("CN=", "") +".cer");
FileOutputStream os = new FileOutputStream(file);
//os.write("-----BEGIN CERTIFICATE-----\n".getBytes("US-ASCII"));
//os.write(Base64.encode(x509Cert.getEncoded(), true));
//os.write("-----END CERTIFICATE-----".getBytes("US-ASCII"));
//os.close();
PEMWriter pemWriter = new PEMWriter(new PrintWriter(os));
pemWriter.writeObject(x509Cert);
pemWriter.flush();
pemWriter.close();
I never use EJBCA, however if you have the certificate and the private key and you want to create a PKCS12 you can use setKeyEntry(String alias,byte[] key,Certificate[] chain) method from java.security.KeyStore to add the entry, and then store(OutputStream stream, char[] password) method to save the PKCS12 on a file (look at API for more details). Your code could be something like:
import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
public class SamplePKCS12 {
public static void main(String args[]) throws Exception {
String alias = // the alias for your key...
PrivateKey key = // your private key
Certificate[] chain = // an array with your EE certificate to your CA issuer
// create keystore
KeyStore keystore = KeyStore.getInstance("PKCS12");
// add your key and cert
keystore.setKeyEntry(alias, key.getEncoded(), chain);
// save the keystore to file
keystore.store(new FileOutputStream("/tmp/keystore.p12"), "yourPin".toCharArray());
}
}
Note I suppose that you have your certificate and your private key as you said in your question. To work with PKCS12 you need SunJSSE provider (which is normally loaded by default), or alternatively you can use BouncyCastle provider.
Hope this helps,
Related
I wrote a client/server Java program using ServerSocket and Socket objects. I then modified the code to use SSLServerSocket and 'SSLSocket` however I am getting different Exceptions thrown including:
javax.net.ssl.SSLHandshakeException: no cipher suites in common
I am hoping to do as much programmatically as I can. I am also okay with self signed certificates.
One tutorial I followed suggested creating a certificate with the keytool java application, then moving that file into your java project. I have done that with the terminal command keytool -genkey -alias zastore -keyalg RSA -keystore za.store. I assigned the password to be password.
I then call the function System.setProperty in hopes of the SSLSockets working but it still does not.
Here is my server code
public class Server implements Runnable
{
private SSLServerSocket serverSocket;
private int portNumber;
private Thread acceptThread;
private LinkedList<Connection> connections;
private ConnectionListener connectionListener;
public Server(int port, ConnectionListener connectionListener)
{
this.connectionListener = connectionListener;
portNumber = port;
connections = new LinkedList<Connection>();
try
{
System.setProperty("javax.net.ssl.trustStore", "za.store");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
SSLServerSocketFactory sslssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
serverSocket = (SSLServerSocket) sslssf.createServerSocket(portNumber,15);
}
catch (IOException e)
{
e.printStackTrace();
}
}
public void startListening()
{
acceptThread = new Thread(this);
acceptThread.start();
}
public void stopListening()
{
for(Connection c:connections)
{
c.stopListeningAndDisconnect();
}
try
{
serverSocket.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void run()
{
try
{
while(true)
{
SSLSocket s = (SSLSocket) serverSocket.accept();
Connection c = new Connection(s,connectionListener);
connections.add(c);
System.out.println("New Connection Established From"+s.getInetAddress().toString());
}
}
catch(java.net.SocketException e)
{
System.out.println("Listening thread terminated with exception.");
}
catch(IOException e)
{
e.printStackTrace();
}
}
public void removeConnection(Connection c)
{
connections.remove(c);
}
public void printConnections()
{
System.out.println("Number of connections "+connections.toString());
for(int i=0; i<connections.size(); i++)
{
System.out.println(connections.toString());
}
}
}
And then a snipbit of my client code that connects when a button is pressed:
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == connect)
{
try
{
System.setProperty("javax.net.ssl.trustStore", "za.store");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
SSLSocketFactory sslsf = (SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket s = (SSLSocket)sslsf.createSocket(ipBox.getText(), Integer.parseInt(portBox.getText()));
Connection c = new Connection(s,parent);
parent.connectionSuccessful(c);
}
catch (NumberFormatException e1)
{
JOptionPane.showMessageDialog(this, "Error! Port number must be a number", "Error", JOptionPane.ERROR_MESSAGE);
}
catch (UnknownHostException e1)
{
JOptionPane.showMessageDialog(this, "Error! Unable to find that host", "Error", JOptionPane.ERROR_MESSAGE);
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
}
One Stackoverflow article suggested that my server "doesn't have a certificate." I don't know what that means or how to go about getting one and locating it in the right place.
The following error may come due to various reasons:
javax.net.ssl.SSLHandshakeException: no cipher suites in common
The points to check while debugging:
The keystore and truststore are correctly loaded and used to create the socket connections
The certificate is compatible with the enabled cipher suites
At least one common cipher suite should be enabled in the client and the server and that cipher suite should be also compatible with the certificate
For e.g. in the following example, I am using Java 8 with the default set of cipher suites. The certificate I have generated is using ECDSA and SHA384, and hence when the TLS connection is established between the server and the client, I can see the negotiated cipher suite is TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 by enabling the debug (System.setProperty("javax.net.debug", "ssl");).
Following is a working example:
As a first step, a key pair and a certificate need to be created. For testing purpose, let's create a self-signed certificate and let's use the same certificate for both the server and the client:
keytool -genkeypair -alias server -keyalg EC \
-sigalg SHA384withECDSA -keysize 256 -keystore servercert.p12 \
-storetype pkcs12 -v -storepass abc123 -validity 10000 -ext san=ip:127.0.0.1
Let's now create the server:
package com.sapbasu.javastudy;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Objects;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManagerFactory;
/*
* keytool -genkeypair -alias server -keyalg EC \
* -sigalg SHA384withECDSA -keysize 256 -keystore servercert.p12 \
* -storetype pkcs12 -v -storepass abc123 -validity 10000 -ext san=ip:127.0.0.1
*/
public class TLSServer {
public void serve(int port, String tlsVersion, String trustStoreName,
char[] trustStorePassword, String keyStoreName, char[] keyStorePassword)
throws Exception {
Objects.requireNonNull(tlsVersion, "TLS version is mandatory");
if (port <= 0) {
throw new IllegalArgumentException(
"Port number cannot be less than or equal to 0");
}
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream tstore = TLSServer.class
.getResourceAsStream("/" + trustStoreName);
trustStore.load(tstore, trustStorePassword);
tstore.close();
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream kstore = TLSServer.class
.getResourceAsStream("/" + keyStoreName);
keyStore.load(kstore, keyStorePassword);
KeyManagerFactory kmf = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyStorePassword);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
SecureRandom.getInstanceStrong());
SSLServerSocketFactory factory = ctx.getServerSocketFactory();
try (ServerSocket listener = factory.createServerSocket(port)) {
SSLServerSocket sslListener = (SSLServerSocket) listener;
sslListener.setNeedClientAuth(true);
sslListener.setEnabledProtocols(new String[] {tlsVersion});
// NIO to be implemented
while (true) {
try (Socket socket = sslListener.accept()) {
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("Hello World!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
Now create the client:
package com.sapbasu.javastudy;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Objects;
import javax.net.SocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;
public class TLSClient {
public String request(InetAddress serverHost, int serverPort,
String tlsVersion, String trustStoreName, char[] trustStorePassword,
String keyStoreName, char[] keyStorePassword) throws Exception {
Objects.requireNonNull(tlsVersion, "TLS version is mandatory");
Objects.requireNonNull(serverHost, "Server host cannot be null");
if (serverPort <= 0) {
throw new IllegalArgumentException(
"Server port cannot be lesss than or equal to 0");
}
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream tstore = TLSClient.class
.getResourceAsStream("/" + trustStoreName);
trustStore.load(tstore, trustStorePassword);
tstore.close();
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream kstore = TLSClient.class
.getResourceAsStream("/" + keyStoreName);
keyStore.load(kstore, keyStorePassword);
KeyManagerFactory kmf = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyStorePassword);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
SecureRandom.getInstanceStrong());
SocketFactory factory = ctx.getSocketFactory();
try (Socket connection = factory.createSocket(serverHost, serverPort)) {
((SSLSocket) connection).setEnabledProtocols(new String[] {tlsVersion});
SSLParameters sslParams = new SSLParameters();
sslParams.setEndpointIdentificationAlgorithm("HTTPS");
((SSLSocket) connection).setSSLParameters(sslParams);
BufferedReader input = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
return input.readLine();
}
}
}
Finally, here's is a JUnit test to test the connection:
package com.sapbasu.javastudy;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.net.InetAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.jupiter.api.Test;
public class TLSServerClientTest {
private static final int SERVER_PORT = 8444;
private static final String TLS_VERSION = "TLSv1.2";
private static final int SERVER_COUNT = 1;
private static final String SERVER_HOST_NAME = "127.0.0.1";
private static final String TRUST_STORE_NAME = "servercert.p12";
private static final char[] TRUST_STORE_PWD = new char[] {'a', 'b', 'c', '1',
'2', '3'};
private static final String KEY_STORE_NAME = "servercert.p12";
private static final char[] KEY_STORE_PWD = new char[] {'a', 'b', 'c', '1',
'2', '3'};
#Test
public void whenClientSendsServerRequest_givenServerIsUp_returnsHelloWorld()
throws Exception {
TLSServer server = new TLSServer();
TLSClient client = new TLSClient();
System.setProperty("javax.net.debug", "ssl");
ExecutorService serverExecutor = Executors.newFixedThreadPool(SERVER_COUNT);
serverExecutor.submit(() -> {
try {
server.serve(SERVER_PORT, TLS_VERSION, TRUST_STORE_NAME,
TRUST_STORE_PWD, KEY_STORE_NAME, KEY_STORE_PWD);
} catch (Exception e) {
e.printStackTrace();
}
});
try {
String returnedValue = client.request(
InetAddress.getByName(SERVER_HOST_NAME), SERVER_PORT, TLS_VERSION,
TRUST_STORE_NAME, TRUST_STORE_PWD, KEY_STORE_NAME, KEY_STORE_PWD);
assertEquals("Hello World!", returnedValue);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
Note: The certificate (servercert.p12 in this example) should be in the classpath. In this example, I've kept it in the test/resources folder of Maven folder structure so that the JUnit test can get it in the classpath.
Cipher Suite Background
When using TLS/SSL, the cryptographic algorithms to be used are determined by cipher suites. The server supports a set of cipher suites (you can enable or disable certain suites as per your needs and security level you want). The client also supports a set of cipher suites. During connection setup, the cipher suite to be used is negotiated between the client and the server. The client preference will be honored given that the server supports that particular cipher suite.
You'd find the list of cipher suites supported by the Sun Providers upto Java 8 here.
A typical cipher suite name looks like this:
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
Here,
ECDHE stands for Elliptic Curve Diffie Hellman Ephemeral. It's a Key exchange algorithm. The Elliptic variant (the first E) is used for performance, whereas the Ephemeral variant (the last E) is for forward secrecy. Forward secrecy means that if an attacker keeps recording all the communications over TLS and at a later point of time somehow gets hold of the private key, he/she cannot decrypt the past recorded communications.
ECDSA is a digital signature algorithm used for signing the key and is used for authenticating (verifying the integrity of) the shared secret. ECDSA is weaker and slower than the other authentication algorithms like HMAC. Yet it is used for shared key authentication because it does not need the verifier know the secret key used to create the authentication tag. The server can very well use its private key to verify the integrity of the message.
AES_128_GCM - Once a common secret key is shared between both the parties (usually a browser and a web server), a symmetric block cipher algorithm is used to encrypt the message exchanges between the parties. In this particular case, the block cipher AES with 128 bit key and GCM authentication mode is used.
SHA256 - Hashing algorithm for the PRF
You are setting the system properties wrong.
The server needs javax.net.keyStore (not trustStore) and javax.net.keyStorePassword -- and sometimes javax.net.keyStoreType but probably not in your case. For a server using self-signed cert, the client needs the truststore to contain that cert, and does not need any keystore at all. Although it's not documented, if these are on the same system you can use the server keystore (containing a privateKeyEntry) as the client truststore and JSSE automatically treats the privateKey's leaf cert as a trustedCert. In any other situation you need to extract the server's cert (not key), copy/send it to the client, and use keytool -importcert to put the cert (not key) into a keystore file which is used as truststore; this can be either a custom file (identified with the sysprops) or the default truststore at JRE/lib/security/cacerts (except on current Windows if JRE is under \Program Files[ (x86)] as is the installer default, because Windows now messes with people who try to change files there).
And these sysprops (when used) must be set BEFORE THE JSSE CLASSES ARE LOADED -- setting them when you call SSL[Server]SocketFactory is usually far too late. Usually they are set on the commandline that invokes Java with the -Dname=value option; that is guaranteed to be done early enough. If you can't do that, put the setProperty calls at the beginning of your main method, or the equivalent for a GUI.
This manifests as 'no common/shared cipher' because without a key+cert the server cannot support the ciphersuites that do server authentication, and Java/JSSE by default only enables ciphersuites that do server authentication because the others are insecure in many/most situations.
I'm sure I've answered the second part before, but can't find a dupe. The first part has occurred in many answers, but is usually not clearly presented in the question.
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");
}
}
I use a java with Itext for make a digital sign PDF document using a LUNA HSM.
My objective is sign a document with PKCS11 and assemble the certificates chain from the HSM. I dont want to install certificates into the server.
I try to use a sample program called C4_01_SignWithPKCS11HSM.java from the iText.
I take this from:
http://developers.itextpdf.com/examples/security/digital-signatures-white-paper/digital-signatures-chapter-4
When I compiled program, it show me the follow warning:
[luna#sumCentosHsm pdf]$ javac -Xlint signPdf.java signPdf.java:93:
warning: [deprecation] OcspClientBouncyCastle() in
OcspClientBouncyCastle has been deprecated
OcspClient ocspClient = new OcspClientBouncyCastle();
Also, how to build the configuration file and parameters.
I would like to know if someone had the same problem
Thank you.
import java.security.*;
import java.security.KeyStore.*;
import java.security.cert.X509Certificate;
import java.security.cert.Certificate;
import com.safenetinc.luna.*;
import java.io.*;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.security.*;
public class SignPdfUsingLuna{
private static String keyAlias = null;
private static String slotPin = null;
private static int slotId;
private static String inputFile = null;
private static String outputFile = null;
private static KeyStore ks = null;
private static PrivateKeyEntry prKE = null;
private static void usage(){
System.out.println("Command usage :-");
System.out.println("java SignPdfUsingLuna <SlotNumber> <SlotPassword> <KeyAlias> <InputFile>");
}
public static void main(String args[]){
try{
slotId = Integer.parseInt(args[0]);
slotPin = args[1];
keyAlias = args[2];
inputFile = args[3];
ks = KeyStore.getInstance("Luna");
ks.load(new ByteArrayInputStream(("slot:"+slotId).getBytes()),slotPin.toCharArray());
ProtectionParameter param = new PasswordProtection("abcd".toCharArray());
prKE = (PrivateKeyEntry)ks.getEntry(keyAlias,param);
X509Certificate cert = (X509Certificate)ks.getCertificate(keyAlias);
Certificate[] certchain = (Certificate[]) ks.getCertificateChain(keyAlias);
PdfReader readPdf = new PdfReader(inputFile);
FileOutputStream outFile = new FileOutputStream("Signed"+inputFile);
PdfStamper stamp = PdfStamper.createSignature(readPdf, outFile, '\0');
PdfSignatureAppearance psa = stamp.getSignatureAppearance();
psa.setReason("Signed by :- Sam Paul");
psa.setLocation("India");
Image img = Image.getInstance("Logo.jpg");
psa.setImage(img);
psa.setVisibleSignature(new Rectangle(100, 100, 300, 200), 1, "Signature");
ExternalDigest dgst = new BouncyCastleDigest();
Provider prod = ks.getProvider();
PrivateKey pk = prKE.getPrivateKey();
ExternalSignature sign = new PrivateKeySignature(pk,DigestAlgorithms.SHA256,prod.getName());
MakeSignature.signDetached(psa, dgst, sign, certchain, null, null, null, 0, MakeSignature.CryptoStandard.CMS);
stamp.close();
}catch(ArrayIndexOutOfBoundsException aio){
usage();
}catch(NumberFormatException nfe){
System.out.println("Please enter a valid slot number");
usage();
}catch(Exception e){
e.printStackTrace();
}
}
Hope this helps.
Sam.
Perhaps you have a special circumstance where you want to use HSM keys to sign documents. Most of the time, document signing is done with 'person-entity' PKI certificates. In this scenario, your local Certificate Authority (Windows Server), has been configured to store the CA's private key on the SafeNet HSM. Then the local CA would issue personal PKI certificates to users of that domain (Bob Smith). Then a user, Bob, could use his certificate that is local to his machine to sign documents. This would provide integrity and nonrepudiation, and the certificate would signed by that individual.
In your implementation, any signed document would simply display the subject name of the HSM certificate, which in most implementations would be a domain's CA name, etc.
I'm trying to validate the access token signature with my public key retrieved from an authentication server (OpenId).
The client get an access token from the same server and then request my Resource server API with it. Now I have to check its signature with the Spring Security library.
The access token has an "alg" : "RS256" attribute.
But the code below remains unsuccessful and I'm always getting the InvalidSignatureException...
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.InvalidSignatureException;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
public class JWTValidation {
private static final Logger logger = LoggerFactory.getLogger(JWTValidation.class);
private static final String PUBLIC_KEY_MODULUS = "qOYyKKnoUpXd2qIj8A0tdumWwnDbVjXOVaPfiX5lxBvYEtgWPLknf1Nftdk371a7f1jD8SFFDxXnj-PPFx8qoNETOITvbR12uvWmS1J36B5Uo_ViHp7dC-GaZG_EdafyK0rxRPvK8b37NPXWhTggbxCZhYaqJUMb1t0xogDadEyM95lZweEXrwsJNzoyXiGnPfsRgy32TjOOXIMZnAMoj-osYd2WawymkRV6cteo3f8KMT72_kp8oG-kGm1s3ZooEfI3_9Z2jHVGWQLUWbmZKIrvjuUo2dhmqWWsNyTO3RsF4qyrRCpmZNawDf_GsioBTZ3vfPF_T58moH7cJ50Byw";
private static final String PUBLIC_KEY_PUBLIC_EXPONENT = "AQAB";
//Public key =
// {
// "keys":[
// {
// "kty":"RSA",
// "use":"sig",
// "kid":"DQr-GCc8rH3y5fkAuo0iau-ue-s",
// "x5t":"DQr-GCc8rH3y5fkAuo0iau-ue-s",
// "e":"AQAB",
// "n":"qOYyKKnoUpXd2qIj8A0tdumWwnDbVjXOVaPfiX5lxBvYEtgWPLknf1Nftdk371a7f1jD8SFFDxXnj-PPFx8qoNETOITvbR12uvWmS1J36B5Uo_ViHp7dC-GaZG_EdafyK0rxRPvK8b37NPXWhTggbxCZhYaqJUMb1t0xogDadEyM95lZweEXrwsJNzoyXiGnPfsRgy32TjOOXIMZnAMoj-osYd2WawymkRV6cteo3f8KMT72_kp8oG-kGm1s3ZooEfI3_9Z2jHVGWQLUWbmZKIrvjuUo2dhmqWWsNyTO3RsF4qyrRCpmZNawDf_GsioBTZ3vfPF_T58moH7cJ50Byw",
// "x5c":["MIIDBDCCAfCgAwIBAgIQt1HpvYkM6oxJ1ZjbpW1fPTAJBgUrDgMCHQUAMBkxFzAVBgNVBAMTDkRhdGFEb29ycyBUZXN0MCAXDTE1MDQwMjIyMjQwM1oYDzIwNTAwMTAxMDYwMDAwWjAZMRcwFQYDVQQDEw5EYXRhRG9vcnMgVGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKjmMiip6FKV3dqiI/ANLXbplsJw21Y1zlWj34l+ZcQb2BLYFjy5J39TX7XZN+9Wu39Yw/EhRQ8V54/jzxcfKqDREziE720ddrr1pktSd+geVKP1Yh6e3QvhmmRvxHWn8itK8UT7yvG9+zT11oU4IG8QmYWGqiVDG9bdMaIA2nRMjPeZWcHhF68LCTc6Ml4hpz37EYMt9k4zjlyDGZwDKI/qLGHdlmsMppEVenLXqN3/CjE+9v5KfKBvpBptbN2aKBHyN//Wdox1RlkC1Fm5mSiK747lKNnYZqllrDckzt0bBeKsq0QqZmTWsA3/xrIqAU2d73zxf0+fJqB+3CedAcsCAwEAAaNOMEwwSgYDVR0BBEMwQYAQgtiIGHLzFEskZSe/65EOTqEbMBkxFzAVBgNVBAMTDkRhdGFEb29ycyBUZXN0ghC3Uem9iQzqjEnVmNulbV89MAkGBSsOAwIdBQADggEBADtIlf41MLeGwjTbhJS88stZEBhEexxXNJDlW92GKPVv0JJWD/5m8tfADzXOgP65rTyQ4lTGOFFRYQu0ajMYAzggqJTmU1rMrHxuVwLfJ3OpSOc9UZBs2gW/IUZFvSugMKNboTsfTPgpsHK1ag68NKvR/V209zYZd6A7zisGgUr2Oc5jNEj7lSQY6pME2ZXU0YppC6Dctj8XHkTO9Ji9vDj+iGoS4+RvHZ3cDr5YUbOKSooOAZ/kjqtm+VK2jdjdLrkduz/24NKIxEXQqhmM28f8kh5Wc2ilaMya9pxQZpTWk7sjOBkZCcw24tx6UqpSTsV/XnnTmJMIhgXFJXWnXFc="]
// }
// ]
// }
//Access Token = base64url encoded String
public boolean verifySignature(String accessToken){
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Base64 decoder = new Base64(true);//URL-safe Base64 decoder
BigInteger modulus = new BigInteger(decoder.decode(PUBLIC_KEY_MODULUS.getBytes()));
BigInteger publicExponent = new BigInteger(decoder.decode(PUBLIC_KEY_PUBLIC_EXPONENT.getBytes()));
RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
PublicKey newPublicKey = keyFactory.generatePublic(spec);
RsaVerifier verif = new RsaVerifier((RSAPublicKey) newPublicKey, "SHA256withRSA");
JwtHelper.decodeAndVerify(accessToken, verif);
} catch (InvalidSignatureException e){
logger.info(e.getMessage());
return false;
} catch (Exception e){
logger.info(e.getMessage());
return false;
}
return true;
}
}
I also tried to use the online tool jwt.io but I've not been able to make it work (the signature remains invalid)
And for the other one (tool_jwt), the only way to have a valid signature is to choose the "default X.509 certificate RSA" with comments around my public key "x5c" value :
-----BEGIN CERTIFICATE-----
MIIDBDCCAfCgAwIBAgIQt1HpvYkM6oxJ1ZjbpW1fPTAJBgUrDgMCHQUAMBkxFzAVBgNVBAMTDkRhdGFEb29ycyBUZXN0MCAXDTE1MDQwMjIyMjQwM1oYDzIwNTAwMTAxMDYwMDAwWjAZMRcwFQYDVQQDEw5EYXRhRG9vcnMgVGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKjmMiip6FKV3dqiI/ANLXbplsJw21Y1zlWj34l+ZcQb2BLYFjy5J39TX7XZN+9Wu39Yw/EhRQ8V54/jzxcfKqDREziE720ddrr1pktSd+geVKP1Yh6e3QvhmmRvxHWn8itK8UT7yvG9+zT11oU4IG8QmYWGqiVDG9bdMaIA2nRMjPeZWcHhF68LCTc6Ml4hpz37EYMt9k4zjlyDGZwDKI/qLGHdlmsMppEVenLXqN3/CjE+9v5KfKBvpBptbN2aKBHyN//Wdox1RlkC1Fm5mSiK747lKNnYZqllrDckzt0bBeKsq0QqZmTWsA3/xrIqAU2d73zxf0+fJqB+3CedAcsCAwEAAaNOMEwwSgYDVR0BBEMwQYAQgtiIGHLzFEskZSe/65EOTqEbMBkxFzAVBgNVBAMTDkRhdGFEb29ycyBUZXN0ghC3Uem9iQzqjEnVmNulbV89MAkGBSsOAwIdBQADggEBADtIlf41MLeGwjTbhJS88stZEBhEexxXNJDlW92GKPVv0JJWD/5m8tfADzXOgP65rTyQ4lTGOFFRYQu0ajMYAzggqJTmU1rMrHxuVwLfJ3OpSOc9UZBs2gW/IUZFvSugMKNboTsfTPgpsHK1ag68NKvR/V209zYZd6A7zisGgUr2Oc5jNEj7lSQY6pME2ZXU0YppC6Dctj8XHkTO9Ji9vDj+iGoS4+RvHZ3cDr5YUbOKSooOAZ/kjqtm+VK2jdjdLrkduz/24NKIxEXQqhmM28f8kh5Wc2ilaMya9pxQZpTWk7sjOBkZCcw24tx6UqpSTsV/XnnTmJMIhgXFJXWnXFc=
-----END CERTIFICATE-----
So I don't know what to do now, which public key attribute should I use, and how to make it work ?
Thanks a lot for your help :)
I had the use the x509 key spec in addition to the RSA key spec
RSAPublicKeySpec spec = new RSAPublicKeySpec(new BigInteger(modulusBytes), new BigInteger(exponentBytes));
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey key = factory.generatePublic(spec);
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(key.getEncoded());
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pubKey64 = kf.generatePublic(X509publicKey);
This worked for both the auth0 and jwt.io libraries
To validate the signature online with jwt.io, you just need to put there the following json as a public key:
{
"kty":"RSA",
"kid":"DQr-GCc8rH3y5fkAuo0iau-ue-s",
"e":"AQAB",
"n":"qOYyKKnoUpXd2qIj8A0tdumWwnDbVjXOVaPfiX5lxBvYEtgWPLknf1Nftdk371a7f1jD8SFFDxXnj-PPFx8qoNETOITvbR12uvWmS1J36B5Uo_ViHp7dC-GaZG_EdafyK0rxRPvK8b37NPXWhTggbxCZhYaqJUMb1t0xogDadEyM95lZweEXrwsJNzoyXiGnPfsRgy32TjOOXIMZnAMoj-osYd2WawymkRV6cteo3f8KMT72_kp8oG-kGm1s3ZooEfI3_9Z2jHVGWQLUWbmZKIrvjuUo2dhmqWWsNyTO3RsF4qyrRCpmZNawDf_GsioBTZ3vfPF_T58moH7cJ50Byw"
}
Currently, the only way I know to retrieve the administrator password from a newly created EC2 windows instance is through the AWS management console. This is fine, but I need to know how to accomplish this via the Java API - I can't seem to find anything on the subject. Also, once obtained, how do I modify the password using the same API?
The EC2 API has a call "GetPasswordData" which you can use to retrieve an encrypted block of data containing the Administrator password. To decrypt it, you need 2 things:
First, the private key. This is the private half of the keypair you used to instantiate the instance. A complication is that normally Amazon uses keys in PEM format ("-----BEGIN"...) but the Java Crypto API wants keys in DER format. You can do the conversion yourself - strip off the -----BEGIN and -----END lines, take the block of text in the middle and base64-decode it.
Second, the encryption parameters. The data is encrypted with RSA, with PKCS1 padding – so the magic invocation to give to JCE is: Cipher.getInstance("RSA/NONE/PKCS1Padding")
Here's a full example (that relies on BouncyCastle, but could be modified to use a different crypto engine)
package uk.co.frontiertown;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.GetPasswordDataRequest;
import com.amazonaws.services.ec2.model.GetPasswordDataResult;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;
import javax.crypto.Cipher;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Security;
import java.security.spec.PKCS8EncodedKeySpec;
public class GetEc2WindowsAdministratorPassword {
private static final String ACCESS_KEY = "xxxxxxxxxxxxxxxxxxxx";
private static final String SECRET_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
private static final String PRIVATE_KEY_MATERIAL = "-----BEGIN RSA PRIVATE KEY-----\n" +
"MIIEowIBAAKCAQEAjdD54kJ88GxkeRc96EQPL4h8c/7V2Q2QY5VUiJ+EblEdcVnADRa12qkohT4I\n" +
// several more lines of key data
"srz+xXTvbjIJ6RL/FDqF8lvWEvb8uSC7GeCMHTznkicwUs0WiFax2AcK3xjgtgQXMgoP\n" +
"-----END RSA PRIVATE KEY-----\n";
public static void main(String[] args) throws GeneralSecurityException, InterruptedException {
Security.addProvider(new BouncyCastleProvider());
String password = getPassword(ACCESS_KEY, SECRET_KEY, "i-XXXXXXXX", PRIVATE_KEY_MATERIAL);
System.out.println(password);
}
private static String getPassword(String accessKey, String secretKey, String instanceId, String privateKeyMaterial) throws GeneralSecurityException, InterruptedException {
// Convert the private key in PEM format to DER format, which JCE can understand
privateKeyMaterial = privateKeyMaterial.replace("-----BEGIN RSA PRIVATE KEY-----\n", "");
privateKeyMaterial = privateKeyMaterial.replace("-----END RSA PRIVATE KEY-----", "");
byte[] der = Base64.decode(privateKeyMaterial);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(der);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
// Get the encrypted password data from EC2
AWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
AmazonEC2Client client = new AmazonEC2Client(awsCredentials);
GetPasswordDataRequest getPasswordDataRequest = new GetPasswordDataRequest().withInstanceId(instanceId);
GetPasswordDataResult getPasswordDataResult = client.getPasswordData(getPasswordDataRequest);
String passwordData = getPasswordDataResult.getPasswordData();
while (passwordData == null || passwordData.isEmpty()) {
System.out.println("No password data - probably not generated yet - waiting and retrying");
Thread.sleep(10000);
getPasswordDataResult = client.getPasswordData(getPasswordDataRequest);
passwordData = getPasswordDataResult.getPasswordData();
}
// Decrypt the password
Cipher cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] cipherText = Base64.decode(passwordData);
byte[] plainText = cipher.doFinal(cipherText);
String password = new String(plainText, Charset.forName("ASCII"));
return password;
}
}
ObDisclosure: I originally answered this on a blog posting at http://www.frontiertown.co.uk/2012/03/java-administrator-password-windows-ec2-instance/
You can create an instance, set the password and then turn it back into an image. Effectively setting a default password for each instance you create. Wouldn't this be simpler?
Looks like you are looking for the following parts of the API: GetPasswordDataRequest and GetPasswordDataResult
You can also create a Image with default user name and Password setup on that Image.And then launch all instances with that image id..so that you dont need to create and retrieve password evry time..just launch your instance rdp that launched instance with definde credntials in Image. I am doing same.And its perfectly working for me.