EST with Bouncy Castle - java

I try to request a new certificate via EST protocol from the EST test service URL “https://testrfc7030.com/”. The program uses Bouncy Castle for this.
I have already configured the EST service’s TA and my client certificate obtained from them. I also use the BC JSSE provider to get access to the “tls-unique” channel binding value.
eSTService = new JsseESTServiceBuilder(Config.CredAdmin.caHost, trustManagers)
.withKeyManagers(keyManagers)
.withProvider(BouncyCastleJsseProvider.PROVIDER_NAME)
.withChannelBindingProvider(new ChannelBindingProvider() {
//Use an anonymous binding provider that supports linking
//Identity and POP Information (RFC7030, Section 3.5.), that
//relies on Channel Bindings for TLS (RFC5929) using "tls-unique".
public boolean canAccessChannelBinding(Socket sock) {
boolean ret = sock instanceof BCSSLSocket;
if (!ret)
//should never happen
MyUtils.LambdaLogger.error("Can't get channel binding value, check if BouncyCastleJsseProvider could be loaded.");
return ret;
}
public byte[] getChannelBinding(Socket sock, String binding) {
BCSSLConnection bcon = ((BCSSLSocket)sock).getConnection();
if (bcon == null) {
//should never happen
MyUtils.LambdaLogger.error("Can't get \"%s\" channel binding value, check if BouncyCastleJsseProvider could be loaded.", binding);
return null;
}
byte[] ret = bcon.getChannelBinding(binding);
MyUtils.LambdaLogger.debug("retrieved %d bytes \"%s\" channel binding value", ret.length, binding);
return ret;
}
})
.build();
and
Security.addProvider(new BouncyCastleJsseProvider());
When I configure EST service port 9443 – that requires my client cert but no TLS channel binding – I do get a new certificate:
However, when I configure port 443 – that also needs TLS channel binding – although I get 12 bytes of “tls-unique” from BC JSSE, these won’t get accepted by the EST service testrfc7030.com, so it gives me an HTTP 401 – Unauthorized:
My problem is, I don’t know, what’s wrong:
my code
the BC JSSE implementation of “tls-unique” (RFC 5929)
the EST service’s implementation of “tls-unique” (RFC 5929)?
Does someone have an implementation that works with the EST service “testrfc7030.com:443” art has at least an idea, what's wrong?
---Update 1---
I'm creating the ContentSigner as following:
ContentSigner signer =
new JcaContentSignerBuilder(MyUtils.Crypto.sha256WithRSAEncryption)
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
.build(keyPair.getPrivate());
and the csrBuilder:
PKCS10CertificationRequestBuilder csrBuilder =
new PKCS10CertificationRequestBuilder(
new X500Name(subjectDN),
SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()));
csrBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate());
with
ExtensionsGenerator extGen = new ExtensionsGenerator();
...
This we then use as following:
EnrollmentResponse resp = eSTService.simpleEnrollPoP(false, cb.csrBuilder, cb.signer, null);

Based on the input by Peter we were able to fix this problem as following:
//just for testrfc7030.com
ESTAuth auth = new JcaHttpAuthBuilder(null, "estuser", "estpwd".toCharArray())
.setNonceGenerator(new SecureRandom())
.setProvider("BC")
.build();
EnrollmentResponse resp = eSTService.simpleEnrollPoP(false, cb.csrBuilder, cb.signer, auth);
It turned out, that testrfc3070 requires the following authentication schemes:
Port 443: requires HTTP user auth + identity POP linking
Port 8443: requires HTTP user auth but no identity POP linking
Port 9443: requires user auth with client certificate (obtained via Port 8443 or Port 443) but no identity POP linking
identity POP linking = TLS channel binding

Related

org.bouncycastle.cms.CMSException: content-type attribute value does not match eContentType

