-
Notifications
You must be signed in to change notification settings - Fork 235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Npatilsen/dps cert march #1687
base: preview
Are you sure you want to change the base?
Npatilsen/dps cert march #1687
Changes from 7 commits
e5de94d
03598c3
9002b90
bc85fad
4c681b1
abfe97a
99fd298
0b8344d
8a68f34
2f7fb66
e8095c3
fa45082
f3183d2
c1ed4b9
4cab828
972fcce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -9,20 +9,22 @@ | |||||||
import com.microsoft.azure.sdk.iot.provisioning.device.AdditionalData; | ||||||||
import com.microsoft.azure.sdk.iot.provisioning.device.internal.exceptions.ProvisioningDeviceClientException; | ||||||||
import com.microsoft.azure.sdk.iot.provisioning.security.SecurityProvider; | ||||||||
//import com.microsoft.azure.sdk.iot.provisioning.service; | ||||||||
import com.microsoft.azure.sdk.iot.provisioning.security.SecurityProviderSymmetricKey; | ||||||||
import com.microsoft.azure.sdk.iot.provisioning.security.hsm.SecurityProviderX509Cert; | ||||||||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; | ||||||||
import org.bouncycastle.jcajce.provider.asymmetric.X509; | ||||||||
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 java.io.ByteArrayInputStream; | ||||||||
import java.io.IOException; | ||||||||
import java.io.StringReader; | ||||||||
import java.io.*; | ||||||||
import java.security.Key; | ||||||||
import java.security.Security; | ||||||||
import java.security.cert.Certificate; | ||||||||
import java.security.cert.CertificateException; | ||||||||
import java.security.cert.CertificateFactory; | ||||||||
import java.security.cert.X509Certificate; | ||||||||
|
@@ -37,18 +39,25 @@ | |||||||
@SuppressWarnings("CommentedOutCode") // Ignored in samples as we use these comments to show other options. | ||||||||
public class ProvisioningCertificateIssuanceSample | ||||||||
{ | ||||||||
private static final String idScope = "[Your ID scope here]"; | ||||||||
private static final String globalEndpoint = "[Your Provisioning Service Global Endpoint here]"; | ||||||||
private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.HTTPS; | ||||||||
private static final String idScope = ""; | ||||||||
private static final String globalEndpoint = ""; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This default value works for everyone who isn't on a govt. or private cloud instance, so we may as well fill it in for them |
||||||||
|
||||||||
// For the sake of security, you shouldn't save keys into String variables as that places them in heap memory. For the sake | ||||||||
// of simplicity within this sample, though, we will save it as a string. Typically this key would be loaded as byte[] so that | ||||||||
// it can be removed from stack memory. | ||||||||
private static final String SYMMETRIC_KEY = ""; | ||||||||
|
||||||||
// The registration Id to provision the device to. When creating an individual enrollment prior to running this sample, you choose this value. | ||||||||
private static final String REGISTRATION_ID = ""; | ||||||||
|
||||||||
private static final String DPS_CLIENT_CERTIFICATE_FOLDER = ".\\DpsClientCertificates"; | ||||||||
|
||||||||
//private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.HTTPS; | ||||||||
//private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.AMQPS; | ||||||||
//private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.AMQPS_WS; | ||||||||
//private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.MQTT; | ||||||||
private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.MQTT; | ||||||||
//private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.MQTT_WS; | ||||||||
private static final int MAX_TIME_TO_WAIT_FOR_REGISTRATION = 10000; // in milli seconds | ||||||||
private static final String leafPublicPem = "<Your Public Leaf Certificate Here>"; | ||||||||
private static final String leafPrivateKeyPem = "<Your Leaf Key Here>"; | ||||||||
|
||||||||
private static final Collection<String> signerCertificatePemList = new LinkedList<>(); | ||||||||
|
||||||||
static class ProvisioningStatus | ||||||||
{ | ||||||||
|
@@ -87,31 +96,37 @@ public static void main(String[] args) throws Exception | |||||||
{ | ||||||||
System.out.println("Starting..."); | ||||||||
System.out.println("Beginning setup."); | ||||||||
SecurityProviderSymmetricKey securityProviderSymmetricKey = new SecurityProviderSymmetricKey(SYMMETRIC_KEY.getBytes(StandardCharsets.UTF_8), REGISTRATION_ID); | ||||||||
ProvisioningDeviceClient provisioningDeviceClient = null; | ||||||||
DeviceClient deviceClient = null; | ||||||||
try | ||||||||
{ | ||||||||
ProvisioningStatus provisioningStatus = new ProvisioningStatus(); | ||||||||
|
||||||||
// For group enrollment uncomment this line | ||||||||
//signerCertificatePemList.add("<Your Signer/intermediate Certificate Here>"); | ||||||||
|
||||||||
X509Certificate leafPublicCert = parsePublicKeyCertificate(leafPublicPem); | ||||||||
Key leafPrivateKey = parsePrivateKey(leafPrivateKeyPem); | ||||||||
Collection<X509Certificate> signerCertificates = new LinkedList<>(); | ||||||||
for (String signerCertificatePem : signerCertificatePemList) | ||||||||
File csrDirectory = new File(DPS_CLIENT_CERTIFICATE_FOLDER); | ||||||||
if (csrDirectory.mkdir()) | ||||||||
{ | ||||||||
signerCertificates.add(parsePublicKeyCertificate(signerCertificatePem)); | ||||||||
System.out.println("Certificate directory created."); | ||||||||
} | ||||||||
else | ||||||||
{ | ||||||||
System.out.println("Failed to create directory for certificates."); | ||||||||
throw new IOException(); | ||||||||
Comment on lines
+113
to
+114
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
} | ||||||||
|
||||||||
SecurityProvider securityProviderX509 = new SecurityProviderX509Cert(leafPublicCert, leafPrivateKey, signerCertificates); | ||||||||
provisioningDeviceClient = ProvisioningDeviceClient.create(globalEndpoint, idScope, PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL, | ||||||||
securityProviderX509); | ||||||||
securityProviderSymmetricKey); | ||||||||
|
||||||||
AdditionalData additionalData = new AdditionalData | ||||||||
{ | ||||||||
operationalCertificateRequest = "someString", | ||||||||
}; | ||||||||
GenerateCertificateSigningRequestFiles(securityProviderSymmetricKey.getRegistrationId(), csrDirectory); | ||||||||
String csrFile = String.format("%s\\%s.csr", csrDirectory.getAbsolutePath().toString(), securityProviderSymmetricKey.getRegistrationId()); | ||||||||
|
||||||||
// Read certificate signing request | ||||||||
Scanner sc = new Scanner(new File(csrFile)); | ||||||||
String certificateSigningRequest = sc.next(); | ||||||||
|
||||||||
// Inform provisioning device client about the CSR | ||||||||
AdditionalData additionalData = new AdditionalData(); | ||||||||
additionalData.setOperationalCertificateRequest(certificateSigningRequest); | ||||||||
|
||||||||
provisioningDeviceClient.registerDevice(new ProvisioningDeviceClientRegistrationCallbackImpl(), provisioningStatus, additionalData); | ||||||||
|
||||||||
|
@@ -130,6 +145,33 @@ public static void main(String[] args) throws Exception | |||||||
Thread.sleep(MAX_TIME_TO_WAIT_FOR_REGISTRATION); | ||||||||
} | ||||||||
|
||||||||
String issuedClientCertificate = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIssuedClientCertificate(); | ||||||||
|
||||||||
if (issuedClientCertificate == null) | ||||||||
{ | ||||||||
System.out.println("Expected client certificate was not returned by DPS, exiting sample."); | ||||||||
return; | ||||||||
} | ||||||||
|
||||||||
// Write issued certificate to disk | ||||||||
String cerFile = String.format("%s\\%s.cer", csrDirectory.getAbsolutePath(), provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getRegistrationId()); | ||||||||
try | ||||||||
{ | ||||||||
BufferedWriter writer = new BufferedWriter(new FileWriter(cerFile)); | ||||||||
writer.write(issuedClientCertificate); | ||||||||
} | ||||||||
catch (IOException ex) | ||||||||
{ | ||||||||
System.out.println("Encountered an exception writing issued client certificate to disk: " + ex.getMessage()); | ||||||||
return; | ||||||||
} | ||||||||
|
||||||||
System.out.println("Creatign an X509 certifiate from the issued client certificate..."); | ||||||||
GeneratePfxFromPublicCertificateAndPrivateKey(securityProviderSymmetricKey.getRegistrationId(), csrDirectory); | ||||||||
X509Certificate clientCertificate = CreateX509CertificateFromPfxFile(securityProviderSymmetricKey.getRegistrationId(), csrDirectory); | ||||||||
System.out.println("COMPLETED CSR GENERATION!"); | ||||||||
//SecurityProviderX509Cert auth = new SecurityProviderX509Cert(clientCertificate); // TODO | ||||||||
|
||||||||
if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED) | ||||||||
{ | ||||||||
System.out.println("IotHUb Uri : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri()); | ||||||||
|
@@ -140,7 +182,8 @@ public static void main(String[] args) throws Exception | |||||||
String deviceId = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId(); | ||||||||
try | ||||||||
{ | ||||||||
deviceClient = new DeviceClient(iotHubUri, deviceId, securityProviderX509, IotHubClientProtocol.MQTT); | ||||||||
// TODO -- replace with generated x509 security provider | ||||||||
deviceClient = new DeviceClient(iotHubUri, deviceId, securityProviderSymmetricKey, IotHubClientProtocol.MQTT); | ||||||||
deviceClient.open(false); | ||||||||
Message messageToSendFromDeviceToHub = new Message("Whatever message you would like to send"); | ||||||||
|
||||||||
|
@@ -182,6 +225,76 @@ public static void main(String[] args) throws Exception | |||||||
} | ||||||||
} | ||||||||
|
||||||||
private static void GenerateCertificateSigningRequestFiles(String subject, File certificateDirectory) | ||||||||
{ | ||||||||
// TODO -- linux file path formatting | ||||||||
String path = certificateDirectory.getAbsolutePath(); | ||||||||
String keyfile = String.format("%s\\%s.key", path, subject); | ||||||||
String csrFile = String.format("%s\\%s.csr", path, subject); | ||||||||
|
||||||||
System.out.println(String.format("Generating private key for the certificate with subject %s", subject)); | ||||||||
String keyGen = String.format("openssl genpkey -out \"%s\" -algorithm RSA -pkeyopt rsa_keygen_bits:2048", keyfile); | ||||||||
|
||||||||
try | ||||||||
{ | ||||||||
Process keyGenProcess = Runtime.getRuntime().exec(keyGen); | ||||||||
keyGenProcess.waitFor(); | ||||||||
} | ||||||||
catch (IOException ex) | ||||||||
{ | ||||||||
System.out.println("An exception was thrown while running an openssl command: " + ex.getMessage()); | ||||||||
} | ||||||||
catch (InterruptedException ex) | ||||||||
{ | ||||||||
System.out.println("The openssl command was interuppted."); | ||||||||
} | ||||||||
|
||||||||
System.out.println(String.format("Generating CSR for certificate with subject %s", subject)); | ||||||||
String csrGen = String.format("openssl req -new -subj /CN=%s -key \"%s\" -out \"%s\"", subject, keyfile, csrFile); | ||||||||
try | ||||||||
{ | ||||||||
Process csrGenProcess = Runtime.getRuntime().exec(csrGen); | ||||||||
csrGenProcess.waitFor(); | ||||||||
} | ||||||||
catch (IOException ex) | ||||||||
{ | ||||||||
System.out.println("An exception was thrown while running an openssl command: " + ex.getMessage()); | ||||||||
} | ||||||||
catch (InterruptedException ex) | ||||||||
{ | ||||||||
System.out.println("The openssl command was interuppted."); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
private static void GeneratePfxFromPublicCertificateAndPrivateKey(String subject, File certificateDirectory) | ||||||||
{ | ||||||||
// TODO -- linux file formatting | ||||||||
String keyFile = String.format("%s\\%s.key", certificateDirectory.getAbsolutePath(), subject); | ||||||||
String cerFile = String.format("%s\\%s.cer", certificateDirectory.getAbsolutePath(), subject); | ||||||||
String pfxFile = String.format("%s\\%s.pfx", certificateDirectory.getAbsolutePath(), subject); | ||||||||
|
||||||||
System.out.println("Generating .pfx file..."); | ||||||||
String pfxGen = String.format("openssl pkcs12 -export -in \"%s\" -inkey \"%s\" -out \"%s\" -passout pass:", cerFile, keyFile, pfxFile); | ||||||||
|
||||||||
try | ||||||||
{ | ||||||||
Process pfxGenProcess = Runtime.getRuntime().exec(pfxGen); | ||||||||
} | ||||||||
catch (IOException ex) | ||||||||
{ | ||||||||
System.out.println("An exception was thrown while running an openssl command: " + ex.getMessage()); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
private static X509Certificate CreateX509CertificateFromPfxFile(String subject, File certificateDirectory) throws FileNotFoundException, CertificateException | ||||||||
{ | ||||||||
// TODO -- linux file formatting | ||||||||
String pfxFile = String.format("%s\\%s.pfx", certificateDirectory.getAbsolutePath(), subject); | ||||||||
CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); | ||||||||
return (X509Certificate) certFactory.generateCertificate(new FileInputStream(pfxFile)); | ||||||||
} | ||||||||
|
||||||||
|
||||||||
private static Key parsePrivateKey(String privateKeyString) throws IOException | ||||||||
{ | ||||||||
Security.addProvider(new BouncyCastleProvider()); | ||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,10 @@ public class AdditionalData | |
@Setter | ||
private String provisioningPayload; | ||
|
||
/** | ||
* The PEM encoded operational client certificate request that DPS will send to a linked certificate authority which | ||
* signs and returns an X.509 certificate that the IoT device can use to authenticate itself with an IoT Hub. | ||
*/ | ||
@Getter | ||
@Setter | ||
public String operationalCertificateRequest; | ||
patilsnr marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The choice of variable name on the .NET SDK (we'd like to keep the variable name same across all languages so as to keep the user experience similar): https://github.com/Azure/azure-iot-sdk-csharp/blob/c3008205c68de5b8fede085533048987e15af1c9/provisioning/device/src/ProvisioningRegistrationAdditionalData.cs#L23 |
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove?