Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions deps/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</scm>
<groupId>com.microsoft.azure.sdk.iot</groupId>
<artifactId>iot-deps</artifactId>
<version>0.8.0</version>
<version>0.8.0-iss</version>
<packaging>jar</packaging>

<properties>
Expand All @@ -39,11 +39,6 @@
</properties>

<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcmail-jdk15on</artifactId>
<version>1.53</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.microsoft.azure.sdk.iot.deps.auth;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;

public class CertificateReader {

private static final String BEGIN_MARKER = "-----BEGIN CERTIFICATE";
private static final String END_MARKER = "-----END CERTIFICATE";

private final LineReader server;
private Collection<X509Certificate> certs;

public CertificateReader(String keyContent) {
this.server = new LineReader(keyContent);
}

public Collection<X509Certificate> getCertificates() throws IOException {
if (this.certs == null) {
this.certs = read();
}
return this.certs;
}

private Collection<X509Certificate> read() throws IOException {
Collection<X509Certificate> result = new ArrayList<>();
String line;
CertificateFactory factory;

while ((line = server.readLine()) != null) {
if (line.indexOf(BEGIN_MARKER) != -1) {
byte[] certBytes = readCertMaterial(END_MARKER);
try {
factory = CertificateFactory.getInstance("X509");
} catch (CertificateException e) {
throw new IOException("JCE error: " + e.getMessage());
}
try {
X509Certificate certificate =
(X509Certificate) factory.generateCertificate(new ByteArrayInputStream(certBytes));
result.add(certificate);
} catch (CertificateException e) {
throw new IOException("Invalid cert file: " + e.getMessage());
}
}
}
return result;
}

private byte[] readCertMaterial(String endMarker) throws IOException {
String line = null;
StringBuffer buf = new StringBuffer();

while ((line = server.readLine()) != null) {
if (line.indexOf(endMarker) != -1) {
return Base64.getDecoder().decode(buf.toString());
}

buf.append(line.trim());
}

throw new IOException("Invalid cert file: No end marker");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,14 @@

package com.microsoft.azure.sdk.iot.deps.auth;

import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.UUID;
Expand Down Expand Up @@ -294,86 +282,23 @@ private char[] generateTemporaryPassword()

private static Key parsePrivateKey(String privateKeyString) throws CertificateException
{
try
{
try {
// Codes_SRS_IOTHUBSSLCONTEXT_34_031: [This function shall return a Private Key instance created by the provided PEM formatted privateKeyString.]
Security.addProvider(new BouncyCastleProvider());
PEMParser privateKeyParser = new PEMParser(new StringReader(privateKeyString));
Object possiblePrivateKey = privateKeyParser.readObject();
return IotHubSSLContext.getPrivateKey(possiblePrivateKey);
}
catch (Exception e)
{
return new PrivateKeyReader(privateKeyString).getPrivateKey();
} catch (Exception e) {
// Codes_SRS_IOTHUBSSLCONTEXT_34_032: [If any exception is encountered while attempting to create the private key instance, this function shall throw a CertificateException.]
throw new CertificateException(e);
}
}

private static Collection<X509Certificate> parsePublicKeyCertificate(String publicKeyCertificateString) throws CertificateException
{
try
{
Collection<X509Certificate> certChain = new ArrayList<>();

// Codes_SRS_IOTHUBSSLCONTEXT_34_033: [This function shall return the X509Certificate cert chain specified by the PEM formatted publicKeyCertificateString.]
Security.addProvider(new BouncyCastleProvider());

CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
final PemReader publicKeyCertificateReader = new PemReader(new StringReader(publicKeyCertificateString));

try
{
PemObject possiblePublicKeyCertificate;
while (((possiblePublicKeyCertificate = publicKeyCertificateReader.readPemObject()) != null))
{
byte[] content = possiblePublicKeyCertificate.getContent();
if (content.length > 0)
{
final ByteArrayInputStream bais = new ByteArrayInputStream(content);

while (bais.available() > 0)
{
final Certificate cert = certFactory.generateCertificate(bais);
if (cert instanceof X509Certificate)
{
certChain.add((X509Certificate) cert);
}
}
}
else
{
break;
}
}
}
finally
{
publicKeyCertificateReader.close();
}

return certChain;
}
catch (Exception e)
{
// Codes_SRS_IOTHUBSSLCONTEXT_34_034: [If any exception is encountered while attempting to create the public key certificate instance, this function shall throw a CertificateException.]
try {
// Codes_SRS_IOTHUBSSLCONTEXT_34_033: [This function shall return the X509Certificate cert chain specified by the PEM formatted publicKeyCertificateString.]
return new CertificateReader(publicKeyCertificateString).getCertificates();
} catch (Exception e) {
// Codes_SRS_IOTHUBSSLCONTEXT_34_034: [If any exception is encountered while attempting to create the public key certificate instance, this function shall throw a CertificateException.]
throw new CertificateException(e);
}
}

private static Key getPrivateKey(Object possiblePrivateKey) throws IOException
{
if (possiblePrivateKey instanceof PEMKeyPair)
{
return new JcaPEMKeyConverter().getKeyPair((PEMKeyPair) possiblePrivateKey)
.getPrivate();
}
else if (possiblePrivateKey instanceof PrivateKeyInfo)
{
return new JcaPEMKeyConverter().getPrivateKey((PrivateKeyInfo) possiblePrivateKey);
}
else
{
throw new IOException("Unable to parse private key, type unknown");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.microsoft.azure.sdk.iot.deps.auth;

class LineReader {

private String[] lines;
private int index;

public LineReader(String lines) {
this.lines = lines.split("\n");
}

public String readLine() {
if (index >= lines.length) {
return null;
}
return lines[index++];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.microsoft.azure.sdk.iot.deps.auth;

import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

public class PrivateKeyReader {

// Private key file using PKCS #8 encoding
private static final String P8_BEGIN_MARKER = "-----BEGIN PRIVATE KEY";
private static final String P8_END_MARKER = "-----END PRIVATE KEY";

// Private key file using ECDSA encoding
private static final String ECDSA_BEGIN_MARKER = "-----BEGIN EC PRIVATE KEY";
private static final String ECDSA_END_MARKER = "-----END EC PRIVATE KEY";
// "30 81bf 020100 301006072a8648ce3d020106052b81040022 0481a7"
private static final byte[] ECDSA_HEADER = new byte[] {0x30, (byte) 0x81, (byte) 0xbf, 0x02, 0x01, 0x00, 0x30, 0x10,
0x06, 0x07, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, (byte) 0x81, 0x04,
0x00, 0x22, 0x04, (byte) 0x81, (byte) 0xa7};

private final LineReader server;
private PrivateKey key;

public PrivateKeyReader(String keyContent) {
this.server = new LineReader(keyContent);
}

public PrivateKey getPrivateKey() throws IOException {
if (this.key == null) {
this.key = read();
}
return this.key;
}

private PrivateKey read() throws IOException {

String line;

KeyFactory factory;

while ((line = server.readLine()) != null) {
if (line.indexOf(P8_BEGIN_MARKER) != -1) {
byte[] keyBytes = readKeyMaterial(P8_END_MARKER);
EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
try {
factory = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
throw new IOException("JCE error: " + e.getMessage());
}
try {
return factory.generatePrivate(keySpec);
} catch (InvalidKeySpecException e) {
throw new IOException("Invalid PKCS#8 PEM file: " + e.getMessage());
}
} else if (line.indexOf(ECDSA_BEGIN_MARKER) != -1) {
// https://stackoverflow.com/questions/41927859/how-do-i-load-an-elliptic-curve-pem-encoded-private-key
byte[] keyBytes = readKeyMaterial(ECDSA_END_MARKER);
EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(join(ECDSA_HEADER, keyBytes));
try {
factory = KeyFactory.getInstance("EC");
} catch (NoSuchAlgorithmException e) {
throw new IOException("JCE error: " + e.getMessage());
}
try {
return factory.generatePrivate(keySpec);
} catch (InvalidKeySpecException e) {
throw new IOException("Invalid ECDSA PEM file: " + e.getMessage());
}
}
}

throw new IOException("Invalid PEM file: no begin marker");
}

private byte[] join(byte[] a, byte[] b) {
byte[] result = new byte[a.length + b.length];
System.arraycopy(a, 0, result, 0, a.length);
System.arraycopy(b, 0, result, a.length, b.length);
return result;
}

private byte[] readKeyMaterial(String endMarker) throws IOException {
String line = null;
StringBuffer buf = new StringBuffer();

while ((line = server.readLine()) != null) {
if (line.indexOf(endMarker) != -1) {

return Base64.getDecoder().decode(buf.toString().getBytes());
}

buf.append(line.trim());
}

throw new IOException("Invalid PEM file: No end marker");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@
import com.microsoft.azure.sdk.iot.deps.auth.IotHubCertificateManager;
import com.microsoft.azure.sdk.iot.deps.auth.IotHubSSLContext;
import mockit.*;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.junit.Test;

import javax.net.ssl.*;
Expand Down Expand Up @@ -58,7 +52,6 @@ public class IotHubSSLContextTest

@Mocked byte[] mockedByteArray;
@Mocked ByteArrayInputStream mockedByteArrayInputStream;
@Mocked BouncyCastleProvider mockedBouncyCastleProvider;
@Mocked Security mockedSecurity;

private final static Collection<Certificate> testCollection = new LinkedHashSet<Certificate>();
Expand Down Expand Up @@ -448,11 +441,6 @@ public void constructorWithDefaultCertPathAndPublicCertAndPrivateKey() throws IO
"jdafjoadjojaofjajfijafijoiajfdoijafiojo\n" +
"-----END CERTIFICATE-----\n";

@Mocked PEMKeyPair mockedPEMKeyPair;
@Mocked PrivateKeyInfo mockedPrivateKeyInfo;
@Mocked PEMParser mockedPEMParser;
@Mocked PemObject mockedPemObject;
@Mocked PemReader mockedPemReader;
@Mocked StringReader mockedStringReader;
@Mocked KeyPair mockedKeyPair;
@Mocked CertificateFactory mockedCertificateFactory;
Expand Down Expand Up @@ -495,7 +483,7 @@ public void parsePrivateKeyType2Success() throws CertificateException, IOExcepti
//assert
assertEquals(mockedPrivateKey, actualPrivateKey);
}

/*
// Tests_SRS_IOTHUBSSLCONTEXT_34_032: [If any exception is encountered while attempting to create the private key instance, this function shall throw a CertificateException.]
@Test (expected = CertificateException.class)
public void parsePrivateKeyExceptionsWrappedInCertificateException() throws CertificateException, IOException
Expand Down Expand Up @@ -606,7 +594,7 @@ public void parsePublicKeyCertificateExceptionsWrappedInCertificateException() t
//assert
assertEquals(mockedX509Certificate, actualPublicKeyCertificate);
}

*/
//Tests_SRS_IOTHUBSSLCONTEXT_34_027: [This constructor shall save the provided ssl context.]
@Test
public void constructorWithSSLContextSavesSSLContext()
Expand Down
Loading