<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
<year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
The contents of this file are subject to the Erlang Public License,
Version 1.1, (the "License"); you may not use this file except in
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
</legalnotice>
<title>Getting Started</title>
<file>using_public_key.xml</file>
</header>
<section>
<title>General information</title>
<p> This chapter is dedicated to showing some
examples of how to use the public_key API. Keys and certificates
used in the following sections are generated only for the purpose
of testing the public key application.</p>
<p>Note that some shell printouts, in the following examples,
have been abbreviated for increased readability.</p>
</section>
<section>
<title>PEM files</title>
<p> Public key data (keys, certificates etc) may be stored in PEM format. PEM files
comes from the Private Enhanced Mail Internet standard and has a
structure that looks like this:</p>
<code><text>
-----BEGIN <SOMETHING>-----
<Attribute> : <Value>
<Base64 encoded DER data>
-----END <SOMETHING>-----
<text></code>
<p>A file can contain several BEGIN/END blocks. Text lines between
blocks are ignored. Attributes, if present, are currently ignored except
for <c>Proc-Type</c> and <c>DEK-Info</c> that are used when the DER data is
encrypted.</p>
<section>
<title>DSA private key</title>
<p>Note file handling is not done by the public_key application. </p>
<code>1> {ok, PemBin} = file:read_file("dsa.pem").
{ok,<<"-----BEGIN DSA PRIVATE KEY-----\nMIIBuw"...>>}</code>
<p>This PEM file only has one entry, a private DSA key.</p>
<code>2> [DSAEntry] = public_key:pem_decode(PemBin).
[{'DSAPrivateKey',<<48,130,1,187,2,1,0,2,129,129,0,183,
179,230,217,37,99,144,157,21,228,204,
162,207,61,246,...>>,
not_encrypted}]</code>
<code>3> Key = public_key:pem_entry_decode(DSAEntry).
#'DSAPrivateKey'{version = 0,
p = 12900045185019966618...6593,
q = 1216700114794736143432235288305776850295620488937,
g = 10442040227452349332...47213,
y = 87256807980030509074...403143,
x = 510968529856012146351317363807366575075645839654}</code>
</section>
<section>
<title>RSA private key encrypted with a password.</title>
<code>1> {ok, PemBin} = file:read_file("rsa.pem").
{ok,<<"Bag Attribut"...>>}</code>
<p>This PEM file only has one entry a private RSA key.</p>
<code>2>[RSAEntry] = public_key:pem_decode(PemBin).
[{'RSAPrivateKey',<<224,108,117,203,152,40,15,77,128,126,
221,195,154,249,85,208,202,251,109,
119,120,57,29,89,19,9,...>>,
{"DES-EDE3-CBC",<<"k�e��p�L">>}}]
</code>
<p>In this example the password is "abcd1234".</p>
<code>3> Key = public_key:pem_entry_decode(RSAEntry, "abcd1234").
#'RSAPrivateKey'{version = 'two-prime',
modulus = 1112355156729921663373...2737107,
publicExponent = 65537,
privateExponent = 58064406231183...2239766033,
prime1 = 11034766614656598484098...7326883017,
prime2 = 10080459293561036618240...77738643771,
exponent1 = 77928819327425934607...22152984217,
exponent2 = 36287623121853605733...20588523793,
coefficient = 924840412626098444...41820968343,
otherPrimeInfos = asn1_NOVALUE}</code>
</section>
<section>
<title>X509 Certificates</title>
<code>1> {ok, PemBin} = file:read_file("cacerts.pem").
{ok,<<"-----BEGIN CERTIFICATE-----\nMIIC7jCCAl"...>>}</code>
<p>This file includes two certificates</p>
<code>2> [CertEntry1, CertEntry2] = public_key:pem_decode(PemBin).
[{'Certificate',<<48,130,2,238,48,130,2,87,160,3,2,1,2,2,
9,0,230,145,97,214,191,2,120,150,48,13,
...>>,
not_encrypted},
{'Certificate',<<48,130,3,200,48,130,3,49,160,3,2,1,2,2,1,
1,48,13,6,9,42,134,72,134,247,...>>>,
not_encrypted}]</code>
<p>Certificates may of course be decoded as usual ... </p>
<code>2> Cert = public_key:pem_entry_decode(CertEntry1).
#'Certificate'{
tbsCertificate =
#'TBSCertificate'{
version = v3,serialNumber = 16614168075301976214,
signature =
#'AlgorithmIdentifier'{
algorithm = {1,2,840,113549,1,1,5},
parameters = <<5,0>>},
issuer =
{rdnSequence,
[[#'AttributeTypeAndValue'{
type = {2,5,4,3},
value = <<19,8,101,114,108,97,110,103,67,65>>}],
[#'AttributeTypeAndValue'{
type = {2,5,4,11},
value = <<19,10,69,114,108,97,110,103,32,79,84,80>>}],
[#'AttributeTypeAndValue'{
type = {2,5,4,10},
value = <<19,11,69,114,105,99,115,115,111,110,32,65,66>>}],
[#'AttributeTypeAndValue'{
type = {2,5,4,7},
value = <<19,9,83,116,111,99,107,104,111,108,109>>}],
[#'AttributeTypeAndValue'{
type = {2,5,4,6},
value = <<19,2,83,69>>}],
[#'AttributeTypeAndValue'{
type = {1,2,840,113549,1,9,1},
value = <<22,22,112,101,116,101,114,64,101,114,...>>}]]},
validity =
#'Validity'{
notBefore = {utcTime,"080109082929Z"},
notAfter = {utcTime,"080208082929Z"}},
subject =
{rdnSequence,
[[#'AttributeTypeAndValue'{
type = {2,5,4,3},
value = <<19,8,101,114,108,97,110,103,67,65>>}],
[#'AttributeTypeAndValue'{
type = {2,5,4,11},
value = <<19,10,69,114,108,97,110,103,32,79,84,80>>}],
[#'AttributeTypeAndValue'{
type = {2,5,4,10},
value = <<19,11,69,114,105,99,115,115,111,110,32,...>>}],
[#'AttributeTypeAndValue'{
type = {2,5,4,7},
value = <<19,9,83,116,111,99,107,104,111,108,...>>}],
[#'AttributeTypeAndValue'{
type = {2,5,4,6},
value = <<19,2,83,69>>}],
[#'AttributeTypeAndValue'{
type = {1,2,840,113549,1,9,1},
value = <<22,22,112,101,116,101,114,64,...>>}]]},
subjectPublicKeyInfo =
#'SubjectPublicKeyInfo'{
algorithm =
#'AlgorithmIdentifier'{
algorithm = {1,2,840,113549,1,1,1},
parameters = <<5,0>>},
subjectPublicKey =
{0,<<48,129,137,2,129,129,0,203,209,187,77,73,231,90,...>>}},
issuerUniqueID = asn1_NOVALUE,
subjectUniqueID = asn1_NOVALUE,
extensions =
[#'Extension'{
extnID = {2,5,29,19},
critical = true,
extnValue = [48,3,1,1,255]},
#'Extension'{
extnID = {2,5,29,15},
critical = false,
extnValue = [3,2,1,6]},
#'Extension'{
extnID = {2,5,29,14},
critical = false,
extnValue = [4,20,27,217,65,152,6,30,142|...]},
#'Extension'{
extnID = {2,5,29,17},
critical = false,
extnValue = [48,24,129,22,112,101,116,101|...]}]},
signatureAlgorithm =
#'AlgorithmIdentifier'{
algorithm = {1,2,840,113549,1,1,5},
parameters = <<5,0>>},
signature =
{0,
<<163,186,7,163,216,152,63,47,154,234,139,73,154,96,120,
165,2,52,196,195,109,167,192,...>>}}
</code>
<p> Parts of certificates can be decoded with
public_key:der_decode/2 using that parts ASN.1 type.
Although application specific certificate
extension requires application specific ASN.1 decode/encode-functions.
Example, the first value of the rdnSequence above is of ASN.1 type
'X520CommonName'. ({2,5,4,3} = ?id-at-commonName)</p>
<code>public_key:der_decode('X520CommonName', <<19,8,101,114,108,97,110,103,67,65>>).
{printableString,"erlangCA"}</code>
<p>... but certificates can also be decode using the pkix_decode_cert/2 that
can customize and recursively decode standard parts of a certificate.</p>
<code>3>{_, DerCert, _} = CertEntry1.</code>
<code>4> public_key:pkix_decode_cert(DerCert, otp).
#'OTPCertificate'{
tbsCertificate =
#'OTPTBSCertificate'{
version = v3,serialNumber = 16614168075301976214,
signature =
#'SignatureAlgorithm'{
algorithm = {1,2,840,113549,1,1,5},
parameters = 'NULL'},
issuer =
{rdnSequence,
[[#'AttributeTypeAndValue'{
type = {2,5,4,3},
value = {printableString,"erlangCA"}}],
[#'AttributeTypeAndValue'{
type = {2,5,4,11},
value = {printableString,"Erlang OTP"}}],
[#'AttributeTypeAndValue'{
type = {2,5,4,10},
value = {printableString,"Ericsson AB"}}],
[#'AttributeTypeAndValue'{
type = {2,5,4,7},
value = {printableString,"Stockholm"}}],
[#'AttributeTypeAndValue'{type = {2,5,4,6},value = "SE"}],
[#'AttributeTypeAndValue'{
type = {1,2,840,113549,1,9,1},
value = "[email protected]"}]]},
validity =
#'Validity'{
notBefore = {utcTime,"080109082929Z"},
notAfter = {utcTime,"080208082929Z"}},
subject =
{rdnSequence,
[[#'AttributeTypeAndValue'{
type = {2,5,4,3},
value = {printableString,"erlangCA"}}],
[#'AttributeTypeAndValue'{
type = {2,5,4,11},
value = {printableString,"Erlang OTP"}}],
[#'AttributeTypeAndValue'{
type = {2,5,4,10},
value = {printableString,"Ericsson AB"}}],
[#'AttributeTypeAndValue'{
type = {2,5,4,7},
value = {printableString,"Stockholm"}}],
[#'AttributeTypeAndValue'{type = {2,5,4,6},value = "SE"}],
[#'AttributeTypeAndValue'{
type = {1,2,840,113549,1,9,1},
value = "[email protected]"}]]},
subjectPublicKeyInfo =
#'OTPSubjectPublicKeyInfo'{
algorithm =
#'PublicKeyAlgorithm'{
algorithm = {1,2,840,113549,1,1,1},
parameters = 'NULL'},
subjectPublicKey =
#'RSAPublicKey'{
modulus =
1431267547247997...37419,
publicExponent = 65537}},
issuerUniqueID = asn1_NOVALUE,
subjectUniqueID = asn1_NOVALUE,
extensions =
[#'Extension'{
extnID = {2,5,29,19},
critical = true,
extnValue =
#'BasicConstraints'{
cA = true,pathLenConstraint = asn1_NOVALUE}},
#'Extension'{
extnID = {2,5,29,15},
critical = false,
extnValue = [keyCertSign,cRLSign]},
#'Extension'{
extnID = {2,5,29,14},
critical = false,
extnValue = [27,217,65,152,6,30,142,132,245|...]},
#'Extension'{
extnID = {2,5,29,17},
critical = false,
extnValue = [{rfc822Name,"[email protected]"}]}]},
signatureAlgorithm =
#'SignatureAlgorithm'{
algorithm = {1,2,840,113549,1,1,5},
parameters = 'NULL'},
signature =
{0,
<<163,186,7,163,216,152,63,47,154,234,139,73,154,96,120,
165,2,52,196,195,109,167,192,...>>}}
</code>
<p>This call is equivalent to public_key:pem_entry_decode(CertEntry1)</p>
<code>5> public_key:pkix_decode_cert(DerCert, plain).
#'Certificate'{ ...}
</code>
</section>
<section>
<title>Encoding public key data to PEM format</title>
<p>If you have public key data and and want to create a PEM file
you can do that by calling the functions
public_key:pem_entry_encode/2 and pem_encode/1 and then saving the
result to a file. For example assume you have PubKey =
'RSAPublicKey'{} then you can create a PEM-"RSA PUBLIC KEY" file
(ASN.1 type 'RSAPublicKey') or a PEM-"PUBLIC KEY" file
('SubjectPublicKeyInfo' ASN.1 type).</p>
<p> The second element of the PEM-entry will be the ASN.1 DER encoded
key data.</p>
<code>1> PemEntry = public_key:pem_entry_encode('RSAPublicKey', RSAPubKey).
{'RSAPublicKey', <<48,72,...>>, not_encrypted}
2> PemBin = public_key:pem_encode([PemEntry]).
<<"-----BEGIN RSA PUBLIC KEY-----\nMEgC...>>
3> file:write_file("rsa_pub_key.pem", PemBin).
ok</code>
<p> or </p>
<code>1> PemBin = public_key:pem_entry_encode('SubjectPublicKeyInfo', RSAPubKey).
{'SubjectPublicKeyInfo', <<48,92...>>, not_encrypted}
2> PemBin = public_key:pem_encode([PemEntry]).
<<"-----BEGIN PUBLIC KEY-----\nMFw...>>
3> file:write_file("pub_key.pem", PemBin).
ok</code>
</section>
</section>
<section>
<title>RSA public key cryptography </title>
<p> Suppose you have PrivateKey = #'RSAPrivateKey{}' and the
plaintext Msg = binary() and the corresponding public key
PublicKey = #'RSAPublicKey'{} then you can do the following.
Note that you normally will only do one of the encrypt or
decrypt operations and the peer will do the other.
</p>
<p>Encrypt with the private key </p>
<code>RsaEncrypted = public_key:encrypt_private(Msg, PrivateKey),
Msg = public_key:decrypt_public(RsaEncrypted, PublicKey),</code>
<p>Encrypt with the public key </p>
<code>RsaEncrypted = public_key:encrypt_public(Msg, PublicKey),
Msg = public_key:decrypt_private(RsaEncrypted, PrivateKey),</code>
</section>
<section>
<title>Digital signatures</title>
<p> Suppose you have PrivateKey = #'RSAPrivateKey{}'or
#'DSAPrivateKey'{} and the plaintext Msg = binary() and the
corresponding public key PublicKey = #'RSAPublicKey'{} or
{integer(), #'DssParams'{}} then you can do the following. Note
that you normally will only do one of the sign or verify operations
and the peer will do the other. </p>
<code>Signature = public_key:sign(Msg, sha, PrivateKey),
true = public_key:verify(Msg, sha, Signature, PublicKey),</code>
<p>It might be appropriate to calculate the message digest before
calling sign or verify and then you can use the none as second
argument.</p>
<code>Digest = crypto:sha(Msg),
Signature = public_key:sign(Digest, none, PrivateKey),
true = public_key:verify(Digest, none, Signature, PublicKey),
</code>
</section>
<section>
<title>SSH files</title>
<p>SSH typically uses PEM files for private keys but has its
own file format for storing public keys. The erlang public_key
application can be used to parse the content of SSH public key files.</p>
<section>
<title> RFC 4716 SSH public key files </title>
<p>RFC 4716 SSH files looks confusingly like PEM files,
but there are some differences.</p>
<code>1> {ok, SshBin} = file:read_file("ssh2_rsa_pub").
{ok, <<"---- BEGIN SSH2 PUBLIC KEY ----\nAAAA"...>>}</code>
<p>This is equivalent to calling public_key:ssh_decode(SshBin, rfc4716_public_key).
</p>
<code>2> public_key:ssh_decode(SshBin, public_key).
[{#'RSAPublicKey'{modulus = 794430685...91663,
publicExponent = 35}, []}]
</code>
</section>
<section>
<title> Openssh public key format </title>
<code>1> {ok, SshBin} = file:read_file("openssh_dsa_pub").
{ok,<<"ssh-dss AAAAB3Nza"...>>}</code>
<p>This is equivalent to calling public_key:ssh_decode(SshBin, openssh_public_key).
</p>
<code>2> public_key:ssh_decode(SshBin, public_key).
[{{15642692...694280725,
#'Dss-Parms'{p = 17291273936...696123221,
q = 1255626590179665817295475654204371833735706001853,
g = 10454211196...480338645}},
[{comment,"dhopson@VMUbuntu-DSH"}]}]
</code>
</section>
<section>
<title> Known hosts - openssh format</title>
<code>1> {ok, SshBin} = file:read_file("known_hosts").
{ok,<<"hostname.domain.com,192.168.0.1 ssh-rsa AAAAB...>>}</code>
<p>Returns a list of public keys and their related attributes
each pair of key and attributes corresponds to one entry in
the known hosts file.</p>
<code>2> public_key:ssh_decode(SshBin, known_hosts).
[{#'RSAPublicKey'{modulus = 1498979460408...72721699,
publicExponent = 35},
[{hostnames,["hostname.domain.com","192.168.0.1"]}]},
{#'RSAPublicKey'{modulus = 14989794604088...2721699,
publicExponent = 35},
[{comment,"[email protected]"},
{hostnames,["|1|BWO5qDxk/cFH0wa05JLdHn+j6xQ=|rXQvIxh5cDD3C43k5DPDamawVNA="]}]}]
</code>
</section>
<section>
<title> Authorized keys - openssh format</title>
<code>1> {ok, SshBin} = file:read_file("auth_keys").
{ok, <<"command=\"dump /home\",no-pty,no-port-forwarding ssh-rsa AAA...>>}</code>
<p>Returns a list of public keys and their related attributes
each pair of key and attributes corresponds to one entry in
the authorized key file.</p>
<code>2> public_key:ssh_decode(SshBin, auth_keys).
[{#'RSAPublicKey'{modulus = 794430685...691663,
publicExponent = 35},
[{comment,"dhopson@VMUbuntu-DSH"},
{options,["command=\"dump/home\"","no-pty",
"no-port-forwarding"]}]},
{{1564269258491...607694280725,
#'Dss-Parms'{p = 17291273936185...763696123221,
q = 1255626590179665817295475654204371833735706001853,
g = 10454211195705...60511039590076780999046480338645}},
[{comment,"dhopson@VMUbuntu-DSH"}]}]
</code>
</section>
<section>
<title> Creating an SSH file from public key data </title>
<p>If you got a public key <c>PubKey</c> and a related list of
attributes <c>Attributes</c> as returned
by ssh_decode/2 you can create a new ssh file for example</p>
<code>N> SshBin = public_key:ssh_encode([{PubKey, Attributes}], openssh_public_key),
<<"ssh-rsa "...>>
N+1> file:write_file("id_rsa.pub", SshBin).
ok</code>
</section>
</section>
</chapter>