I'm currently building a TimeStamp server using BouncyCastle. Server is working well but on the client side, when I want to validate the TimeStampResponse received I'm getting the following error:
org.bouncycastle.cms.CMSException: content-type attribute value does
not match eContentType
On the server side, I'm including the content-type attribute like this:
ASN1EncodableVector signedAttributes = new ASN1EncodableVector();
signedAttributes.add(new Attribute(CMSAttributes.contentType, new DERSet(new ASN1ObjectIdentifier("1.2.840.113549.1.7.1"))));
signedAttributes.add(new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(request.getMessageImprintDigest()))));
signedAttributes.add(new Attribute(CMSAttributes.signingTime, new DERSet(new DERUTCTime(timeStampDate))));
AttributeTable signedAttributesTable = new AttributeTable(signedAttributes);
signedAttributesTable.toASN1EncodableVector();
//Linking Attribute Table to the signBuilder (linked to JKS Certificate)
DefaultSignedAttributeTableGenerator signedAttributeGenerator = new DefaultSignedAttributeTableGenerator(signedAttributesTable);
signBuilder.setSignedAttributeGenerator(signedAttributeGenerator);
signBuilder.setUnsignedAttributeGenerator(new SimpleAttributeTableGenerator(new AttributeTable(new Hashtable<String, String>())));
......
and on the client side:
Collection<X509CertificateHolder> tstMatches = response.getTimeStampToken().getCertificates().getMatches(response.getTimeStampToken().getSID());
X509CertificateHolder holder = tstMatches.iterator().next();
java.security.cert.X509Certificate tstCert = new JcaX509CertificateConverter().getCertificate(holder);
System.out.println("Cert Date exp: "+tstCert.getNotAfter());
SignerInformationVerifier siv = new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(tstCert);
AttributeTable att = response.getTimeStampToken().getSignedAttributes();
System.out.println("Content-type: "+att.get(CMSAttributes.contentType).getAttrValues().getObjectAt(0));
if(bytesToHex(response.getTimeStampToken().getTimeStampInfo().getMessageImprintDigest()).equals(bytesToHex(digest))) {
System.out.println("TimeStamp is valid, imprint is identical");
}
try {
response.getTimeStampToken().validate(siv);
}catch(Exception e) {
System.out.println("Still getting issue with Content Type: "+e.toString());
}
It seems that I include correctly the content-type in my TimeStampToken ("1.2.840.113549.1.7.1") but I don't know where is the eContentType and don't know where I can check it.
EDIT 1: May be I'm not clear in my answer...I'll try to reformulate...
How can I access eContentType of a TimeStampToken ?
What BouncyCastle is comparing ?
After multiple readings, I've seen that adding contentType Attribute is making this kind of error, as I'm already building the TimeStampResponse based on the request, the content type is already taken into account.
It make a conflict on the BouncyCastle Library, so by removing the line :
//signedAttributes.add(new Attribute(CMSAttributes.contentType, new DERSet(new ASN1ObjectIdentifier("1.2.840.113549.1.7.1"))));
everything works fine, my TimeStampResponse is validated correctly.

Invalid keystore format using java

I'm trying to make a post request to one of our clients with certificate as part of their security.
I tried testing it in postman, just like the usual attached the certificate on postman settings and then able to make a post request to their api.
Now my problem is i'm encountering this error when i'm doing the request on our platform built in java
java.base/sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:664)
java.base/sun.security.util.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:222)
java.base/java.security.KeyStore.load(KeyStore.java:1479)
On the test environment they don't require the password hence null.
try (InputStream inputStream = new ByteArrayInputStream(p12Cert))
{
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(inputStream, null);
I traced the engineLoad method from javakeystore.
private static final int MAGIC = 0xfeedfeed;
private static final int VERSION_1 = 0x01;
private static final int VERSION_2 = 0x02;
if (xMagic!=MAGIC ||
(xVersion!=VERSION_1 && xVersion!=VERSION_2)) {
throw new IOException("Invalid keystore format");
}
Can someone elaborate the above?
Able to fix the problem above, first is the p12 cert i am trying to encode in base64 is in utf-8 encoding which is wrong and should be converted from HEXADECIMAL to base64.

empty keyStore in crypto

I'm using wss4j in order to sign a message request.
But when signing the message with this code:
Document d = signer.build(unsignedDoc, crypto, header)
I always get the error
org.apache.ws.security.WSSecurityException: General security error (no certificates for user ... were found for signature)
Searching for the cause I found out, that en empty key store seems to be the reason for this.
This is how I create a crypto instance:
private void createCrypto(){
// reading preferences (keystone-filename, password, etc.) from properties file
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("myprops.properties");
props.load(fis);
// create a crypto instance
Crypto crypto = CryptoFactory.getInstance(props);
// This line always prints "Size:0"
System.out.println("Size:" + crypto.getKeyStore.getSize())
...
But if I load the Keystore the following way, it shows size=1:
private void loadKeystore(){
KeyStore keystore;
try{
// reading and loading keystone file
InputStream is = new FileInputStream("myKeyStore.jks");
keystore.load(is, "password".toCharArray());
// Prints "Size:1"
System.out.println("Size:"+keystore.size());
...
So I wonder, what is wrong with the first example. Why is the keystore empty?
I already checked the properties: keystore and password are set correctly!
But if I remove the keystore-property from the properties-file, the error message is the same.
Can anyone help me with this?

Connecting to and AWS EC2 instance using SSHJ

I am having lots of problems with this.
I have the following code
try {
final SSHClient ssh = new SSHClient();
PKCS8KeyFile keyFile = new PKCS8KeyFile();
keyFile.init(new File(Thread.currentThread().getContextClassLoader().getResource("development.pem").toURI()));
ssh.loadKnownHosts();
ssh.addHostKeyVerifier("ec2-XX-XX-XX-XX.compute-1.amazonaws.com", 22, "ff:59:aa:24:42:b1:a0:9f:c9:4c:73:34:fb:95:53:c2:b8:37:a8:f8");
// ssh.addHostKeyVerifier("ec2-XX-XX-XX-XX.compute-1.amazonaws.com", 22, "90:1e:4d:09:42:c4:16:8a:4c:dc:ae:c2:60:14:f9:ea");
ssh.connect("ec2-XX-XX-XX-XX.compute-1.amazonaws.com");
ssh.auth("ec2-user", new AuthPublickey(keyFile));
Session session = ssh.startSession();
Command sudo = session.exec("sudo su -");
System.out.println("sudo=" +sudo.getOutputAsString());
Command whoami = session.exec("whoami");
System.out.println("whoami=" + whoami.getOutputAsString());
} catch (Exception e) {
e.printStackTrace();
}
The first addHostKeyVerifier is using the fingerprint on the AWS console, the commented out one is the one that it keeps telling me it is failing against. Where am i meant to get the correct key from.
If i use the second key it passes verification then fails afterwards.
I am using SSHJ version 0.8.1
This worked for me.
For your PEM file you need to use the OpenSSHKeyFile key provider.
SSHClient ssh = new SSHClient();
OpenSSHKeyFile keyFile = new OpenSSHKeyFile();
File file = new File("c:\\full\\path\\to\\keyfile.pem");
keyFile.init(file);
Personally, I just surpressed the host key verification to always return true. But I'm sure your way is more secure (if it works).
ssh.loadKnownHosts();
ssh.addHostKeyVerifier((a, b, c) -> true);
The username for AWS depends on your image. Very often it is "root". In my case, it was "ubuntu".
ssh.connect("ec2-54-165-233-48.compute-1.amazonaws.com");
ssh.auth("ubuntu", new AuthPublickey(keyFile));
Session session = ssh.startSession();
(Note: I'm using version 0.26.0 though.)

Disable hostname verification in hsqldb

I have a tomcat-hibernate-hsqldb setup and I want to use SSL to secure data transfer between my application and hsqldb. However, I need to pre install a certificate which can be used at any deployment. I do not want to use a new certificate for each new deployment site. For this, if I just use a self-signed certificate issues to any random Common Name and then install the same certificate in the trust store of tomcat, then I get this exception
java.net.UnknownHostException: Certificate Common Name[random name] does not match host name[192.168.100.10]
I need to disable hostname verification in this setup, but all the info I found on web points to the mechanism of disabling it for HttpsURLConnection.
I believe hsqldb has a custom code to do it, in the file
org.hsqldb.serverHsqlSocketFactorySecure
Here is the method, which does this:
protected void verify(String host, SSLSession session) throws Exception {
X509Certificate[] chain;
X509Certificate certificate;
Principal principal;
PublicKey publicKey;
String DN;
String CN;
int start;
int end;
String emsg;
chain = session.getPeerCertificateChain();
certificate = chain[0];
principal = certificate.getSubjectDN();
DN = String.valueOf(principal);
start = DN.indexOf("CN=");
if (start < 0) {
throw new UnknownHostException(
Error.getMessage(ErrorCode.M_SERVER_SECURE_VERIFY_1));
}
start += 3;
end = DN.indexOf(',', start);
CN = DN.substring(start, (end > -1) ? end
: DN.length());
if (CN.length() < 1) {
throw new UnknownHostException(
Error.getMessage(ErrorCode.M_SERVER_SECURE_VERIFY_2));
}
if (!CN.equalsIgnoreCase(host)) {
// TLS_HOSTNAME_MISMATCH
throw new UnknownHostException(
Error.getMessage(
ErrorCode.M_SERVER_SECURE_VERIFY_3, 0,
new Object[] {
CN, host
}));
}
}
Is there a way to somehow bypass this mechanism and disable hostname validation?
Asked the same question on hsqldb forums and got to know that there is no workaround to this. The only thing you could do is to comment out the code which is calling the verify method and then rebuild the jar. I am still puzzled why hsqldb didn't use the HostnameVerifier (http://docs.oracle.com/javase/6/docs/api/javax/net/ssl/HostnameVerifier.html), which would have made it easier to write a custom Hostname Verifier.

Categories