aboutsummaryrefslogtreecommitdiffstats
path: root/lib/public_key/doc/src/using_public_key.xml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/public_key/doc/src/using_public_key.xml')
-rw-r--r--lib/public_key/doc/src/using_public_key.xml504
1 files changed, 504 insertions, 0 deletions
diff --git a/lib/public_key/doc/src/using_public_key.xml b/lib/public_key/doc/src/using_public_key.xml
new file mode 100644
index 0000000000..f0eaeb8654
--- /dev/null
+++ b/lib/public_key/doc/src/using_public_key.xml
@@ -0,0 +1,504 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2011</year><year>2011</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>Using the public_key API</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> Pulic 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>&lt;text&gt;
+ -----BEGIN &lt;SOMETHING&gt;-----
+ &lt;Attribute&gt; : &lt;Value&gt;
+ &lt;Base64 encoded DER data&gt;
+ -----END &lt;SOMETHING&gt;-----
+ &lt;text&gt;</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,&lt;&lt;"-----BEGIN DSA PRIVATE KEY-----\nMIIBuw"...&gt;&gt;}</code>
+
+ <p>This PEM file only has one entry a private DSA key.</p>
+ <code>2> [DSAEntry] = public_key:pem_decode(PemBin).
+[{'DSAPrivateKey',&lt;&lt;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,...&gt;&gt;,
+ 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,&lt;&lt;"Bag Attribut"...&gt;&gt;}</code>
+
+ <p>This PEM file only has one entry a private RSA key.</p>
+ <code>2>[RSAEntry] = public_key:pem_decode(PemBin).
+[{'RSAPrivateKey',&lt;&lt;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,...&gt;&gt;,
+ {"DES-EDE3-CBC",&lt;&lt;"k�e��p�L"&gt;&gt;}}]
+
+ </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,&lt;&lt;"-----BEGIN CERTIFICATE-----\nMIIC7jCCAl"...&gt;&gt;}</code>
+
+ <p>This file includes two certificates</p>
+ <code>2> [CertEntry1, CertEntry2] = public_key:pem_decode(PemBin).
+[{'Certificate',&lt;&lt;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,
+ ...&gt;&gt;,
+ not_encrypted},
+ {'Certificate',&lt;&lt;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,...&gt;&gt;>,
+ 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 = &lt;&lt;5,0&gt;&gt;},
+ issuer =
+ {rdnSequence,
+ [[#'AttributeTypeAndValue'{
+ type = {2,5,4,3},
+ value = &lt;&lt;19,8,101,114,108,97,110,103,67,65&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,11},
+ value = &lt;&lt;19,10,69,114,108,97,110,103,32,79,84,80&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,10},
+ value = &lt;&lt;19,11,69,114,105,99,115,115,111,110,32,65,66&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,7},
+ value = &lt;&lt;19,9,83,116,111,99,107,104,111,108,109&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,6},
+ value = &lt;&lt;19,2,83,69&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {1,2,840,113549,1,9,1},
+ value = &lt;&lt;22,22,112,101,116,101,114,64,101,114,...&gt;&gt;}]]},
+ validity =
+ #'Validity'{
+ notBefore = {utcTime,"080109082929Z"},
+ notAfter = {utcTime,"080208082929Z"}},
+ subject =
+ {rdnSequence,
+ [[#'AttributeTypeAndValue'{
+ type = {2,5,4,3},
+ value = &lt;&lt;19,8,101,114,108,97,110,103,67,65&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,11},
+ value = &lt;&lt;19,10,69,114,108,97,110,103,32,79,84,80&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,10},
+ value = &lt;&lt;19,11,69,114,105,99,115,115,111,110,32,...&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,7},
+ value = &lt;&lt;19,9,83,116,111,99,107,104,111,108,...&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {2,5,4,6},
+ value = &lt;&lt;19,2,83,69&gt;&gt;}],
+ [#'AttributeTypeAndValue'{
+ type = {1,2,840,113549,1,9,1},
+ value = &lt;&lt;22,22,112,101,116,101,114,64,...&gt;&gt;}]]},
+ subjectPublicKeyInfo =
+ #'SubjectPublicKeyInfo'{
+ algorithm =
+ #'AlgorithmIdentifier'{
+ algorithm = {1,2,840,113549,1,1,1},
+ parameters = &lt;&lt;5,0&gt;&gt;},
+ subjectPublicKey =
+ {0,&lt;&lt;48,129,137,2,129,129,0,203,209,187,77,73,231,90,...&gt;&gt;}},
+ 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 = &lt;&lt;5,0&gt;&gt;},
+ signature =
+ {0,
+ &lt;&lt;163,186,7,163,216,152,63,47,154,234,139,73,154,96,120,
+ 165,2,52,196,195,109,167,192,...&gt;&gt;}}
+</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', &lt;&lt;19,8,101,114,108,97,110,103,67,65&gt;&gt;).
+{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,
+ &lt;&lt;163,186,7,163,216,152,63,47,154,234,139,73,154,96,120,
+ 165,2,52,196,195,109,167,192,...&gt;&gt;}}
+</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', &lt;&lt;48,72,...&gt;&gt;, not_encrypted}
+
+2> PemBin = public_key:pem_encode([PemEntry]).
+&lt;&lt;"-----BEGIN RSA PUBLIC KEY-----\nMEgC...&gt;&gt;
+
+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', &lt;&lt;48,92...&gt;&gt;, not_encrypted}
+
+2> PemBin = public_key:pem_encode([PemEntry]).
+&lt;&lt;"-----BEGIN PUBLIC KEY-----\nMFw...&gt;&gt;
+
+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, &lt;&lt;"---- BEGIN SSH2 PUBLIC KEY ----\nAAAA"...&gt;&gt;}</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,&lt;&lt;"ssh-dss AAAAB3Nza"...&gt;&gt;}</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,&lt;&lt;"hostname.domain.com,192.168.0.1 ssh-rsa AAAAB...&gt;&gt;}</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, &lt;&lt;"command=\"dump /home\",no-pty,no-port-forwarding ssh-rsa AAA...&gt;&gt;}</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),
+&lt;&lt;"ssh-rsa "...&gt;&gt;
+N+1> file:write_file("id_rsa.pub", SshBin).
+ok</code>
+ </section>
+ </section>
+</chapter>