diff --git a/SelfSignedCertificate/SelfSignedCertificate.m b/SelfSignedCertificate/SelfSignedCertificate.m index 2bff37e..811c8c6 100644 --- a/SelfSignedCertificate/SelfSignedCertificate.m +++ b/SelfSignedCertificate/SelfSignedCertificate.m @@ -22,28 +22,65 @@ "\x74\xe6\xc2\xda\x23\x93\xff\xac\x1d\x50\x34\x6c\x5c\x23\x90\x65" "\x57\x93\x3e\xcb\x93\xff\x6e\xde\xd1"; +/* +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: CN=Soft U2F, O=GitHub Inc., OU=Security + Validity + Not Before: Jul 26 20:09:08 2017 GMT + Not After : Jul 24 20:09:08 2027 GMT + Subject: CN=Soft U2F, O=GitHub Inc., OU=Security + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:f6:9c:ab:24:14:4b:b4:ef:87:f7:0f:23:1c:5c: + d4:f5:78:04:ac:f8:e0:c6:b2:b3:e3:52:18:3d:80: + 39:1f:6b:d2:79:d2:6a:4c:83:64:74:e6:c2:da:23: + 93:ff:ac:1d:50:34:6c:5c:23:90:65:57:93:3e:cb: + 93:ff:6e:de:d1 + ASN1 OID: prime256v1 + X509v3 extensions: + 1.3.6.1.4.1.45724.2.1.1: + .... + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:21:00:fe:22:1d:97:b8:ea:ea:12:bb:9f:42:14:85: + 0f:48:17:65:b5:e0:95:93:5e:a1:a3:d6:6d:0f:b1:6f:39:f7: + 22:02:20:64:d7:dc:2f:5c:6c:38:2a:f7:65:f5:78:6a:39:b0: + b1:4a:97:45:28:ef:7d:df:21:02:15:1b:88:4a:d4:41:7a +*/ const unsigned char *cert = (unsigned char*) - "\x30\x82\x01\x15\x30\x81\xbd\xa0\x03\x02\x01\x02\x02\x01\x01\x30" - "\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x15\x31\x13\x30" - "\x11\x06\x03\x55\x04\x03\x0c\x0a\x6d\x61\x73\x74\x61\x68\x79\x65" - "\x74\x69\x30\x1e\x17\x0d\x31\x37\x30\x36\x30\x39\x31\x34\x30\x38" - "\x31\x37\x5a\x17\x0d\x31\x37\x30\x36\x31\x30\x31\x34\x30\x38\x31" - "\x37\x5a\x30\x15\x31\x13\x30\x11\x06\x03\x55\x04\x03\x0c\x0a\x6d" - "\x61\x73\x74\x61\x68\x79\x65\x74\x69\x30\x59\x30\x13\x06\x07\x2a" - "\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07" - "\x03\x42\x00\x04\xf6\x9c\xab\x24\x14\x4b\xb4\xef\x87\xf7\x0f\x23" - "\x1c\x5c\xd4\xf5\x78\x04\xac\xf8\xe0\xc6\xb2\xb3\xe3\x52\x18\x3d" - "\x80\x39\x1f\x6b\xd2\x79\xd2\x6a\x4c\x83\x64\x74\xe6\xc2\xda\x23" - "\x93\xff\xac\x1d\x50\x34\x6c\x5c\x23\x90\x65\x57\x93\x3e\xcb\x93" - "\xff\x6e\xde\xd1\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04\x03\x02" - "\x03\x47\x00\x30\x44\x02\x20\x7c\xa5\x9b\x1e\x3a\x0e\xc4\xe1\xff" - "\x67\x76\xd3\xde\x93\xbc\x11\x02\xef\xbb\x1b\x18\x52\x32\x03\x07" - "\xf0\xea\xb1\xfa\x36\x70\x33\x02\x20\x3f\x92\xec\x0c\xbe\xc6\xd5" - "\xe8\x57\x92\x43\xe4\x3e\x4a\xdd\xd4\xd0\x8c\x7b\x6c\x02\x6c\xfd" - "\x1e\x8f\x84\x34\x2f\xdf\x81\xe1\x36"; + "\x30\x82\x01\x7e\x30\x82\x01\x24\xa0\x03\x02\x01\x02\x02\x01\x01" + "\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x3c\x31\x11" + "\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x53\x6f\x66\x74\x20\x55\x32" + "\x46\x31\x14\x30\x12\x06\x03\x55\x04\x0a\x0c\x0b\x47\x69\x74\x48" + "\x75\x62\x20\x49\x6e\x63\x2e\x31\x11\x30\x0f\x06\x03\x55\x04\x0b" + "\x0c\x08\x53\x65\x63\x75\x72\x69\x74\x79\x30\x1e\x17\x0d\x31\x37" + "\x30\x37\x32\x36\x32\x30\x30\x39\x30\x38\x5a\x17\x0d\x32\x37\x30" + "\x37\x32\x34\x32\x30\x30\x39\x30\x38\x5a\x30\x3c\x31\x11\x30\x0f" + "\x06\x03\x55\x04\x03\x0c\x08\x53\x6f\x66\x74\x20\x55\x32\x46\x31" + "\x14\x30\x12\x06\x03\x55\x04\x0a\x0c\x0b\x47\x69\x74\x48\x75\x62" + "\x20\x49\x6e\x63\x2e\x31\x11\x30\x0f\x06\x03\x55\x04\x0b\x0c\x08" + "\x53\x65\x63\x75\x72\x69\x74\x79\x30\x59\x30\x13\x06\x07\x2a\x86" + "\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03" + "\x42\x00\x04\xf6\x9c\xab\x24\x14\x4b\xb4\xef\x87\xf7\x0f\x23\x1c" + "\x5c\xd4\xf5\x78\x04\xac\xf8\xe0\xc6\xb2\xb3\xe3\x52\x18\x3d\x80" + "\x39\x1f\x6b\xd2\x79\xd2\x6a\x4c\x83\x64\x74\xe6\xc2\xda\x23\x93" + "\xff\xac\x1d\x50\x34\x6c\x5c\x23\x90\x65\x57\x93\x3e\xcb\x93\xff" + "\x6e\xde\xd1\xa3\x17\x30\x15\x30\x13\x06\x0b\x2b\x06\x01\x04\x01" + "\x82\xe5\x1c\x02\x01\x01\x04\x04\x03\x02\x03\x08\x30\x0a\x06\x08" + "\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x48\x00\x30\x45\x02\x21\x00" + "\xfe\x22\x1d\x97\xb8\xea\xea\x12\xbb\x9f\x42\x14\x85\x0f\x48\x17" + "\x65\xb5\xe0\x95\x93\x5e\xa1\xa3\xd6\x6d\x0f\xb1\x6f\x39\xf7\x22" + "\x02\x20\x64\xd7\xdc\x2f\x5c\x6c\x38\x2a\xf7\x65\xf5\x78\x6a\x39" + "\xb0\xb1\x4a\x97\x45\x28\xef\x7d\xdf\x21\x02\x15\x1b\x88\x4a\xd4" + "\x41\x7a"; const int priv_len = 121; -const int cert_len = 281; +const int cert_len = 386; @implementation SelfSignedCertificate {} diff --git a/script/generate_certificate b/script/generate_certificate new file mode 100755 index 0000000..4c336df --- /dev/null +++ b/script/generate_certificate @@ -0,0 +1,3 @@ +#!/bin/sh + +ruby script/generate_certificate.rb diff --git a/script/generate_certificate.rb b/script/generate_certificate.rb new file mode 100644 index 0000000..9ee2c7f --- /dev/null +++ b/script/generate_certificate.rb @@ -0,0 +1,77 @@ +#!/usr/bin/ruby + +require "openssl" + +PRIV = OpenSSL::PKey::EC.new( + "\x30\x77\x02\x01\x01\x04\x20\x03\x84\x2a\xc7\xf4\xcd\xe3\x67\xde"\ + "\xa0\x56\xc6\x4f\x7f\x3b\x15\xea\x7d\x4b\xc4\x83\xca\xc6\x97\x9f"\ + "\x2a\x31\x93\xad\x57\x31\x09\xa0\x0a\x06\x08\x2a\x86\x48\xce\x3d"\ + "\x03\x01\x07\xa1\x44\x03\x42\x00\x04\xf6\x9c\xab\x24\x14\x4b\xb4"\ + "\xef\x87\xf7\x0f\x23\x1c\x5c\xd4\xf5\x78\x04\xac\xf8\xe0\xc6\xb2"\ + "\xb3\xe3\x52\x18\x3d\x80\x39\x1f\x6b\xd2\x79\xd2\x6a\x4c\x83\x64"\ + "\x74\xe6\xc2\xda\x23\x93\xff\xac\x1d\x50\x34\x6c\x5c\x23\x90\x65"\ + "\x57\x93\x3e\xcb\x93\xff\x6e\xde\xd1" +) + +# From "FIDO U2F Authenticator Transports Extension" spec. +# X509 Extension OID for specifying supported transports. +U2F_TRANSPORT_EXTENSION_OID = "1.3.6.1.4.1.45724.2.1.1" + +# From "FIDO U2F Authenticator Transports Extension" spec. +# BIT STRING values for U2F_TRANSPORT_EXTENSION_OID. +U2F_TRANSPORT_BLUETOOTH_RADIO = 0b10000000 +U2F_TRANSPORT_BLUETOOTH_LOW_ENERGY_RADIO = 0b01000000 +U2F_TRANSPORT_USB = 0b00100000 +U2F_TRANSPORT_NFC = 0b00010000 +U2F_TRANSPORT_USB_INTERNAL = 0b00001000 + +SECOND = 1 +MINUTE = 60 * SECOND +HOUR = 60 * MINUTE +DAY = 24 * HOUR +YEAR = 365 * DAY + +def bit_string_extension(oid, value) + bsvalue = OpenSSL::ASN1::BitString.new([value].pack("C*")) + + # There's probably a "smart" way to do this. + bsvalue.unused_bits = value.to_s(2).match(/(0*)$/)[1].size + + OpenSSL::X509::Extension.new(oid, bsvalue, false) +end + +def generate_cert(private_key:, subject:, transports:) + # https://bugs.ruby-lang.org/issues/8177 + private_key.define_singleton_method(:private?) { private_key? } + private_key.define_singleton_method(:public?) { public_key? } + + OpenSSL::X509::Certificate.new().tap do |cert| + cert.version = 2 + cert.serial = 1 + cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject) + cert.not_before = Time.now + cert.not_after = Time.now + (10 * YEAR) + cert.public_key = private_key + + cert.add_extension(bit_string_extension( + U2F_TRANSPORT_EXTENSION_OID, + transports + )) + + cert.sign(private_key, OpenSSL::Digest::SHA256.new) + end +end + +cert = generate_cert( + private_key: PRIV, + subject: "CN=Soft U2F/O=GitHub Inc./OU=Security", + transports: U2F_TRANSPORT_USB_INTERNAL, +).to_der + +puts "Cert size: #{cert.bytesize}" + +puts "Cert: " +cert.bytes.each_slice(16) do |bytes| + line = bytes.map { |b| "\\x%02x" % b }.join + puts %Q["#{line}"] +end